-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
Accepted Proposal
The motivation behind this proposal is the analysis done in #21198 (comment), with the goal of defining exactly how comptime_float
should behave given conflicting implementation details and documentation.
Background
The language reference has this to say about comptime_float
:
Float literals have type
comptime_float
which is guaranteed to have the same precision and operations of the largest other floating point type, which isf128
.
As explained in #2794 (comment), one reason for comptime_float
to exist rather than just defining all floating-point literals to be comptime-known f128
s is that comptime_float
may implicitly cast to other floating point types even if precision is lost. #5780 (comment) also solidified the connection between the implementation of comptime_float
and the capabilities of f128
by reaffirming the above quote from the langref.
Despite this, there are some other differences in today's Zig between comptime_float
and f128
which are not limited to implicit casting behavior (test remarks below are as of Zig 0.14.0-dev.1304+7d54c62c8):
const std = @import("std");
const expect = std.testing.expect;
test {
// https://github.com/ziglang/zig/issues/21198
try expect(0.0 / 0.0 == 0.0);
try expect(std.math.isNan(@as(f128, 0.0) / @as(f128, 0.0)));
// try expect(std.math.isInf(@as(f128, 1.0 / 0.0))); // error: division by zero here causes undefined behavior
try expect(std.math.isInf(@as(f128, 1.0) / @as(f128, 0.0)));
try expect(std.math.isPositiveZero(@as(f128, -0.0 / 1.0)));
try expect(std.math.isNegativeZero(@as(f128, -0.0) / @as(f128, 1.0)));
}
The first two sets of discrepancies are included in the current compiler test suite (0.0 / 0.0
, comptime_float / 0.0
error), which is why this is a proposal rather than a bug fix; evidently, some other behavior was intended at some point.
Proposed behavior
All arithmetic operations with comptime_float
should behave as if they were done with comptime-known f128
. That is, the test above should be updated to the following:
const std = @import("std");
const expect = std.testing.expect;
test {
try expect(std.math.isNan(0.0 / 0.0));
try expect(std.math.isNan(@as(f128, 0.0) / @as(f128, 0.0)));
try expect(std.math.isInf(1.0 / 0.0));
try expect(std.math.isInf(@as(f128, 1.0) / @as(f128, 0.0)));
try expect(std.math.isNegativeZero(-0.0 / 1.0));
try expect(std.math.isNegativeZero(@as(f128, -0.0) / @as(f128, 1.0)));
}
As the updated test suggests, accepting this proposal could also allow the float functions std.math
(such as std.math.isInf
) to be expanded to work with comptime_float
by internally casting to/from f128
, without concern that the semantics might be different. The formatted float printing logic already does this today:
zig/lib/std/fmt/format_float.zig
Lines 57 to 58 in 7d54c62
// comptime_float internally is a f128; this preserves precision. | |
comptime_float => @as(f128, v_), |
Alternatives and follow-up changes
Signaling NaNs
This proposal makes no comment on the behavior of "signaling NaN", since as far as I can tell, Zig is currently lacking any builtins or functions to interact with the floating point environment (and frankly I lack the expertise to make an informed proposal on what such support should look like). If this proposal is accepted, then any future proposal to clarify or expand the behavior of signaling NaNs in Zig should comment on the behavior of signaling NaNs at comptime, and the behavior of comptime-known f128
and comptime_float
should be identical.
Restricted comptime_float
The existing behavior of treating 1.0 / 0.0
as a compile error suggests that comptime_float
may have originally been intended as not just f128
, but f128
with no infinities or NaNs. If this is true, and if this still intended going forward, it will be necessary to clarify and fix implementation of comptime_float
in that direction, and document it as the expected behavior. For example, as it is now, despite 1.0 / 0.0
being banned, it is trivial to create comptime_float
infinities and NaNs by implicitly casting from f128
or another float type.
I can't think of any reason for 0.0 / 0.0 == 0.0
, though; in a universe where comptime_float
does not include infinities or NaNs, 0.0 / 0.0
should be a compile error just like 1.0 / 0.0
.
std.math
support for comptime_float
As mentioned in the proposed behavior section, if this is accepted and implemented, every std.math
function which supports f128
could easily be updated to support comptime_float
. For example, comptime_float
NaNs could be produced using std.math.nan(comptime_float)
.