The following code prints nan, even though one would reasonably assume it to print 1.
std.debug.print("{}\n", .{std.math.pow(f32, -1, 1e20)}); // nan?
std.math.pow seems to return nan whenever the base is negative and the power is very large. Curiously, the following 2 lines print what you would expect:
std.debug.print("{}\n", .{std.math.pow(f32, -1, std.math.inf(f32))}); // 1
std.debug.print("{}\n", .{std.math.pow(f32, -std.math.inf(f32), 1e20)}); // inf
Looking at the doc comment for `std.math.pow`, I found this line interesting:
pow(x, y) = nan for finite x < 0 and finite non-integer y
It’s the only special case that almost describes our first case. However, if I understand floats correctly, 1e20 and other similarly large numbers (e.g., 1.00000000001e10) should be considered integers because at that size, the gaps between consecutive f32s are so large that f32 can effectively only represent integers.
Running the code through a debugger reveals the offending code:
const r1 = math.modf(@abs(y));
var yi = r1.ipart;
var yf = r1.fpart;
if (yf != 0 and x < 0) {
return math.nan(T);
}
if (yi >= 1 << (@typeInfo(T).float.bits - 1)) {
return @exp(y * @log(x)); // !!
}
The log of a negative number is nan, which then propagates through and we get a nan returned to us. This looks unintentional but I’m not sure. I’m not even sure if the condition right above should be using @typeInfo(T).float.bits - 1 or math.floatFractionalBits(T).
