authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2023-10-06 20:44:47+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-06 14:44:47-04:00
logad6f8e3a5955df4d26478bc284919f4e5c8a6913
tree3877a9c1b0b2d5b5f198da70fd83cb9412882127
parente6590fea19e3eab94b35bfd3c36e29b53cefcaaf
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.math: add nextAfter (#16894)

`nextAfter()` returns the next representable value after `x` in the direction of `y` and is a standard math library function ([C++](https://en.cppreference.com/w/cpp/numeric/math/nextafter), [Java](https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#nextAfter-double-double-)). It is primarily useful for bitwise incrementing/decrementing floats. This implementation supports runtime integers, runtime floats and `comptime_int`. `comptime_float` is not supported because NaNs/infinities are intentionally difficult to obtain and because I'm not sure if the fact that it's backed by `f128` is supposed to be an implementation detail. Either way, the user could just call the function with the floating-point type whose behavior they want at comptime and then cast the result to `comptime_float`. The float implementation was ported from mingw-w64 with some slight changes made possible because the Zig standard library doesn't care about raising FP exceptions. The number of test cases may seem excessive but they should cover every normal and edge case for every float type and are especially important for verifying that `f80` works.

2 files changed, 329 insertions(+), 0 deletions(-)

lib/std/math.zig+2
...@@ -223,6 +223,7 @@ pub const isInf = @import("math/isinf.zig").isInf;...@@ -223,6 +223,7 @@ pub const isInf = @import("math/isinf.zig").isInf;
223pub const isPositiveInf = @import("math/isinf.zig").isPositiveInf;223pub const isPositiveInf = @import("math/isinf.zig").isPositiveInf;
224pub const isNegativeInf = @import("math/isinf.zig").isNegativeInf;224pub const isNegativeInf = @import("math/isinf.zig").isNegativeInf;
225pub const isNormal = @import("math/isnormal.zig").isNormal;225pub const isNormal = @import("math/isnormal.zig").isNormal;
226pub const nextAfter = @import("math/nextafter.zig").nextAfter;
226pub const signbit = @import("math/signbit.zig").signbit;227pub const signbit = @import("math/signbit.zig").signbit;
227pub const scalbn = @import("math/scalbn.zig").scalbn;228pub const scalbn = @import("math/scalbn.zig").scalbn;
228pub const ldexp = @import("math/ldexp.zig").ldexp;229pub const ldexp = @import("math/ldexp.zig").ldexp;
...@@ -345,6 +346,7 @@ test {...@@ -345,6 +346,7 @@ test {
345 _ = isPositiveInf;346 _ = isPositiveInf;
346 _ = isNegativeInf;347 _ = isNegativeInf;
347 _ = isNormal;348 _ = isNormal;
349 _ = nextAfter;
348 _ = signbit;350 _ = signbit;
349 _ = scalbn;351 _ = scalbn;
350 _ = ldexp;352 _ = ldexp;
lib/std/math/nextafter.zig created+327
...@@ -0,0 +1,327 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const math = std.math;
4const assert = std.debug.assert;
5const expect = std.testing.expect;
6
7/// Returns the next representable value after `x` in the direction of `y`.
8///
9/// Special cases:
10///
11/// - If `x == y`, `y` is returned.
12/// - For floats, if either `x` or `y` is a NaN, a NaN is returned.
13/// - For floats, if `x == 0.0` and `@abs(y) > 0.0`, the smallest subnormal number with the sign of
14/// `y` is returned.
15///
16pub fn nextAfter(comptime T: type, x: T, y: T) T {
17 return switch (@typeInfo(T)) {
18 .Int, .ComptimeInt => nextAfterInt(T, x, y),
19 .Float => nextAfterFloat(T, x, y),
20 else => @compileError("expected int or non-comptime float, found '" ++ @typeName(T) ++ "'"),
21 };
22}
23
24fn nextAfterInt(comptime T: type, x: T, y: T) T {
25 comptime assert(@typeInfo(T) == .Int or @typeInfo(T) == .ComptimeInt);
26 return if (@typeInfo(T) == .Int and @bitSizeOf(T) < 2)
27 // Special case for `i0`, `u0`, `i1`, and `u1`.
28 y
29 else if (y > x)
30 x + 1
31 else if (y < x)
32 x - 1
33 else
34 y;
35}
36
37// Based on nextafterf/nextafterl from mingw-w64 which are both public domain.
38// <https://github.com/mingw-w64/mingw-w64/blob/e89de847dd3e05bb8e46344378ce3e124f4e7d1c/mingw-w64-crt/math/nextafterf.c>
39// <https://github.com/mingw-w64/mingw-w64/blob/e89de847dd3e05bb8e46344378ce3e124f4e7d1c/mingw-w64-crt/math/nextafterl.c>
40
41fn nextAfterFloat(comptime T: type, x: T, y: T) T {
42 comptime assert(@typeInfo(T) == .Float);
43 if (x == y) {
44 // Returning `y` ensures that (0.0, -0.0) returns -0.0 and that (-0.0, 0.0) returns 0.0.
45 return y;
46 }
47 if (math.isNan(x) or math.isNan(y)) {
48 return math.nan(T);
49 }
50 if (x == 0.0) {
51 return if (y > 0.0)
52 math.floatTrueMin(T)
53 else
54 -math.floatTrueMin(T);
55 }
56 if (@bitSizeOf(T) == 80) {
57 // Unlike other floats, `f80` has an explicitly stored integer bit between the fractional
58 // part and the exponent and thus requires special handling. This integer bit *must* be set
59 // when the value is normal, an infinity or a NaN and *should* be cleared otherwise.
60
61 const fractional_bits_mask = (1 << math.floatFractionalBits(f80)) - 1;
62 const integer_bit_mask = 1 << math.floatFractionalBits(f80);
63 const exponent_bits_mask = (1 << math.floatExponentBits(f80)) - 1;
64
65 var x_parts = math.break_f80(x);
66
67 // Bitwise increment/decrement the fractional part while also taking care to update the
68 // exponent if we overflow the fractional part. This might flip the integer bit; this is
69 // intentional.
70 if ((x > 0.0) == (y > x)) {
71 x_parts.fraction +%= 1;
72 if (x_parts.fraction & fractional_bits_mask == 0) {
73 x_parts.exp += 1;
74 }
75 } else {
76 if (x_parts.fraction & fractional_bits_mask == 0) {
77 x_parts.exp -= 1;
78 }
79 x_parts.fraction -%= 1;
80 }
81
82 // If the new value is normal or an infinity (indicated by at least one bit in the exponent
83 // being set), the integer bit might have been cleared from an overflow, so we must ensure
84 // that it remains set.
85 if (x_parts.exp & exponent_bits_mask != 0) {
86 x_parts.fraction |= integer_bit_mask;
87 }
88 // Otherwise, the new value is subnormal and the integer bit will have either flipped from
89 // set to cleared (if the old value was normal) or remained cleared (if the old value was
90 // subnormal), both of which are the outcomes we want.
91
92 return math.make_f80(x_parts);
93 } else {
94 const Bits = std.meta.Int(.unsigned, @bitSizeOf(T));
95 var x_bits: Bits = @bitCast(x);
96 if ((x > 0.0) == (y > x)) {
97 x_bits += 1;
98 } else {
99 x_bits -= 1;
100 }
101 return @bitCast(x_bits);
102 }
103}
104
105test "math.nextAfter.int" {
106 try expect(nextAfter(i0, 0, 0) == 0);
107 try expect(nextAfter(u0, 0, 0) == 0);
108 try expect(nextAfter(i1, 0, 0) == 0);
109 try expect(nextAfter(i1, 0, -1) == -1);
110 try expect(nextAfter(i1, -1, -1) == -1);
111 try expect(nextAfter(i1, -1, 0) == 0);
112 try expect(nextAfter(u1, 0, 0) == 0);
113 try expect(nextAfter(u1, 0, 1) == 1);
114 try expect(nextAfter(u1, 1, 1) == 1);
115 try expect(nextAfter(u1, 1, 0) == 0);
116 inline for (.{ i8, i16, i32, i64, i128, i333 }) |T| {
117 try expect(nextAfter(T, 3, 7) == 4);
118 try expect(nextAfter(T, 3, -7) == 2);
119 try expect(nextAfter(T, -3, -7) == -4);
120 try expect(nextAfter(T, -3, 7) == -2);
121 try expect(nextAfter(T, 5, 5) == 5);
122 try expect(nextAfter(T, -5, -5) == -5);
123 try expect(nextAfter(T, 0, 0) == 0);
124 try expect(nextAfter(T, math.minInt(T), math.minInt(T)) == math.minInt(T));
125 try expect(nextAfter(T, math.maxInt(T), math.maxInt(T)) == math.maxInt(T));
126 }
127 inline for (.{ u8, u16, u32, u64, u128, u333 }) |T| {
128 try expect(nextAfter(T, 3, 7) == 4);
129 try expect(nextAfter(T, 7, 3) == 6);
130 try expect(nextAfter(T, 5, 5) == 5);
131 try expect(nextAfter(T, 0, 0) == 0);
132 try expect(nextAfter(T, math.minInt(T), math.minInt(T)) == math.minInt(T));
133 try expect(nextAfter(T, math.maxInt(T), math.maxInt(T)) == math.maxInt(T));
134 }
135 comptime {
136 try expect(nextAfter(comptime_int, 3, 7) == 4);
137 try expect(nextAfter(comptime_int, 3, -7) == 2);
138 try expect(nextAfter(comptime_int, -3, -7) == -4);
139 try expect(nextAfter(comptime_int, -3, 7) == -2);
140 try expect(nextAfter(comptime_int, 5, 5) == 5);
141 try expect(nextAfter(comptime_int, -5, -5) == -5);
142 try expect(nextAfter(comptime_int, 0, 0) == 0);
143 try expect(nextAfter(comptime_int, math.maxInt(u512), math.maxInt(u512)) == math.maxInt(u512));
144 }
145}
146
147test "math.nextAfter.float" {
148 @setEvalBranchQuota(2000);
149
150 // normal -> normal
151 try expect(nextAfter(f16, 0x1.234p0, 2.0) == 0x1.238p0);
152 try expect(nextAfter(f16, 0x1.234p0, -2.0) == 0x1.230p0);
153 try expect(nextAfter(f16, 0x1.234p0, 0x1.234p0) == 0x1.234p0);
154 try expect(nextAfter(f16, -0x1.234p0, -2.0) == -0x1.238p0);
155 try expect(nextAfter(f16, -0x1.234p0, 2.0) == -0x1.230p0);
156 try expect(nextAfter(f16, -0x1.234p0, -0x1.234p0) == -0x1.234p0);
157 try expect(nextAfter(f32, 0x1.001234p0, 2.0) == 0x1.001236p0);
158 try expect(nextAfter(f32, 0x1.001234p0, -2.0) == 0x1.001232p0);
159 try expect(nextAfter(f32, 0x1.001234p0, 0x1.001234p0) == 0x1.001234p0);
160 try expect(nextAfter(f32, -0x1.001234p0, -2.0) == -0x1.001236p0);
161 try expect(nextAfter(f32, -0x1.001234p0, 2.0) == -0x1.001232p0);
162 try expect(nextAfter(f32, -0x1.001234p0, -0x1.001234p0) == -0x1.001234p0);
163 inline for (.{f64} ++ if (@bitSizeOf(c_longdouble) == 64) .{c_longdouble} else .{}) |T64| {
164 try expect(nextAfter(T64, 0x1.0000000001234p0, 2.0) == 0x1.0000000001235p0);
165 try expect(nextAfter(T64, 0x1.0000000001234p0, -2.0) == 0x1.0000000001233p0);
166 try expect(nextAfter(T64, 0x1.0000000001234p0, 0x1.0000000001234p0) == 0x1.0000000001234p0);
167 try expect(nextAfter(T64, -0x1.0000000001234p0, -2.0) == -0x1.0000000001235p0);
168 try expect(nextAfter(T64, -0x1.0000000001234p0, 2.0) == -0x1.0000000001233p0);
169 try expect(nextAfter(T64, -0x1.0000000001234p0, -0x1.0000000001234p0) == -0x1.0000000001234p0);
170 }
171 inline for (.{f80} ++ if (@bitSizeOf(c_longdouble) == 80) .{c_longdouble} else .{}) |T80| {
172 try expect(nextAfter(T80, 0x1.0000000000001234p0, 2.0) == 0x1.0000000000001236p0);
173 try expect(nextAfter(T80, 0x1.0000000000001234p0, -2.0) == 0x1.0000000000001232p0);
174 try expect(nextAfter(T80, 0x1.0000000000001234p0, 0x1.0000000000001234p0) == 0x1.0000000000001234p0);
175 try expect(nextAfter(T80, -0x1.0000000000001234p0, -2.0) == -0x1.0000000000001236p0);
176 try expect(nextAfter(T80, -0x1.0000000000001234p0, 2.0) == -0x1.0000000000001232p0);
177 try expect(nextAfter(T80, -0x1.0000000000001234p0, -0x1.0000000000001234p0) == -0x1.0000000000001234p0);
178 }
179 inline for (.{f128} ++ if (@bitSizeOf(c_longdouble) == 128) .{c_longdouble} else .{}) |T128| {
180 try expect(nextAfter(T128, 0x1.0000000000000000000000001234p0, 2.0) == 0x1.0000000000000000000000001235p0);
181 try expect(nextAfter(T128, 0x1.0000000000000000000000001234p0, -2.0) == 0x1.0000000000000000000000001233p0);
182 try expect(nextAfter(T128, 0x1.0000000000000000000000001234p0, 0x1.0000000000000000000000001234p0) == 0x1.0000000000000000000000001234p0);
183 try expect(nextAfter(T128, -0x1.0000000000000000000000001234p0, -2.0) == -0x1.0000000000000000000000001235p0);
184 try expect(nextAfter(T128, -0x1.0000000000000000000000001234p0, 2.0) == -0x1.0000000000000000000000001233p0);
185 try expect(nextAfter(T128, -0x1.0000000000000000000000001234p0, -0x1.0000000000000000000000001234p0) == -0x1.0000000000000000000000001234p0);
186 }
187
188 // subnormal -> subnormal
189 try expect(nextAfter(f16, 0x0.234p-14, 1.0) == 0x0.238p-14);
190 try expect(nextAfter(f16, 0x0.234p-14, -1.0) == 0x0.230p-14);
191 try expect(nextAfter(f16, 0x0.234p-14, 0x0.234p-14) == 0x0.234p-14);
192 try expect(nextAfter(f16, -0x0.234p-14, -1.0) == -0x0.238p-14);
193 try expect(nextAfter(f16, -0x0.234p-14, 1.0) == -0x0.230p-14);
194 try expect(nextAfter(f16, -0x0.234p-14, -0x0.234p-14) == -0x0.234p-14);
195 try expect(nextAfter(f32, 0x0.001234p-126, 1.0) == 0x0.001236p-126);
196 try expect(nextAfter(f32, 0x0.001234p-126, -1.0) == 0x0.001232p-126);
197 try expect(nextAfter(f32, 0x0.001234p-126, 0x0.001234p-126) == 0x0.001234p-126);
198 try expect(nextAfter(f32, -0x0.001234p-126, -1.0) == -0x0.001236p-126);
199 try expect(nextAfter(f32, -0x0.001234p-126, 1.0) == -0x0.001232p-126);
200 try expect(nextAfter(f32, -0x0.001234p-126, -0x0.001234p-126) == -0x0.001234p-126);
201 inline for (.{f64} ++ if (@bitSizeOf(c_longdouble) == 64) .{c_longdouble} else .{}) |T64| {
202 try expect(nextAfter(T64, 0x0.0000000001234p-1022, 1.0) == 0x0.0000000001235p-1022);
203 try expect(nextAfter(T64, 0x0.0000000001234p-1022, -1.0) == 0x0.0000000001233p-1022);
204 try expect(nextAfter(T64, 0x0.0000000001234p-1022, 0x0.0000000001234p-1022) == 0x0.0000000001234p-1022);
205 try expect(nextAfter(T64, -0x0.0000000001234p-1022, -1.0) == -0x0.0000000001235p-1022);
206 try expect(nextAfter(T64, -0x0.0000000001234p-1022, 1.0) == -0x0.0000000001233p-1022);
207 try expect(nextAfter(T64, -0x0.0000000001234p-1022, -0x0.0000000001234p-1022) == -0x0.0000000001234p-1022);
208 }
209 inline for (.{f80} ++ if (@bitSizeOf(c_longdouble) == 80) .{c_longdouble} else .{}) |T80| {
210 try expect(nextAfter(T80, 0x0.0000000000001234p-16382, 1.0) == 0x0.0000000000001236p-16382);
211 try expect(nextAfter(T80, 0x0.0000000000001234p-16382, -1.0) == 0x0.0000000000001232p-16382);
212 try expect(nextAfter(T80, 0x0.0000000000001234p-16382, 0x0.0000000000001234p-16382) == 0x0.0000000000001234p-16382);
213 try expect(nextAfter(T80, -0x0.0000000000001234p-16382, -1.0) == -0x0.0000000000001236p-16382);
214 try expect(nextAfter(T80, -0x0.0000000000001234p-16382, 1.0) == -0x0.0000000000001232p-16382);
215 try expect(nextAfter(T80, -0x0.0000000000001234p-16382, -0x0.0000000000001234p-16382) == -0x0.0000000000001234p-16382);
216 }
217 inline for (.{f128} ++ if (@bitSizeOf(c_longdouble) == 128) .{c_longdouble} else .{}) |T128| {
218 try expect(nextAfter(T128, 0x0.0000000000000000000000001234p-16382, 1.0) == 0x0.0000000000000000000000001235p-16382);
219 try expect(nextAfter(T128, 0x0.0000000000000000000000001234p-16382, -1.0) == 0x0.0000000000000000000000001233p-16382);
220 try expect(nextAfter(T128, 0x0.0000000000000000000000001234p-16382, 0x0.0000000000000000000000001234p-16382) == 0x0.0000000000000000000000001234p-16382);
221 try expect(nextAfter(T128, -0x0.0000000000000000000000001234p-16382, -1.0) == -0x0.0000000000000000000000001235p-16382);
222 try expect(nextAfter(T128, -0x0.0000000000000000000000001234p-16382, 1.0) == -0x0.0000000000000000000000001233p-16382);
223 try expect(nextAfter(T128, -0x0.0000000000000000000000001234p-16382, -0x0.0000000000000000000000001234p-16382) == -0x0.0000000000000000000000001234p-16382);
224 }
225
226 // normal -> normal (change in exponent)
227 try expect(nextAfter(f16, 0x1.FFCp3, math.inf(f16)) == 0x1p4);
228 try expect(nextAfter(f16, 0x1p4, -math.inf(f16)) == 0x1.FFCp3);
229 try expect(nextAfter(f16, -0x1.FFCp3, -math.inf(f16)) == -0x1p4);
230 try expect(nextAfter(f16, -0x1p4, math.inf(f16)) == -0x1.FFCp3);
231 try expect(nextAfter(f32, 0x1.FFFFFEp3, math.inf(f32)) == 0x1p4);
232 try expect(nextAfter(f32, 0x1p4, -math.inf(f32)) == 0x1.FFFFFEp3);
233 try expect(nextAfter(f32, -0x1.FFFFFEp3, -math.inf(f32)) == -0x1p4);
234 try expect(nextAfter(f32, -0x1p4, math.inf(f32)) == -0x1.FFFFFEp3);
235 inline for (.{f64} ++ if (@bitSizeOf(c_longdouble) == 64) .{c_longdouble} else .{}) |T64| {
236 try expect(nextAfter(T64, 0x1.FFFFFFFFFFFFFp3, math.inf(T64)) == 0x1p4);
237 try expect(nextAfter(T64, 0x1p4, -math.inf(T64)) == 0x1.FFFFFFFFFFFFFp3);
238 try expect(nextAfter(T64, -0x1.FFFFFFFFFFFFFp3, -math.inf(T64)) == -0x1p4);
239 try expect(nextAfter(T64, -0x1p4, math.inf(T64)) == -0x1.FFFFFFFFFFFFFp3);
240 }
241 inline for (.{f80} ++ if (@bitSizeOf(c_longdouble) == 80) .{c_longdouble} else .{}) |T80| {
242 try expect(nextAfter(T80, 0x1.FFFFFFFFFFFFFFFEp3, math.inf(T80)) == 0x1p4);
243 try expect(nextAfter(T80, 0x1p4, -math.inf(T80)) == 0x1.FFFFFFFFFFFFFFFEp3);
244 try expect(nextAfter(T80, -0x1.FFFFFFFFFFFFFFFEp3, -math.inf(T80)) == -0x1p4);
245 try expect(nextAfter(T80, -0x1p4, math.inf(T80)) == -0x1.FFFFFFFFFFFFFFFEp3);
246 }
247 inline for (.{f128} ++ if (@bitSizeOf(c_longdouble) == 128) .{c_longdouble} else .{}) |T128| {
248 try expect(nextAfter(T128, 0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp3, math.inf(T128)) == 0x1p4);
249 try expect(nextAfter(T128, 0x1p4, -math.inf(T128)) == 0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp3);
250 try expect(nextAfter(T128, -0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp3, -math.inf(T128)) == -0x1p4);
251 try expect(nextAfter(T128, -0x1p4, math.inf(T128)) == -0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp3);
252 }
253
254 // normal -> subnormal
255 try expect(nextAfter(f16, 0x1p-14, -math.inf(f16)) == 0x0.FFCp-14);
256 try expect(nextAfter(f16, -0x1p-14, math.inf(f16)) == -0x0.FFCp-14);
257 try expect(nextAfter(f32, 0x1p-126, -math.inf(f32)) == 0x0.FFFFFEp-126);
258 try expect(nextAfter(f32, -0x1p-126, math.inf(f32)) == -0x0.FFFFFEp-126);
259 inline for (.{f64} ++ if (@bitSizeOf(c_longdouble) == 64) .{c_longdouble} else .{}) |T64| {
260 try expect(nextAfter(T64, 0x1p-1022, -math.inf(T64)) == 0x0.FFFFFFFFFFFFFp-1022);
261 try expect(nextAfter(T64, -0x1p-1022, math.inf(T64)) == -0x0.FFFFFFFFFFFFFp-1022);
262 }
263 inline for (.{f80} ++ if (@bitSizeOf(c_longdouble) == 80) .{c_longdouble} else .{}) |T80| {
264 try expect(nextAfter(T80, 0x1p-16382, -math.inf(T80)) == 0x0.FFFFFFFFFFFFFFFEp-16382);
265 try expect(nextAfter(T80, -0x1p-16382, math.inf(T80)) == -0x0.FFFFFFFFFFFFFFFEp-16382);
266 }
267 inline for (.{f128} ++ if (@bitSizeOf(c_longdouble) == 128) .{c_longdouble} else .{}) |T128| {
268 try expect(nextAfter(T128, 0x1p-16382, -math.inf(T128)) == 0x0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382);
269 try expect(nextAfter(T128, -0x1p-16382, math.inf(T128)) == -0x0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382);
270 }
271
272 // subnormal -> normal
273 try expect(nextAfter(f16, 0x0.FFCp-14, math.inf(f16)) == 0x1p-14);
274 try expect(nextAfter(f16, -0x0.FFCp-14, -math.inf(f16)) == -0x1p-14);
275 try expect(nextAfter(f32, 0x0.FFFFFEp-126, math.inf(f32)) == 0x1p-126);
276 try expect(nextAfter(f32, -0x0.FFFFFEp-126, -math.inf(f32)) == -0x1p-126);
277 inline for (.{f64} ++ if (@bitSizeOf(c_longdouble) == 64) .{c_longdouble} else .{}) |T64| {
278 try expect(nextAfter(T64, 0x0.FFFFFFFFFFFFFp-1022, math.inf(T64)) == 0x1p-1022);
279 try expect(nextAfter(T64, -0x0.FFFFFFFFFFFFFp-1022, -math.inf(T64)) == -0x1p-1022);
280 }
281 inline for (.{f80} ++ if (@bitSizeOf(c_longdouble) == 80) .{c_longdouble} else .{}) |T80| {
282 try expect(nextAfter(T80, 0x0.FFFFFFFFFFFFFFFEp-16382, math.inf(T80)) == 0x1p-16382);
283 try expect(nextAfter(T80, -0x0.FFFFFFFFFFFFFFFEp-16382, -math.inf(T80)) == -0x1p-16382);
284 }
285 inline for (.{f128} ++ if (@bitSizeOf(c_longdouble) == 128) .{c_longdouble} else .{}) |T128| {
286 try expect(nextAfter(T128, 0x0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382, math.inf(T128)) == 0x1p-16382);
287 try expect(nextAfter(T128, -0x0.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-16382, -math.inf(T128)) == -0x1p-16382);
288 }
289
290 // special values
291 inline for (.{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
292 try expect(bitwiseEqual(T, nextAfter(T, 0.0, 0.0), 0.0));
293 try expect(bitwiseEqual(T, nextAfter(T, 0.0, -0.0), -0.0));
294 try expect(bitwiseEqual(T, nextAfter(T, -0.0, -0.0), -0.0));
295 try expect(bitwiseEqual(T, nextAfter(T, -0.0, 0.0), 0.0));
296 try expect(nextAfter(T, 0.0, math.inf(T)) == math.floatTrueMin(T));
297 try expect(nextAfter(T, 0.0, -math.inf(T)) == -math.floatTrueMin(T));
298 try expect(nextAfter(T, -0.0, -math.inf(T)) == -math.floatTrueMin(T));
299 try expect(nextAfter(T, -0.0, math.inf(T)) == math.floatTrueMin(T));
300 try expect(bitwiseEqual(T, nextAfter(T, math.floatTrueMin(T), 0.0), 0.0));
301 try expect(bitwiseEqual(T, nextAfter(T, math.floatTrueMin(T), -0.0), 0.0));
302 try expect(bitwiseEqual(T, nextAfter(T, math.floatTrueMin(T), -math.inf(T)), 0.0));
303 try expect(bitwiseEqual(T, nextAfter(T, -math.floatTrueMin(T), -0.0), -0.0));
304 try expect(bitwiseEqual(T, nextAfter(T, -math.floatTrueMin(T), 0.0), -0.0));
305 try expect(bitwiseEqual(T, nextAfter(T, -math.floatTrueMin(T), math.inf(T)), -0.0));
306 try expect(nextAfter(T, math.inf(T), math.inf(T)) == math.inf(T));
307 try expect(nextAfter(T, math.inf(T), -math.inf(T)) == math.floatMax(T));
308 try expect(nextAfter(T, math.floatMax(T), math.inf(T)) == math.inf(T));
309 try expect(nextAfter(T, -math.inf(T), -math.inf(T)) == -math.inf(T));
310 try expect(nextAfter(T, -math.inf(T), math.inf(T)) == -math.floatMax(T));
311 try expect(nextAfter(T, -math.floatMax(T), -math.inf(T)) == -math.inf(T));
312 try expect(math.isNan(nextAfter(T, 1.0, math.nan(T))));
313 try expect(math.isNan(nextAfter(T, math.nan(T), 1.0)));
314 try expect(math.isNan(nextAfter(T, math.nan(T), math.nan(T))));
315 try expect(math.isNan(nextAfter(T, math.inf(T), math.nan(T))));
316 try expect(math.isNan(nextAfter(T, -math.inf(T), math.nan(T))));
317 try expect(math.isNan(nextAfter(T, math.nan(T), math.inf(T))));
318 try expect(math.isNan(nextAfter(T, math.nan(T), -math.inf(T))));
319 }
320}
321
322/// Helps ensure that 0.0 doesn't compare equal to -0.0.
323fn bitwiseEqual(comptime T: type, x: T, y: T) bool {
324 comptime assert(@typeInfo(T) == .Float);
325 const Bits = std.meta.Int(.unsigned, @bitSizeOf(T));
326 return @as(Bits, @bitCast(x)) == @as(Bits, @bitCast(y));
327}