Proposal
In some cases you can get away with using operations that are already defined on FloatingPoint/BinaryFloatingPoint. For example, if the new value of pi makes your operation “overshoot” some range, you can use min/max to correct it.
func foo<F: BinaryFloatingPoint>(…) {
let x: F = …
let value = x * F.pi // Operation involving the new pi
let correctedValue = F.minimum(value, 1.0) // Correction
}
Personally, I do not think that a lot of people will be affected by this change.
Tiny change
There are other implementations of FloatingPoint outside of stdlib. Authors of those implementations need to be notified.
For example, there are a few implementations of IEEE 754 decimals. Without this/pitch thread I would never notice the change, which would make my implementation non-conforming to FloatingPoint.
Documentation changes
I see that the Swift team is currently working on floating points.
A few years ago, I pointed out some inconsistencies in how FloatingPoint protocol is defined, but I have never received any answers. As it currently stands, it is not possible to conform to this protocol, as it contradicts itself.
Can someone look at this? Do we need a new thread?
Minimum with sNaN is ambiguous
For FloatingPoint.minimum(_:_:) the documentation states:
This method returns the minimum of two values, preserving order and eliminating NaN when possible.
This sentence does not specify whether we are talking about quiet or signaling NaN, so it applies to both: NaNs should be eliminated when possible.
For two values x and y, the result of minimum(x, y) is x if x <= y, y if y < x, or whichever of x or y is a number if the other is a quiet NaN.
Both x <= y and y < x are false if one of the operands is NaN. The rest of the sentence says:
- if one of the operands is quiet NaN -> return number
- signaling NaN is not mentioned
If both x and y are NaN, or either x or y is a signaling NaN, the result is NaN.
This sentence says that the result is NaN if one of the following is true:
- both
x and y are NaN
- either
x or y is a signaling NaN
Based on the above, what should be the result of min(sNaN, 1.0)?
- Answer A:
1.0 because “eliminating NaN when possible” takes precedence
- Answer B:
NaN because “either x or y is a signaling NaN, the result is NaN” takes precedence
In swift-6.3.2-RELEASE on x86_64-unknown-linux-gnu the answer A (1.0) would be correct because of the default implementation:
@inlinable
public static func minimum(_ x: Self, _ y: Self) -> Self {
if x <= y || y.isNaN { return x }
return y
}
Minimum is a bit weird, as it has changed between IEEE 754 2008 and IEEE 754 2019. The reasoning is described in this document. That said, Swift documentation should be unambiguous.
ScaleB requires NaN equality?
For init(sign:exponent:significand:) the documentation states:
If the value you pass to significand is NaN, the result is NaN.
100% correct, no problems here.
For any floating-point value x of type F, the result of the following is equal to x, with the distinction that the result is canonicalized if x is in a noncanonical encoding:
let x0 = F(sign: x.sign, exponent: x.exponent, significand: x.significand)
If significand is NaN then the result is NaN. In such case the, “is equal to x” guarantee will not be true.
let x = Double.nan
let x0 = Double(sign: x.sign, exponent: x.exponent, significand: x.significand)
print(x) // nan
print(x0) // nan
print(x == x0) // false
FloatingPoint does not conform to AdditiveArithmetic
FloatingPoint types have to also conform to AdditiveArithmetic:
protocol FloatingPoint : Hashable, SignedNumeric, Strideable
protocol SignedNumeric : Numeric
protocol Numeric : AdditiveArithmetic, ExpressibleByIntegerLiteral
For AdditiveArithmetic.zero the documentation states:
Zero is the identity element for addition. For any value, x + .zero == x and .zero + x == x.
This is not true for NaN:
let x = Double.nan
print(x + Double.zero) // nan
print(x + Double.zero == x) // false
I guess Swift just went with the pragmatic choice, but they never documented it.
Incorrect pi value
Slightly off-topic, but famously Doom shipped with incorrect value of pi. And the game still worked. Someone even did a presentation on how different values of pi affect the gameplay (timestamp to the moment where the actual talk starts).