-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add lerp
to the standard math
library
#54
base: master
Are you sure you want to change the base?
Conversation
Could we get this merged? Doesn't seem like theres any objections to it. |
Suggested wording change has never been applied. I would say there's no rush in getting it in. Additionally, the implementation is hard to follow with respect to the guarantees presented and whether or not we need them. Feels like that the developers will be better off implementing their usual custom implementation of a simple Here's the objection you wanted :) |
|
||
## Drawbacks | ||
|
||
As mentioned in the [Design](##design) section, the naïve implementation of `lerp` may introduce precision error, and all of the edge-cases will need to be accounted for. |
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.
lerp(a0, a1, t)
:
t
can be much higher precision when near0
- so when
t
is near0
, we don't ever want to have a function of the formA + (1 - t)*B
- so when
- we want exactness,
lerp(a0, a1, 0) == a0
andlerp(a0, a1, 1) == a1
- when
t
is closer to0
, we want higher accuracy arounda0
- when
t
is closer to1
, we want higher accuracy arounda1
- when
- we want functions of the form
A + t*B
, as this guarantees monotonic behavior
Some things to consider for building intuition:
-- (100000000 samples)
-- given x, y = sortabs(random()*randomsign(), random()*randomsign()):
-- x - y + y ~= x: ~49.2%
-- y - x + x ~= y: ~20.6%
-- given x, y = sortabs(random(), random()):
-- x - y + y ~= x: ~30.1%
-- y - x + x ~= y: ~3.98%
-- given x, y = sortabs(random()*1.5, random()*1.5):
-- x - y + y ~= x: ~25.4%
-- y - x + x ~= y: ~1.63%
-- given floorlog2(x) == floorlog2(y) >= floorlog2(y - x)
-- x - y + y ~= x: 0%
-- y - x + x ~= y: 0%
Taking all this into account, we build our first example:
- Obvious solution is to split when
t < 1/2
- at the interchange point,
t = 1/2
, we want to guarantee monotonic behavior - so we bound the result with respect to
(a0 + a1)/2
- at the interchange point,
--[[
guarantees exactness because
a0 + 0*(a1 - a0) = a0
a1 + (1 - 1)*(a1 - a0) = a1 + 0*(a1 - a0) = a1
guarantees consistency because
a + (a - a)*t = a + 0*t = a
guarantees monotonicity because
we bound the results such that
lerp(a0, a1, 1/2 - 2^-54) <= lerp(a0, a1, 1/2)
]]
local function lerp1(a0, a1, t)
local m = (a0 + a1)/2
if t < 1/2 then
local a = a0 + (a1 - a0)*t
if a0 < a1 then
return math.min(m, a)
else
return math.max(m, a)
end
else
local a = a1 + (a1 - a0)*(t - 1)
if a0 < a1 then
return math.max(m, a)
else
return math.min(m, a)
end
end
end
Then we build a simpler case, to make sure what we are doing is necessary:
- The relevant question is
- can
a0 + (1/2 - 2^-54)*(a1 - a0) > a1 - 1/2*(a1 - a0)
ever be true? - From a lot of random sampling, it appears to be impossible, or at least very rare
- can
-- this guarantees exactness and consistency
-- this appears to guarantee monotonicity intrinsically
local function lerp2(a0, a1, t)
if t < 1/2 then
return a0 + (a1 - a0)*t
else
return a1 + (a1 - a0)*(t - 1)
end
end
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.
so from that, seems like at most all we'd need is to conditionally swap a
<-> b
and invert t
to reach most/all the guarantees we wanted? aka no reason to go more complex than lerp2
?
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.
blending in @/vegorov-rbx's comments, would it even be worth special casing anything? would the only issue with using a0 + (a1 - a0) * t
be that t
has less precision around 1
?
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.
t
has less precision around 1
, but there's no way to extract more precision from t
in the first place, what they give is what we get to work with.
Edit:
using only a0 + (a1 - a0)*t
is problematic because often times, when t = 1
, it does not return a1
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.
From a user perspective, if I want more accuracy around a1
than a0
, I would need to find some accurate way of generating s = 1 - t
directly, and then use math.lerp(a1, a0, s)
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.
Oh right this is about the precision of (a1 - a0)
, right? We want to take advantage of the higher density of floating point numbers near 0 so we would rather "multiply down". e.g.: x * 0.01
will be more precise than x - (x * 0.99)
.
All makes sense - lerp2
seems like the nicest "correct" solution. I suppose it just depends on whether we would want to use a conditional or whether that's deemed too heavy - will let the Luau folk speak to that.
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.
In my experience as a user, the most important guarantees lerp
can have is for
lerp(a0, a1, 0) == a0
and lerp(a0, a1, 1) == a1
If conditionals are not allowed, then the optimal solution seems to be
(1 - t)*a0 + t*a1
but this is neither monotonic nor consistent.
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.
Oh this is actually really clever, so the only "inaccuracy" we get is near 0.5
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.
Thank you for this new insight, I will try and push this forward :)
Add
lerp
to the standardmath
library