authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-09 10:36:31-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-10-09 11:09:02-07:00
logeac1e613be56875a08102bd06e333a0621600ee6
tree7c65fbc28f805bcb3d0e1fe4522a738cb36df97d
parent7f508480f49b78817bf67579a8810d4499cbbc13

compiler_rt: Re-implement `ldexp`/`ilogb` using bit-ops

This re-write was needed to fix deficiencies in the existing ldexp, which was failing to compute correct results for both f16 and f80. It would be nice to add a fast multiplication-based fallback in the future for targets that have a hardware FPU, but this implementation should be much faster than the existing for targets without one.

2 files changed, 227 insertions(+), 194 deletions(-)

lib/std/math/ilogb.zig+110-128
...@@ -15,175 +15,157 @@ const minInt = std.math.minInt;...@@ -15,175 +15,157 @@ const minInt = std.math.minInt;
15///15///
16/// Special Cases:16/// Special Cases:
17/// - ilogb(+-inf) = maxInt(i32)17/// - ilogb(+-inf) = maxInt(i32)
18/// - ilogb(0) = maxInt(i32)18/// - ilogb(+-0) = minInt(i32)
19/// - ilogb(nan) = maxInt(i32)19/// - ilogb(nan) = minInt(i32)
20pub fn ilogb(x: anytype) i32 {20pub fn ilogb(x: anytype) i32 {
21 const T = @TypeOf(x);21 const T = @TypeOf(x);
22 return switch (T) {22 return ilogbX(T, x);
23 f32 => ilogb32(x),
24 f64 => ilogb64(x),
25 f128 => ilogb128(x),
26 else => @compileError("ilogb not implemented for " ++ @typeName(T)),
27 };
28}23}
2924
30// TODO: unify these implementations with generics25pub const fp_ilogbnan = minInt(i32);
26pub const fp_ilogb0 = minInt(i32);
3127
32// NOTE: Should these be exposed publicly?28fn ilogbX(comptime T: type, x: T) i32 {
33const fp_ilogbnan = -1 - @as(i32, maxInt(u32) >> 1);29 const typeWidth = @typeInfo(T).Float.bits;
34const fp_ilogb0 = fp_ilogbnan;30 const significandBits = math.floatMantissaBits(T);
31 const exponentBits = math.floatExponentBits(T);
3532
36fn ilogb32(x: f32) i32 {33 const Z = std.meta.Int(.unsigned, typeWidth);
37 var u = @bitCast(u32, x);
38 var e = @intCast(i32, (u >> 23) & 0xFF);
3934
40 // TODO: We should be able to merge this with the lower check.35 const signBit = (@as(Z, 1) << (significandBits + exponentBits));
41 if (math.isNan(x)) {36 const maxExponent = ((1 << exponentBits) - 1);
42 return maxInt(i32);37 const exponentBias = (maxExponent >> 1);
43 }
4438
45 if (e == 0) {39 const absMask = signBit - 1;
46 u <<= 9;
47 if (u == 0) {
48 math.raiseInvalid();
49 return fp_ilogb0;
50 }
5140
52 // subnormal41 var u = @bitCast(Z, x) & absMask;
53 e = -0x7F;42 var e = @intCast(i32, u >> significandBits);
54 while (u >> 31 == 0) : (u <<= 1) {
55 e -= 1;
56 }
57 return e;
58 }
59
60 if (e == 0xFF) {
61 math.raiseInvalid();
62 if (u << 9 != 0) {
63 return fp_ilogbnan;
64 } else {
65 return maxInt(i32);
66 }
67 }
68
69 return e - 0x7F;
70}
71
72fn ilogb64(x: f64) i32 {
73 var u = @bitCast(u64, x);
74 var e = @intCast(i32, (u >> 52) & 0x7FF);
75
76 if (math.isNan(x)) {
77 return maxInt(i32);
78 }
7943
80 if (e == 0) {44 if (e == 0) {
81 u <<= 12;
82 if (u == 0) {45 if (u == 0) {
83 math.raiseInvalid();46 math.raiseInvalid();
84 return fp_ilogb0;47 return fp_ilogb0;
85 }48 }
8649
87 // subnormal50 // offset sign bit, exponent bits, and integer bit (if present) + bias
88 e = -0x3FF;51 const offset = 1 + exponentBits + @boolToInt(T == f80) - exponentBias;
89 while (u >> 63 == 0) : (u <<= 1) {52 return offset - @intCast(i32, @clz(u));
90 e -= 1;
91 }
92 return e;
93 }53 }
9454
95 if (e == 0x7FF) {55 if (e == maxExponent) {
96 math.raiseInvalid();56 math.raiseInvalid();
97 if (u << 12 != 0) {57 if (u > @bitCast(Z, math.inf(T))) {
98 return fp_ilogbnan;58 return fp_ilogbnan; // u is a NaN
99 } else {59 } else return maxInt(i32);
100 return maxInt(i32);
101 }
102 }60 }
10361
104 return e - 0x3FF;62 return e - exponentBias;
105}63}
10664
107fn ilogb128(x: f128) i32 {65test "type dispatch" {
108 var u = @bitCast(u128, x);66 try expect(ilogb(@as(f32, 0.2)) == ilogbX(f32, 0.2));
109 var e = @intCast(i32, (u >> 112) & 0x7FFF);67 try expect(ilogb(@as(f64, 0.2)) == ilogbX(f64, 0.2));
110
111 if (math.isNan(x)) {
112 return maxInt(i32);
113 }
114
115 if (e == 0) {
116 u <<= 16;
117 if (u == 0) {
118 math.raiseInvalid();
119 return fp_ilogb0;
120 }
121
122 // subnormal x
123 return ilogb128(x * 0x1p120) - 120;
124 }
125
126 if (e == 0x7FFF) {
127 math.raiseInvalid();
128 if (u << 16 != 0) {
129 return fp_ilogbnan;
130 } else {
131 return maxInt(i32);
132 }
133 }
134
135 return e - 0x3FFF;
136}68}
13769
138test "type dispatch" {70test "16" {
139 try expect(ilogb(@as(f32, 0.2)) == ilogb32(0.2));71 try expect(ilogbX(f16, 0.0) == fp_ilogb0);
140 try expect(ilogb(@as(f64, 0.2)) == ilogb64(0.2));72 try expect(ilogbX(f16, 0.5) == -1);
73 try expect(ilogbX(f16, 0.8923) == -1);
74 try expect(ilogbX(f16, 10.0) == 3);
75 try expect(ilogbX(f16, -65504) == 15);
76 try expect(ilogbX(f16, 2398.23) == 11);
77
78 try expect(ilogbX(f16, 0x1p-1) == -1);
79 try expect(ilogbX(f16, 0x1p-17) == -17);
80 try expect(ilogbX(f16, 0x1p-24) == -24);
141}81}
14282
143test "32" {83test "32" {
144 try expect(ilogb32(0.0) == fp_ilogb0);84 try expect(ilogbX(f32, 0.0) == fp_ilogb0);
145 try expect(ilogb32(0.5) == -1);85 try expect(ilogbX(f32, 0.5) == -1);
146 try expect(ilogb32(0.8923) == -1);86 try expect(ilogbX(f32, 0.8923) == -1);
147 try expect(ilogb32(10.0) == 3);87 try expect(ilogbX(f32, 10.0) == 3);
148 try expect(ilogb32(-123984) == 16);88 try expect(ilogbX(f32, -123984) == 16);
149 try expect(ilogb32(2398.23) == 11);89 try expect(ilogbX(f32, 2398.23) == 11);
90
91 try expect(ilogbX(f32, 0x1p-1) == -1);
92 try expect(ilogbX(f32, 0x1p-122) == -122);
93 try expect(ilogbX(f32, 0x1p-127) == -127);
150}94}
15195
152test "64" {96test "64" {
153 try expect(ilogb64(0.0) == fp_ilogb0);97 try expect(ilogbX(f64, 0.0) == fp_ilogb0);
154 try expect(ilogb64(0.5) == -1);98 try expect(ilogbX(f64, 0.5) == -1);
155 try expect(ilogb64(0.8923) == -1);99 try expect(ilogbX(f64, 0.8923) == -1);
156 try expect(ilogb64(10.0) == 3);100 try expect(ilogbX(f64, 10.0) == 3);
157 try expect(ilogb64(-123984) == 16);101 try expect(ilogbX(f64, -123984) == 16);
158 try expect(ilogb64(2398.23) == 11);102 try expect(ilogbX(f64, 2398.23) == 11);
103
104 try expect(ilogbX(f64, 0x1p-1) == -1);
105 try expect(ilogbX(f64, 0x1p-127) == -127);
106 try expect(ilogbX(f64, 0x1p-1012) == -1012);
107 try expect(ilogbX(f64, 0x1p-1023) == -1023);
108}
109
110test "80" {
111 try expect(ilogbX(f80, 0.0) == fp_ilogb0);
112 try expect(ilogbX(f80, 0.5) == -1);
113 try expect(ilogbX(f80, 0.8923) == -1);
114 try expect(ilogbX(f80, 10.0) == 3);
115 try expect(ilogbX(f80, -123984) == 16);
116 try expect(ilogbX(f80, 2398.23) == 11);
117
118 try expect(ilogbX(f80, 0x1p-1) == -1);
119 try expect(ilogbX(f80, 0x1p-127) == -127);
120 try expect(ilogbX(f80, 0x1p-1023) == -1023);
121 try expect(ilogbX(f80, 0x1p-16383) == -16383);
159}122}
160123
161test "128" {124test "128" {
162 try expect(ilogb128(0.0) == fp_ilogb0);125 try expect(ilogbX(f128, 0.0) == fp_ilogb0);
163 try expect(ilogb128(0.5) == -1);126 try expect(ilogbX(f128, 0.5) == -1);
164 try expect(ilogb128(0.8923) == -1);127 try expect(ilogbX(f128, 0.8923) == -1);
165 try expect(ilogb128(10.0) == 3);128 try expect(ilogbX(f128, 10.0) == 3);
166 try expect(ilogb128(-123984) == 16);129 try expect(ilogbX(f128, -123984) == 16);
167 try expect(ilogb128(2398.23) == 11);130 try expect(ilogbX(f128, 2398.23) == 11);
131
132 try expect(ilogbX(f128, 0x1p-1) == -1);
133 try expect(ilogbX(f128, 0x1p-127) == -127);
134 try expect(ilogbX(f128, 0x1p-1023) == -1023);
135 try expect(ilogbX(f128, 0x1p-16383) == -16383);
136}
137
138test "16 special" {
139 try expect(ilogbX(f16, math.inf(f16)) == maxInt(i32));
140 try expect(ilogbX(f16, -math.inf(f16)) == maxInt(i32));
141 try expect(ilogbX(f16, 0.0) == minInt(i32));
142 try expect(ilogbX(f16, math.nan(f16)) == fp_ilogbnan);
168}143}
169144
170test "32 special" {145test "32 special" {
171 try expect(ilogb32(math.inf(f32)) == maxInt(i32));146 try expect(ilogbX(f32, math.inf(f32)) == maxInt(i32));
172 try expect(ilogb32(-math.inf(f32)) == maxInt(i32));147 try expect(ilogbX(f32, -math.inf(f32)) == maxInt(i32));
173 try expect(ilogb32(0.0) == minInt(i32));148 try expect(ilogbX(f32, 0.0) == minInt(i32));
174 try expect(ilogb32(math.nan(f32)) == maxInt(i32));149 try expect(ilogbX(f32, math.nan(f32)) == fp_ilogbnan);
175}150}
176151
177test "64 special" {152test "64 special" {
178 try expect(ilogb64(math.inf(f64)) == maxInt(i32));153 try expect(ilogbX(f64, math.inf(f64)) == maxInt(i32));
179 try expect(ilogb64(-math.inf(f64)) == maxInt(i32));154 try expect(ilogbX(f64, -math.inf(f64)) == maxInt(i32));
180 try expect(ilogb64(0.0) == minInt(i32));155 try expect(ilogbX(f64, 0.0) == minInt(i32));
181 try expect(ilogb64(math.nan(f64)) == maxInt(i32));156 try expect(ilogbX(f64, math.nan(f64)) == fp_ilogbnan);
157}
158
159test "80 special" {
160 try expect(ilogbX(f80, math.inf(f80)) == maxInt(i32));
161 try expect(ilogbX(f80, -math.inf(f80)) == maxInt(i32));
162 try expect(ilogbX(f80, 0.0) == minInt(i32));
163 try expect(ilogbX(f80, math.nan(f80)) == fp_ilogbnan);
182}164}
183165
184test "128 special" {166test "128 special" {
185 try expect(ilogb128(math.inf(f128)) == maxInt(i32));167 try expect(ilogbX(f128, math.inf(f128)) == maxInt(i32));
186 try expect(ilogb128(-math.inf(f128)) == maxInt(i32));168 try expect(ilogbX(f128, -math.inf(f128)) == maxInt(i32));
187 try expect(ilogb128(0.0) == minInt(i32));169 try expect(ilogbX(f128, 0.0) == minInt(i32));
188 try expect(ilogb128(math.nan(f128)) == maxInt(i32));170 try expect(ilogbX(f128, math.nan(f128)) == fp_ilogbnan);
189}171}
lib/std/math/ldexp.zig+117-66
...@@ -1,91 +1,142 @@...@@ -1,91 +1,142 @@
1// Ported from musl, which is licensed under the MIT license:
2// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
3//
4// https://git.musl-libc.org/cgit/musl/tree/src/math/ldexpf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/ldexp.c
6
7const std = @import("std");1const std = @import("std");
8const math = std.math;2const math = std.math;
3const Log2Int = std.math.Log2Int;
9const assert = std.debug.assert;4const assert = std.debug.assert;
10const expect = std.testing.expect;5const expect = std.testing.expect;
116
12/// Returns x * 2^n.7/// Returns x * 2^n.
13pub fn ldexp(x: anytype, n: i32) @TypeOf(x) {8pub fn ldexp(x: anytype, n: i32) @TypeOf(x) {
14 var base = x;9 const T = @TypeOf(x);
15 var shift = n;
16
17 const T = @TypeOf(base);
18 const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);10 const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
1911
12 const exponent_bits = math.floatExponentBits(T);
20 const mantissa_bits = math.floatMantissaBits(T);13 const mantissa_bits = math.floatMantissaBits(T);
21 const exponent_min = math.floatExponentMin(T);14 const fractional_bits = math.floatFractionalBits(T);
22 const exponent_max = math.floatExponentMax(T);15
2316 const max_biased_exponent = 2 * math.floatExponentMax(T);
24 const exponent_bias = exponent_max;17 const mantissa_mask = @as(TBits, (1 << mantissa_bits) - 1);
2518
26 // fix double rounding errors in subnormal ranges19 const repr = @bitCast(TBits, x);
27 // https://git.musl-libc.org/cgit/musl/commit/src/math/ldexp.c?id=8c44a060243f04283ca68dad199aab90336141db20 const sign_bit = repr & (1 << (exponent_bits + mantissa_bits));
28 const scale_min_expo = exponent_min + mantissa_bits + 1;21
29 const scale_min = @bitCast(T, @as(TBits, scale_min_expo + exponent_bias) << mantissa_bits);22 if (math.isNan(x) or !math.isFinite(x))
30 const scale_max = @bitCast(T, @intCast(TBits, exponent_max + exponent_bias) << mantissa_bits);23 return x;
3124
32 // scale `shift` within floating point limits, if possible25 var exponent: i32 = @intCast(i32, (repr << 1) >> (mantissa_bits + 1));
33 // second pass is possible due to subnormal range26 if (exponent == 0)
34 // third pass always results in +/-0.0 or +/-inf27 exponent += (@as(i32, exponent_bits) + @boolToInt(T == f80)) - @clz(repr << 1);
35 if (shift > exponent_max) {28
36 base *= scale_max;29 if (n >= 0) {
37 shift -= exponent_max;30 if (n > max_biased_exponent - exponent) {
38 if (shift > exponent_max) {31 // Overflow. Return +/- inf
39 base *= scale_max;32 return @bitCast(T, @bitCast(TBits, math.inf(T)) | sign_bit);
40 shift -= exponent_max;33 } else if (exponent + n <= 0) {
41 if (shift > exponent_max) shift = exponent_max;34 // Result is subnormal
35 return @bitCast(T, (repr << @intCast(Log2Int(TBits), n)) | sign_bit);
36 } else if (exponent <= 0) {
37 // Result is normal, but needs shifting
38 var result = @intCast(TBits, n + exponent) << mantissa_bits;
39 result |= (repr << @intCast(Log2Int(TBits), 1 - exponent)) & mantissa_mask;
40 return @bitCast(T, result | sign_bit);
42 }41 }
43 } else if (shift < exponent_min) {42
44 base *= scale_min;43 // Result needs no shifting
45 shift -= scale_min_expo;44 return @bitCast(T, repr + (@intCast(TBits, n) << mantissa_bits));
46 if (shift < exponent_min) {45 } else {
47 base *= scale_min;46 if (n <= -exponent) {
48 shift -= scale_min_expo;47 if (n < -(mantissa_bits + exponent))
49 if (shift < exponent_min) shift = exponent_min;48 return @bitCast(T, sign_bit); // Severe underflow. Return +/- 0
49
50 // Result underflowed, we need to shift and round
51 const shift = @intCast(Log2Int(TBits), math.min(-n, -(exponent + n) + 1));
52 const exact_tie: bool = @ctz(repr) == shift - 1;
53 var result = repr & mantissa_mask;
54
55 if (T != f80) // Include integer bit
56 result |= @as(TBits, @boolToInt(exponent > 0)) << fractional_bits;
57 result = @intCast(TBits, (result >> (shift - 1)));
58
59 // Round result, including round-to-even for exact ties
60 result = ((result + 1) >> 1) & ~@as(TBits, @boolToInt(exact_tie));
61 return @bitCast(T, result | sign_bit);
50 }62 }
51 }
5263
53 return base * @bitCast(T, @intCast(TBits, shift + exponent_bias) << mantissa_bits);64 // Result is exact, and needs no shifting
65 return @bitCast(T, repr - (@intCast(TBits, -n) << mantissa_bits));
66 }
54}67}
5568
56test "math.ldexp" {69test "math.ldexp" {
57 // TODO derive the various constants here with new maths API
58
59 // basic usage
60 try expect(ldexp(@as(f16, 1.5), 4) == 24.0);
61 try expect(ldexp(@as(f32, 1.5), 4) == 24.0);
62 try expect(ldexp(@as(f64, 1.5), 4) == 24.0);
63 try expect(ldexp(@as(f128, 1.5), 4) == 24.0);
6470
65 // subnormals71 // subnormals
66 try expect(math.isNormal(ldexp(@as(f16, 1.0), -14)));72 try expect(ldexp(@as(f16, 0x1.1FFp14), -14 - 9 - 15) == math.floatTrueMin(f16));
67 try expect(!math.isNormal(ldexp(@as(f16, 1.0), -15)));
68 try expect(math.isNormal(ldexp(@as(f32, 1.0), -126)));
69 try expect(!math.isNormal(ldexp(@as(f32, 1.0), -127)));
70 try expect(math.isNormal(ldexp(@as(f64, 1.0), -1022)));
71 try expect(!math.isNormal(ldexp(@as(f64, 1.0), -1023)));
72 try expect(math.isNormal(ldexp(@as(f128, 1.0), -16382)));
73 try expect(!math.isNormal(ldexp(@as(f128, 1.0), -16383)));
74 // unreliable due to lack of native f16 support, see talk on PR #8733
75 // try expect(ldexp(@as(f16, 0x1.1FFp-1), -14 - 9) == math.floatTrueMin(f16));
76 try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32));73 try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32));
77 try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.floatTrueMin(f64));74 try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.floatTrueMin(f64));
75 try expect(ldexp(@as(f80, 0x1.7FFFFFFFFFFFFFFEp-1), -16382 - 62) == math.floatTrueMin(f80));
78 try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128));76 try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128));
7977
80 // float limits
81 try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0);78 try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0);
82 try expect(ldexp(math.floatMax(f32), -128 - 149 - 1) == 0.0);79 try expect(ldexp(math.floatMax(f32), -128 - 149 - 1) == 0.0);
83 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24)));80
84 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24 + 1)));81 @setEvalBranchQuota(10_000);
85 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149)));82
86 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149 + 1)));83 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
87 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074)));84 const fractional_bits = math.floatFractionalBits(T);
88 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074 + 1)));85
89 try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494)));86 const min_exponent = math.floatExponentMin(T);
90 try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494 + 1)));87 const max_exponent = math.floatExponentMax(T);
88 const exponent_bias = max_exponent;
89
90 // basic usage
91 try expect(ldexp(@as(T, 1.5), 4) == 24.0);
92
93 // normals -> subnormals
94 try expect(math.isNormal(ldexp(@as(T, 1.0), min_exponent)));
95 try expect(!math.isNormal(ldexp(@as(T, 1.0), min_exponent - 1)));
96
97 // normals -> zero
98 try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits) > 0.0);
99 try expect(ldexp(@as(T, 1.0), min_exponent - fractional_bits - 1) == 0.0);
100
101 // subnormals -> zero
102 try expect(ldexp(math.floatTrueMin(T), 0) > 0.0);
103 try expect(ldexp(math.floatTrueMin(T), -1) == 0.0);
104
105 // subnormals -> subnormals
106 try expect(ldexp(math.floatTrueMin(T), 3) == math.floatTrueMin(T) * 8);
107 try expect(ldexp(math.floatTrueMin(T) * 8, -2) == math.floatTrueMin(T) * 2);
108 try expect(ldexp(math.floatTrueMin(T) * 8, -3) == math.floatTrueMin(T));
109
110 // subnormals -> normals (+)
111 try expect(ldexp(math.floatTrueMin(T), fractional_bits) == math.floatMin(T));
112 try expect(ldexp(math.floatTrueMin(T), fractional_bits - 1) == math.floatMin(T) * 0.5);
113
114 // subnormals -> normals (-)
115 try expect(ldexp(-math.floatTrueMin(T), fractional_bits) == -math.floatMin(T));
116 try expect(ldexp(-math.floatTrueMin(T), fractional_bits - 1) == -math.floatMin(T) * 0.5);
117
118 // subnormals -> float limits (+inf)
119 try expect(math.isFinite(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
120 try expect(ldexp(math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == math.inf(T));
121
122 // subnormals -> float limits (-inf)
123 try expect(math.isFinite(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits - 1)));
124 try expect(ldexp(-math.floatTrueMin(T), max_exponent + exponent_bias + fractional_bits) == -math.inf(T));
125
126 // infinity -> infinity
127 try expect(ldexp(math.inf(T), math.maxInt(i32)) == math.inf(T));
128 try expect(ldexp(math.inf(T), math.minInt(i32)) == math.inf(T));
129 try expect(ldexp(math.inf(T), max_exponent) == math.inf(T));
130 try expect(ldexp(math.inf(T), min_exponent) == math.inf(T));
131 try expect(ldexp(-math.inf(T), math.maxInt(i32)) == -math.inf(T));
132 try expect(ldexp(-math.inf(T), math.minInt(i32)) == -math.inf(T));
133
134 // extremely large n
135 try expect(ldexp(math.floatMax(T), math.maxInt(i32)) == math.inf(T));
136 try expect(ldexp(math.floatMax(T), -math.maxInt(i32)) == 0.0);
137 try expect(ldexp(math.floatMax(T), math.minInt(i32)) == 0.0);
138 try expect(ldexp(math.floatTrueMin(T), math.maxInt(i32)) == math.inf(T));
139 try expect(ldexp(math.floatTrueMin(T), -math.maxInt(i32)) == 0.0);
140 try expect(ldexp(math.floatTrueMin(T), math.minInt(i32)) == 0.0);
141 }
91}142}