Skip to content

Commit

Permalink
[poincare/expression] Fix solutions to e^x=0
Browse files Browse the repository at this point in the history
Because of the limitations of the floating-point representation, e^x is
null for x <= 710, causing the nextRoot functions to find roots for it.
To prevent this, we looking for places where a function changes sign, we
actually require the function to take two non-null values of different
signs.
  • Loading branch information
GabrielNumworks authored and EmilieNumworks committed Dec 14, 2020
1 parent 78cb340 commit c7b758f
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 4 deletions.
2 changes: 2 additions & 0 deletions apps/solver/test/equation_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ QUIZ_CASE(equation_solve) {
assert_solves_numerically_to("cos(x)=0", -100, 100, {-90.0, 90.0});
assert_solves_numerically_to("cos(x)=0", -900, 1000, {-810.0, -630.0, -450.0, -270.0, -90.0, 90.0, 270.0, 450.0, 630.0, 810.0});
assert_solves_numerically_to("√(y)=0", -900, 1000, {0}, "y");
assert_solves_numerically_to("ℯ^x=0", -1000, 1000, {});
assert_solves_numerically_to("ℯ^x/1000=0", -1000, 1000, {});

// Long variable names
assert_solves_to("2abcde+3=4", "abcde=1/2");
Expand Down
24 changes: 21 additions & 3 deletions poincare/src/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,9 @@ Coordinate2D<double> Expression::nextMaximum(const char * symbol, double start,
}

double Expression::nextRoot(const char * symbol, double start, double step, double max, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
if (nullStatus(context) == ExpressionNode::NullStatus::Null) {
return start + step;
}
return nextIntersectionWithExpression(symbol, start, step, max,
[](double x, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const void * context1, const void * context2, const void * context3) {
const Expression * expression0 = reinterpret_cast<const Expression *>(context1);
Expand Down Expand Up @@ -1139,17 +1142,32 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st

void Expression::bracketRoot(const char * symbol, double start, double step, double max, double result[2], Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const {
double a = start;
double c = a;
double b = start+step;
double fa = evaluation(a, context, complexFormat, angleUnit, this, symbol, &expression);
double fc = fa;
double fb = evaluation(b, context, complexFormat, angleUnit, this, symbol, &expression);
while (step > 0.0 ? b <= max : b >= max) {
double fa = evaluation(a, context, complexFormat, angleUnit, this, symbol, &expression);
double fb = evaluation(b, context, complexFormat, angleUnit, this, symbol, &expression);
if (fa*fb <= 0) {
if (fa == 0. && ((fc < 0. && fb > 0.) || (fc > 0. && fb < 0.))) {
/* If fa is null, we still check that the function changes sign on ]c,b[,
* and that fc and fb are not null. Otherwise, it's more likely those
* zeroes are caused by approximation errors. */
result[0] = a;
result[1] = b;
return;
} else if (fb != 0. && ((fa < 0.) != (fb < 0.))) {
/* The function changes sign.
* The case fb = 0 is handled in the next pass with fa = 0. */
result[0] = a;
result[1] = b;
return;
}
c = a;
fc = fa;
a = b;
fa = fb;
b = b+step;
fb = evaluation(b, context, complexFormat, angleUnit, this, symbol, &expression);
}
result[0] = NAN;
result[1] = NAN;
Expand Down
8 changes: 7 additions & 1 deletion poincare/test/function_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,15 @@ QUIZ_CASE(poincare_function_root) {
{
constexpr int numberOfRoots = 1;
Coordinate2D<double> roots[numberOfRoots] = {
Coordinate2D<double>(99.8, 0.0)};
Coordinate2D<double>(99.9, 0.0)};
assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "0", nullptr, "a", 100.0, -0.1, -1.0);
}
{
constexpr int numberOfRoots = 1;
Coordinate2D<double> roots[numberOfRoots] = {
Coordinate2D<double>(NAN, 0.0)};
assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "ℯ^x", nullptr, "a", -1000.0, 0.1, -800);
}
}

QUIZ_CASE(poincare_function_intersection) {
Expand Down

0 comments on commit c7b758f

Please sign in to comment.