From 291b400595484613059773d924f53f017f7bb324 Mon Sep 17 00:00:00 2001 From: Gabriel Ozouf Date: Thu, 19 Nov 2020 17:05:21 +0100 Subject: [PATCH] [poincare/zoom] Ignore imprecise float value When looking for extrema in a function, we need to discard results where the function growth rate is smaller than the precision of the float type itself : these results are likely to be too noisy, and can cause false positives. e.g. : The sqrt(x^2+1)-x function Change-Id: I6e2c002d7308b41a4c226d274cbb5d9efe4ea7db --- poincare/src/zoom.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/poincare/src/zoom.cpp b/poincare/src/zoom.cpp index eb8a91428..922071254 100644 --- a/poincare/src/zoom.cpp +++ b/poincare/src/zoom.cpp @@ -22,6 +22,16 @@ constexpr float Zoom::k_largeUnitMantissa, Zoom::k_minimalRangeLength; +static bool DoesNotOverestimatePrecision(float dx, float y1, float y2, float y3) { + /* The float type looses precision surprisingly fast, and cannot confidently + * hold more than 6.6 digits of precision. Results more precise than that are + * too noisy to be be of any value. */ + float yMin = std::min(y1, std::min(y2, y3)); + float yMax = std::max(y1, std::max(y2, y3)); + constexpr float maxPrecision = 2.4e-7f; // 2^-22 ~ 10^-6.6 + return (yMax - yMin) / std::fabs(dx) > maxPrecision; +} + bool Zoom::InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin, float * xMax, float * yMin, float * yMax, float tMin, float tMax, Context * context, const void * auxiliary) { assert(xMin && xMax && yMin && yMax); @@ -84,7 +94,7 @@ bool Zoom::InterestingRangesForDisplay(ValueAtAbscissa evaluation, float * xMin, /* Check for a change in the profile. */ const PointOfInterest variation = BoundOfIntervalOfDefinitionIsReached(yPrev, yNext) ? PointOfInterest::Bound : RootExistsOnInterval(yPrev, yNext) ? PointOfInterest::Root : - ExtremumExistsOnInterval(yOld, yPrev, yNext) ? PointOfInterest::Extremum : + (ExtremumExistsOnInterval(yOld, yPrev, yNext) && DoesNotOverestimatePrecision(dXNext, yOld, yPrev, yNext)) ? PointOfInterest::Extremum : PointOfInterest::None; switch (static_cast(variation)) { /* The fallthrough is intentional, as we only want to update the Y