I've noticed some more cases like that, where promoting to Float
, or rather converting back from Float
can cause an infinite recursion just for some particular values, for example:
init(floatLiteral value: Float) {
// NOTE: Infinite recursion here caused by:
// `Float8(-Float(0))`
// but not for eg:
// `Float8(-Float(1))` or `Float8(Float(0))` ...
self.init(value) // <-- So I guess this init is calling back to this (floatLiteral) init when f is negative zero.
}
So I turned it into this:
init(floatLiteral value: Float) {
// There was an infinite recursion here for eg `Float8(-Float(0))`,
// but not for `Float8(-Float(1))` or `Float8(Float(0))`.
// This check takes care of that particular case, but are there more?
if value == -Float(0) {
self.init(bitPattern: 0b1_000_0000)
} else {
self.init(value) // <-- Will this call back to this for some other f?
}
}
If I'm not mistaken, this means that when I am promoting to Float
in some func/init/property A, and do self.init(f)
(or Float8(f)
), I am just lucky if that self.init(f)
(which I have no control over) doesn't, and won't ever in the future, call back to A
for any f
(that I haven't taken care of as above) ...
In this particular case, self.init(f)
(with f
being -Float(0)
) ends up calling _convert
which will then call back to my .init(floatLiteral:)
, and we have infinite recursion.
@inlinable
public // @testable
static func _convert<Source: BinaryFloatingPoint>(
from source: Source
) -> (value: Self, exact: Bool) {
guard _fastPath(!source.isZero) else {
return (source.sign == .minus ? -0.0 : 0, true) // <-- Here! That `-0.0` calls back to my floatLiteral init.
}
...
It seems kind of hard/impossible to protect against this kind of infinite recursion when promoting to Float
...