-
Notifications
You must be signed in to change notification settings - Fork 6k
Improve the precision of lerpDouble #20879
Conversation
|
In presubmit, this actually does cause four lerp-related tests in the framework to start failing. An example is the HSVColor control test which is effectively equivalent to this check: the result of which differs in the ~15th decimal place. This is likely due to the use of 0.7, 0.3, and 0.6, which can't be represented exactly in a double. One option would be to rewrite those four tests to use inputs and scale factors with exact double representations such that those checks are a bit more tolerant of minor implementation changes. I think this change is still worth making given the big correctness wins in cases with significant differences in magnitude of the inputs, vs the very tiny differences in the few test cases relying on values that can't be exactly represented. |
|
Alright, landed all the stacked dependent PRs. PTAL. |
|
I've sent flutter/flutter#64908 to fix the four framework tests mentioned above to use I've also updated the |
Reduces errors caused by the loss of floating point precision when the
two extrema of the lerp differ significantly in magnitude. Previously,
we used the calculation:
a + (b - a) * t
When the difference in magnitude between `a` and `b` exceeds the
precision representable by double-precision floating point math, `b - a`
results in the larger-magnitude value of `a` or `b`. The error between
the value produced and the correct value is then scaled by t.
A simple example of the impact can be seen when `a` is significantly
larger in magnitude than `b`. In that case, `b - a` results in `a` and
when `t` is 1.0, the resulting value is `a - (a) * 1.0 == 0`.
The patch transforms the computation to the mathematically-equivalent
expression:
a * (1.0 - t) + b * t
By scaling each value independently, the behaviour is more accurate.
From the point of view of performance, this adds an extra
multiplication, but multiplication is relatively cheap and the behaviour
is significantly better.
This patch also adds a `precisionErrorTolerance` constant to
test_utils.dart and migrates existing tests to use `closeTo()` for
testing.
The tests themselves *do* currently use values that have an exact
floating-point representation, but we should allow for flexibility in
future implementation changes.
|
flutter/flutter#64908 has landed which fixes the four framework tests mentioned above. |
| /// Same as [lerpDouble] but specialized for non-null `int` type. | ||
| double _lerpInt(int a, int b, double t) { | ||
| return a + (b - a) * t; | ||
| return a * (1.0 - t) + b * t; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this one doesn't need it
Hixie
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This updates the web_ui implementation of lerpDouble to match the behaviour of the C++ engine implementation in dart:ui. Specifically this covers the following changes: * #20871: stricter handling of NaN and infinity * #20879: Improve the precision of lerpDouble lerpDouble: stricter handling of NaN and infinity (#20871) ---------------------------------------------------------- Previously, the behaviour of lerpDouble with respect to NaN and infinity was relatively complex and difficult to reason about. This patch simplifies the behaviour with respect to those conditions and adds documentation and tests. In general, if `a == b` or both values are null, infinite, or NaN, `a` is returned. Otherwise we require `a` and `b` and `t` to be finite or null and the result of the linear interpolation is returned. Improve the precision of lerpDouble (#20879) -------------------------------------------- Reduces errors caused by the loss of floating point precision when the two extrema of the lerp differ significantly in magnitude. Previously, we used the calculation: a + (b - a) * t When the difference in magnitude between `a` and `b` exceeds the precision representable by double-precision floating point math, `b - a` results in the larger-magnitude value of `a` or `b`. The error between the value produced and the correct value is then scaled by t. A simple example of the impact can be seen when `a` is significantly larger in magnitude than `b`. In that case, `b - a` results in `a` and when `t` is 1.0, the resulting value is `a - (a) * 1.0 == 0`. The patch transforms the computation to the mathematically-equivalent expression: a * (1.0 - t) + b * t By scaling each value independently, the behaviour is more accurate. From the point of view of performance, this adds an extra multiplication, but multiplication is relatively cheap and the behaviour is significantly better. This patch also adds a `precisionErrorTolerance` constant to test_utils.dart and migrates existing tests to use `closeTo()` for testing. The tests themselves *do* currently use values that have an exact floating-point representation, but we should allow for flexibility in future implementation changes.
Description
Reduces errors caused by the loss of floating point precision when the two extrema of the lerp differ significantly in magnitude. Previously, we used the calculation:
When the difference in magnitude between
aandbexceeds the precision representable by double-precision floating point math,b - aresults in the larger-magnitude value ofaorb. The error between the value produced and the correct value is then scaled by t.A simple example of the impact can be seen when
ais significantly larger in magnitude thanb. In that case,b - aresults inaand whentis 1.0, the resulting value isa - (a) * 1.0 == 0.The patch transforms the computation to the mathematically-equivalent expression:
By scaling each value independently, the result produced is more accurate. From the point of view of performance, this adds an extra multiplication, but multiplication is relatively cheap and the behaviour is significantly better.
Tests
Added four tests that verify behaviour of values with large differences in magnitude:
This patch also adds a
precisionErrorToleranceconstant to test_utils.dart and migrates existing tests to usecloseTo()for testing. The tests themselves do currently use values that have an exact floating-point representation, but we should allow for flexibility in future implementation changes.Related patches
flutter/flutter#64908 migrates a few framework tests where we were using exact double equality checks rather than using
precisionErrorTolerance(from the Foundation layer of the framework) ormoreOrLessEqualsto test values that have been scaled/lerped and is required to fix four framework test failures triggered by this patch due to mismatches in the ~15th decimal place.