glam-rs
glam-rs copied to clipboard
lerp()'s implementation has a loss of precision when inputs span multiple orders of magnitude
The general implementation of lerp
in glam for floating point types takes the form as follows:
fn lerp(a: Self, b: Self, t: f32) -> Self {
a + (b - a) * t
}
However, this presents floating point precision issues if a
and b
have very different exponent values due to the (b - a)
in the computation. You can test this via lerp(-16.0e30, 16.0, 1.0)
returning 0.0 instead of the correct 16.0. It may be more accurate to use the following form:
fn lerp(a: Self, b: Self, t: f32) -> Self {
a * (1.0 - t) + b * t
}
This may not be a significant regression in performance as floating point multiplication is generally much faster than addition.
Sorry, I've been meaning to get to this. I've been mulling over if changing the default implementation would cause anyone issues. Allegedly the main advantage of the method glam is using is it is monotonic, whereas apparently the precise version is not, see https://en.wikipedia.org/wiki/Linear_interpolation#Programming_language_support.
I think if I am to merge your PR I will bump the glam version number so people don't get a surprise change in behaviour.
The other option I guess would be to add a second "precise" lerp method, I think I'd rather not do that though.
Another thing to consider with changing the current implementation is currently t
is not clamped, but it will extrapolate just fine if t < 0
or t > 1
. If users happen to be relying on this behaviour then changing the implementation will break their code.
Perhaps it would be better to offer a separate lerp_precise
method for those that need it. This is the approach that vek
has taken (see comparison of different lib's approaches https://github.com/rust-lang/rust/issues/86269#issuecomment-864347515).
I just noticed that glam
uses the "imprecise" linear interpolation formula, and then found this issue.
Another thing to consider with changing the current implementation is currently
t
is not clamped, but it will extrapolate just fine ift < 0
ort > 1
. If users happen to be relying on this behaviour then changing the implementation will break their code.
Can you elaborate on what you imply with this? Since the "precise" implementation will also extrapolate outside the range of 0
and 1