Description
In a blog post (shameless plug) I wrote on clamping between 0.0 and 1.0, I noticed that we don't treat -0.0
as less than +0.0
. Instead, we return whichever is the first argument:
>> f32::min(0.0, -0.0)
0.0
>> f32::min(-0.0, 0.0)
-0.0
>> f32::max(0.0, -0.0)
0.0
>> f32::max(-0.0, 0.0)
-0.0
That behavior is kinda incoherent, probably unintentional, and seems highly unlikely that anybody is relying on it, so I suspect we're free to fix it.
While IEEE-754 defines two families of min/max functions (differing on NaN handling), all of them are required to treat -0.0
as less than +0.0
. We should fix our version to do this ¹.
This problem is present in {float}::clamp
too, which ideally would produce +0.0 for f32::clamp(-0.0, +0.0, _)
. (However, note that clamp uses a different² version of min/max than max/min, so fixing it here won't automatically fix it for clamp).
Excerpt from IEEE754 2019 section 9.6 "Minimum and maximum operations" (emphasis mine)
maximum(x, y) is x if x > y, y if y > x and a quiet NaN if either operand is a NaN, according to 6.2. For this operation, +0 compares greater than −0. ...
maximumNumber(x, y) is x if x > y, y if y > x, and the number if one operand is a number and the other is a NaN. For this operation, +0 compares greater than −0. ...
minimum(x, y) is x if x < y, y if y < x, and a quiet NaN if either operand is a NaN, according to 6.2. For this operation, −0 compares less than +0. ...
minimumNumber(x, y) is x if x < y, y if y < x, and the number if one operand is a number and the other is a NaN. For this operation, −0 compares less than +0. ...
¹ Alternatively, since these functions only "should" be provided, we could say that we just don't provide equivalents to those functions and that ours are different functions. If we do this, we should have a very good reason IMO, and should document this as well.
² Note that for reasons³ our {float}::clamp
functions are documented as propagating NaNs, but {float}::{min, max}
are not. This means this does have to be fixed in multiple places, although, it's coherent under IEEE754 if we assume
{float}::{min, max}
useminimumNumber
andmaximumNumber
respectively{float}::clamp
usesminimum
andmaximum
³ Nobody asked, but personally, I'd rather clamp be consistent with min/max, which for this has the benefit of guaranteeing that the result is always in the provided bound, which is quite desirable sometimes. I know it was probably deliberate, and I do get why, and know it can't be changed, but I'm still going to use this footnote-within-a-footnote on an obscure floating point bug report to gripe about it.