diff --git a/poincare/src/derivative.cpp b/poincare/src/derivative.cpp index 7d4fe0f6c..4f6656f32 100644 --- a/poincare/src/derivative.cpp +++ b/poincare/src/derivative.cpp @@ -41,8 +41,6 @@ Expression DerivativeNode::shallowReduce(ReductionContext reductionContext) { template Evaluation DerivativeNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - static T min = sizeof(T) == sizeof(double) ? DBL_MIN : FLT_MIN; - static T epsilon = sizeof(T) == sizeof(double) ? DBL_EPSILON : FLT_EPSILON; Evaluation evaluationArgumentInput = childAtIndex(2)->approximate(T(), context, complexFormat, angleUnit); T evaluationArgument = evaluationArgumentInput.toScalar(); T functionValue = approximateWithArgument(evaluationArgument, context, complexFormat, angleUnit); @@ -51,20 +49,35 @@ Evaluation DerivativeNode::templatedApproximate(Context * context, Preference return Complex::Undefined(); } - T error, result; + T error = sizeof(T) == sizeof(double) ? DBL_MAX : FLT_MAX; + T result; T h = k_minInitialRate; + static T tenEpsilon = sizeof(T) == sizeof(double) ? 10.0*DBL_EPSILON : 10.0f*FLT_EPSILON; do { - result = riddersApproximation(context, complexFormat, angleUnit, evaluationArgument, h, &error); + T currentError; + T currentResult = riddersApproximation(context, complexFormat, angleUnit, evaluationArgument, h, ¤tError); h /= 10.0; - } while ((std::fabs(error/result) > k_maxErrorRateOnApproximation || std::isnan(error)) && h >= epsilon); + if (std::isnan(currentError) || currentError > error) { + continue; + } + error = currentError; + result = currentResult; + } while ((std::fabs(error/result) > k_maxErrorRateOnApproximation || std::isnan(error)) && h >= tenEpsilon); - // If the error is too big regarding the value, do not return the answer. - if (std::fabs(error/result) > k_maxErrorRateOnApproximation || std::isnan(error)) { + /* If the error is too big regarding the value, do not return the answer. + * If the result is close to 0, we relax this constraint because the error + * will tend to be bigger compared to the result. */ + if (std::isnan(error) + || (std::fabs(result) < k_maxErrorRateOnApproximation && std::fabs(error) > std::fabs(result)) + || (std::fabs(result) >= k_maxErrorRateOnApproximation && std::fabs(error/result) > k_maxErrorRateOnApproximation)) + { return Complex::Undefined(); } + static T min = sizeof(T) == sizeof(double) ? DBL_MIN : FLT_MIN; if (std::fabs(error) < min) { return Complex::Builder(result); } + // Round the result according to the error error = std::pow((T)10, IEEE754::exponentBase10(error)+2); return Complex::Builder(std::round(result/error)*error); } @@ -82,7 +95,7 @@ template T DerivativeNode::growthRateAroundAbscissa(T x, T h, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { T expressionPlus = approximateWithArgument(x+h, context, complexFormat, angleUnit); T expressionMinus = approximateWithArgument(x-h, context, complexFormat, angleUnit); - return (expressionPlus - expressionMinus)/(2*h); + return (expressionPlus - expressionMinus)/(h+h); } template @@ -120,7 +133,9 @@ T DerivativeNode::riddersApproximation(Context * context, Preferences::ComplexFo for (int j = 1; j < 10; j++) { a[j][i] = (a[j-1][i]*fac-a[j-1][i-1])/(fac-1); fac = k_rateStepSize*k_rateStepSize*fac; - errt = std::fabs(a[j][i]-a[j-1][i]) > std::fabs(a[j][i]-a[j-1][i-1]) ? std::fabs(a[j][i]-a[j-1][i]) : std::fabs(a[j][i]-a[j-1][i-1]); + T err1 = std::fabs(a[j][i]-a[j-1][i]); + T err2 = std::fabs(a[j][i]-a[j-1][i-1]); + errt = err1 > err2 ? err1 : err2; // Update error and answer if error decreases if (errt < *error) { *error = errt; @@ -129,7 +144,7 @@ T DerivativeNode::riddersApproximation(Context * context, Preferences::ComplexFo } /* If higher extrapolation order significantly increases the error, return * early */ - if (std::fabs(a[i][i]-a[i-1][i-1]) > 2*(*error)) { + if (std::fabs(a[i][i]-a[i-1][i-1]) > (*error) + (*error)) { break; } } diff --git a/poincare/test/approximation.cpp b/poincare/test/approximation.cpp index bb39230b5..bf8ff4a3e 100644 --- a/poincare/test/approximation.cpp +++ b/poincare/test/approximation.cpp @@ -258,6 +258,9 @@ QUIZ_CASE(poincare_approximation_function) { assert_expression_approximates_to("diff(2×TO^2, TO, 7)", "28"); assert_expression_approximates_to("diff(2×TO^2, TO, 7)", "28"); + //assert_expression_approximates_to("diff(-1/3×x^3+6x^2-11x-50,x,11)", "0"); // FIXME error too big + assert_expression_approximates_to("diff(-1/3×x^3+6x^2-11x-50,x,11)", "0"); + assert_expression_approximates_to("floor(2.3)", "2"); assert_expression_approximates_to("floor(2.3)", "2");