Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@cbracken
Copy link
Member

@cbracken cbracken commented Aug 29, 2020

Description

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.

See related patches #20848 and #20778.

@cbracken cbracken requested a review from Hixie August 29, 2020 00:21
lib/ui/lerp.dart Outdated
/// Linearly interpolate between two numbers, `a` and `b`, by an extrapolation
/// factor `t`.
///
/// When both `a` and `b` are null, infinite, or NaN, `a` is returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is confusing. I thought for a minute that it meant that lerpDouble(null, double.infinity, 1.0) would return null.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm -- indeed. Re-worded a little. Let me know if that still parses badly.

Copy link
Contributor

@Hixie Hixie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cbracken
Copy link
Member Author

This is currently rebased on #20874.

This extracts a Dart test utilities library, containing
`expectAssertion` and `expectArgumentError` functions that simplify
running tests that test assertions across debug, profile, and release
configurations.

This change also restricts Dart unit tests to testing files whose
filename matches `*_test.dart` under `flutter/testing/dart`; previously
any file in that directory was run, but all files matched the above
pattern.
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.
@cbracken cbracken merged commit dbc9b1a into flutter:master Aug 30, 2020
@cbracken cbracken deleted the constrain-lerpDouble branch August 30, 2020 18:09
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Aug 30, 2020
cbracken added a commit that referenced this pull request Sep 8, 2020
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.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants