authorgravatar for 3575188313@qq.comZhenming-Lin <3575188313@qq.com> 2025-10-07 02:45:51+08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-10-06 11:45:51-07:00
log9bfd4c3017aa8ecc5e67e6432810af7457b51c0f
tree4eac854e4c3628251736cc1c51531089684b7c17
parent851ae9bb43251950ddab9775df0310370a4e2301
signaturebadge-check Signed by PGP key B5690EEEBB952194

improve impl of `__sqrth`, `sqrtf`, `sqrt`, `__sqrtx` and `sqrtq` (#25416)

The previous version (ported from musl) used bit-by-bit calculations and was slow, but the current version (also ported from musl) uses lookup tables combined with Goldschmidt iterations to significantly improve the speed.

1 files changed, 674 insertions(+), 242 deletions(-)

lib/compiler_rt/sqrt.zig+674-242
......@@ -1,3 +1,10 @@
1//! Ported from musl, which is MIT licensed.
2//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
3//!
4//! https://git.musl-libc.org/cgit/musl/tree/src/math/sqrtf.c
5//! https://git.musl-libc.org/cgit/musl/tree/src/math/sqrt.c
6//! https://git.musl-libc.org/cgit/musl/tree/src/math/sqrtl.c
7
18const std = @import("std");
29const builtin = @import("builtin");
310const arch = builtin.cpu.arch;
......@@ -21,227 +28,470 @@ comptime {
2128}
2229
2330pub fn __sqrth(x: f16) callconv(.c) f16 {
24 // TODO: more efficient implementation
25 return @floatCast(sqrtf(x));
26}
27
28pub fn sqrtf(x: f32) callconv(.c) f32 {
29 const tiny: f32 = 1.0e-30;
30 const sign: i32 = @bitCast(@as(u32, 0x80000000));
31 var ix: i32 = @bitCast(x);
32
33 if ((ix & 0x7F800000) == 0x7F800000) {
34 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
35 }
36
37 // zero
38 if (ix <= 0) {
39 if (ix & ~sign == 0) {
40 return x; // sqrt (+-0) = +-0
41 }
42 if (ix < 0) {
43 return math.nan(f32);
44 }
31 var ix: u16 = @bitCast(x);
32 var top = ix >> 10;
33
34 // special case handling.
35 if (top -% 0x01 >= 0x1F - 0x01) {
36 @branchHint(.unlikely);
37 // x < 0x1p-14 or inf or nan.
38 if (ix & 0x7FFF == 0) return x;
39 if (ix == 0x7C00) return x;
40 if (ix > 0x7C00) return math.nan(f16);
41 // x is subnormal, normalize it.
42 ix = @bitCast(x * 0x1p10);
43 top = (ix >> 10) -% 10;
4544 }
4645
47 // normalize
48 var m = ix >> 23;
49 if (m == 0) {
50 // subnormal
51 var i: i32 = 0;
52 while (ix & 0x00800000 == 0) : (i += 1) {
53 ix <<= 1;
54 }
55 m -= i - 1;
46 // argument reduction:
47 // x = 4^e m; with integer e, and m in [1, 4)
48 // m: fixed point representation [2.14]
49 // 2^e is the exponent part of the result.
50 const even = (top & 1) != 0;
51 const m = if (even) (ix << 4) & 0x7FFF else (ix << 5) | 0x8000;
52 top = (top +% 0x0F) >> 1;
53
54 // approximate r ~ 1/sqrt(m) and s ~ sqrt(m) when m in [1,4)
55 // the fixed point representations are
56 // m: 2.14 r: 0.16, s: 2.14, d: 2.14, u: 2.14, three: 2.14
57 const three: u16 = 0xC000;
58 const i: usize = @intCast((ix >> 4) & 0x7F);
59 const r = __rsqrt_tab[i];
60 // |r*sqrt(m) - 1| < 0x1p-8
61 var s = mul16(m, r);
62 // |s/sqrt(m) - 1| < 0x1p-8
63 const d = mul16(s, r);
64 const u = three - d;
65 s = mul16(s, u); // repr: 3.13
66 // -0x1.20p-13 < s/sqrt(m) - 1 < 0x7Dp-16
67 s = (s - 1) >> 3; // repr: 6.10
68 // s < sqrt(m) < s + 0x1.24p-10
69
70 // compute nearest rounded result:
71 // the nearest result to 10 bits is either s or s+0x1p-10,
72 // we can decide by comparing (2^10 s + 0.5)^2 to 2^20 m.
73 const d0 = (m << 6) -% s *% s;
74 const d1 = s -% d0;
75 const d2 = d1 +% s +% 1;
76 s += d1 >> 15;
77 s &= 0x03FF;
78 s |= top << 10;
79 const y: f16 = @bitCast(s);
80
81 // handle rounding modes and inexact exception:
82 // only (s+1)^2 == 2^6 m case is exact otherwise
83 // add a tiny value to cause the fenv effects.
84 if (d2 != 0) {
85 @branchHint(.likely);
86 var tiny: u16 = 0x0001;
87 tiny |= (d1 ^ d2) & 0x8000;
88 const t: f16 = @bitCast(tiny);
89 return y + t;
5690 }
5791
58 m -= 127; // unbias exponent
59 ix = (ix & 0x007FFFFF) | 0x00800000;
60
61 if (m & 1 != 0) { // odd m, double x to even
62 ix += ix;
63 }
92 return y;
93}
6494
65 m >>= 1; // m = [m / 2]
66
67 // sqrt(x) bit by bit
68 ix += ix;
69 var q: i32 = 0; // q = sqrt(x)
70 var s: i32 = 0;
71 var r: i32 = 0x01000000; // r = moving bit right -> left
72
73 while (r != 0) {
74 const t = s + r;
75 if (t <= ix) {
76 s = t + r;
77 ix -= t;
78 q += r;
79 }
80 ix += ix;
81 r >>= 1;
95pub fn sqrtf(x: f32) callconv(.c) f32 {
96 var ix: u32 = @bitCast(x);
97 var top = ix >> 23;
98
99 // special case handling.
100 if (top -% 0x01 >= 0xFF - 0x01) {
101 @branchHint(.unlikely);
102 // x < 0x1p-126 or inf or nan.
103 if (ix & 0x7FFF_FFFF == 0) return x;
104 if (ix == 0x7F80_0000) return x;
105 if (ix > 0x7F80_0000) return math.nan(f32);
106 // x is subnormal, normalize it.
107 ix = @bitCast(x * 0x1p23);
108 top = (ix >> 23) -% 23;
82109 }
83110
84 // floating add to find rounding direction
85 if (ix != 0) {
86 var z = 1.0 - tiny; // inexact
87 if (z >= 1.0) {
88 z = 1.0 + tiny;
89 if (z > 1.0) {
90 q += 2;
91 } else {
92 if (q & 1 != 0) {
93 q += 1;
94 }
95 }
96 }
111 // argument reduction:
112 // x = 4^e m; with integer e, and m in [1, 4)
113 // m: fixed point representation [2.30]
114 // 2^e is the exponent part of the result.
115 const even = (top & 1) != 0;
116 const m = if (even) (ix << 7) & 0x7FFF_FFFF else (ix << 8) | 0x8000_0000;
117 top = (top +% 0x7F) >> 1;
118
119 // approximate r ~ 1/sqrt(m) and s ~ sqrt(m) when m in [1,4)
120 // the fixed point representations are
121 // m: 2.30 r: 0.32, s: 2.30, d: 2.30, u: 2.30, three: 2.30
122 const three: u32 = 0xC000_0000;
123 var i: usize = @intCast((ix >> 17) & 0x3F);
124 if (even) i += 64;
125 var r = @as(u32, @intCast(__rsqrt_tab[i])) << 16;
126 // |r*sqrt(m) - 1| < 0x1p-8
127 var s = mul32(m, r);
128 // |s/sqrt(m) - 1| < 0x1p-8
129 var d = mul32(s, r);
130 var u = three - d;
131 r = mul32(r, u) << 1;
132 // |r*sqrt(m) - 1| < 0x1.7bp-16
133 s = mul32(s, u) << 1;
134 // |s/sqrt(m) - 1| < 0x1.7bp-16
135 d = mul32(s, r);
136 u = three - d;
137 s = mul32(s, u); // repr: 3.29
138 // -0x1.03p-28 < s/sqrt(m) - 1 < 0x1.fp-31
139 s = (s - 1) >> 6; // repr: 9.23
140 // s < sqrt(m) < s + 0x1.08p-23
141
142 // compute nearest rounded result:
143 // the nearest result to 23 bits is either s or s+0x1p-23,
144 // we can decide by comparing (2^23 s + 0.5)^2 to 2^46 m.
145 const d0 = (m << 16) -% s *% s;
146 const d1 = s -% d0;
147 const d2 = d1 +% s +% 1;
148 s += d1 >> 31;
149 s &= 0x007F_FFFF;
150 s |= top << 23;
151 const y: f32 = @bitCast(s);
152
153 // handle rounding modes and inexact exception:
154 // only (s+1)^2 == 2^16 m case is exact otherwise
155 // add a tiny value to cause the fenv effects.
156 if (d2 != 0) {
157 @branchHint(.likely);
158 var tiny: u32 = 0x0100_0000;
159 tiny |= (d1 ^ d2) & 0x8000_0000;
160 const t: f32 = @bitCast(tiny);
161 return y + t;
97162 }
98163
99 ix = (q >> 1) + 0x3f000000;
100 ix += m << 23;
101 return @bitCast(ix);
164 return y;
102165}
103166
104/// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
105/// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
106/// potentially some edge cases remaining that are not handled in the same way.
107167pub fn sqrt(x: f64) callconv(.c) f64 {
108 const tiny: f64 = 1.0e-300;
109 const sign: u32 = 0x80000000;
110 const u: u64 = @bitCast(x);
111
112 var ix0: u32 = @intCast(u >> 32);
113 var ix1: u32 = @intCast(u & 0xFFFFFFFF);
114
115 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
116 if (ix0 & 0x7FF00000 == 0x7FF00000) {
117 return x * x + x;
168 var ix: u64 = @bitCast(x);
169 var top = ix >> 52;
170
171 // special case handling.
172 if (top -% 0x001 >= 0x7FF - 0x001) {
173 @branchHint(.unlikely);
174 // x < 0x1p-1022 or inf or nan.
175 if (ix & 0x7FFF_FFFF_FFFF_FFFF == 0) return x;
176 if (ix == 0x7FF0_0000_0000_0000) return x;
177 if (ix > 0x7FF0_0000_0000_0000) return math.nan(f64);
178 // x is subnormal, normalize it.
179 ix = @bitCast(x * 0x1p52);
180 top = (ix >> 52) -% 52;
118181 }
119182
120 // sqrt(+-0) = +-0
121 if (x == 0.0) {
122 return x;
123 }
124 // sqrt(-ve) = nan
125 if (ix0 & sign != 0) {
126 return math.nan(f64);
183 // argument reduction:
184 // x = 4^e m; with integer e, and m in [1, 4)
185 // m: fixed point representation [2.62]
186 // 2^e is the exponent part of the result.
187 const even = (top & 1) != 0;
188 const m = if (even) (ix << 10) & 0x7FFF_FFFF_FFFF_FFFF else (ix << 11) | 0x8000_0000_0000_0000;
189 top = (top +% 0x3FF) >> 1;
190
191 // approximate r ~ 1/sqrt(m) and s ~ sqrt(m) when m in [1,4)
192 //
193 // initial estimate:
194 // 7bit table lookup (1bit exponent and 6bit significand).
195 //
196 // iterative approximation:
197 // using 2 goldschmidt iterations with 32bit int arithmetics
198 // and a final iteration with 64bit int arithmetics.
199 //
200 // details:
201 //
202 // the relative error (e = r0 sqrt(m)-1) of a linear estimate
203 // (r0 = a m + b) is |e| < 0.085955 ~ 0x1.6p-4 at best,
204 // a table lookup is faster and needs one less iteration
205 // 6 bit lookup table (128b) gives |e| < 0x1.f9p-8
206 // 7 bit lookup table (256b) gives |e| < 0x1.fdp-9
207 // for single and double prec 6bit is enough but for quad
208 // prec 7bit is needed (or modified iterations). to avoid
209 // one more iteration >=13bit table would be needed (16k).
210 //
211 // a newton-raphson iteration for r is
212 // w = r*r
213 // u = 3 - m*w
214 // r = r*u/2
215 // can use a goldschmidt iteration for s at the end or
216 // s = m*r
217 //
218 // first goldschmidt iteration is
219 // s = m*r
220 // u = 3 - s*r
221 // r = r*u/2
222 // s = s*u/2
223 // next goldschmidt iteration is
224 // u = 3 - s*r
225 // r = r*u/2
226 // s = s*u/2
227 // and at the end r is not computed only s.
228 //
229 // they use the same amount of operations and converge at the
230 // same quadratic rate, i.e. if
231 // r1 sqrt(m) - 1 = e, then
232 // r2 sqrt(m) - 1 = -3/2 e^2 - 1/2 e^3
233 // the advantage of goldschmidt is that the mul for s and r
234 // are independent (computed in parallel), however it is not
235 // "self synchronizing": it only uses the input m in the
236 // first iteration so rounding errors accumulate. at the end
237 // or when switching to larger precision arithmetics rounding
238 // errors dominate so the first iteration should be used.
239 //
240 // the fixed point representations are
241 // m: 2.30 r: 0.32, s: 2.30, d: 2.30, u: 2.30, three: 2.30
242 // and after switching to 64 bit
243 // m: 2.62 r: 0.64, s: 2.62, d: 2.62, u: 2.62, three: 2.62
244 const three: struct { u32, u64 } = .{
245 0xC000_0000,
246 0xC000_0000_0000_0000,
247 };
248 var r: struct { u32, u64 } = undefined;
249 var s: struct { u32, u64 } = undefined;
250 var d: struct { u32, u64 } = undefined;
251 var u: struct { u32, u64 } = undefined;
252 const i: usize = @intCast((ix >> 46) & 0x7F);
253 r[0] = @intCast(__rsqrt_tab[i]);
254 r[0] <<= 16;
255 // |r sqrt(m) - 1| < 0x1.fdp-9
256 s[0] = mul32(@intCast(m >> 32), r[0]);
257 // |s/sqrt(m) - 1| < 0x1.fdp-9
258 d[0] = mul32(s[0], r[0]);
259 u[0] = three[0] - d[0];
260 r[0] = mul32(r[0], u[0]) << 1;
261 // |r sqrt(m) - 1| < 0x1.7bp-16
262 s[0] = mul32(s[0], u[0]) << 1;
263 // |s/sqrt(m) - 1| < 0x1.7bp-16
264 d[0] = mul32(s[0], r[0]);
265 u[0] = three[0] - d[0];
266 r[0] = mul32(r[0], u[0]) << 1;
267 // |r sqrt(m) - 1| < 0x1.3704p-29 (measured worst-case)
268 r[1] = @intCast(r[0]);
269 r[1] <<= 32;
270 s[1] = mul64(m, r[1]);
271 d[1] = mul64(s[1], r[1]);
272 u[1] = three[1] - d[1];
273 s[1] = mul64(s[1], u[1]); // repr: 3.61
274 // -0x1p-57 < s - sqrt(m) < 0x1.8001p-61
275 s[1] = (s[1] - 2) >> 9; // repr: 12.52
276 // -0x1.09p-52 < s - sqrt(m) < -0x1.fffcp-63
277
278 // s < sqrt(m) < s + 0x1.09p-52
279 // compute nearest rounded result:
280 // the nearest result to 52 bits is either s or s+0x1p-52,
281 // we can decide by comparing (2^52 s + 0.5)^2 to 2^104 m.
282 const d0 = (m << 42) -% s[1] *% s[1];
283 const d1 = s[1] -% d0;
284 const d2 = d1 +% s[1] +% 1;
285 s[1] += d1 >> 63;
286 s[1] &= 0x000F_FFFF_FFFF_FFFF;
287 s[1] |= top << 52;
288 const y: f64 = @bitCast(s[1]);
289
290 // handle rounding modes and inexact exception:
291 // only (s+1)^2 == 2^42 m case is exact otherwise
292 // add a tiny value to cause the fenv effects.
293 if (d2 != 0) {
294 @branchHint(.likely);
295 var tiny: u64 = 0x0010_0000_0000_0000;
296 tiny |= (d1 ^ d2) & 0x8000_0000_0000_0000;
297 const t: f64 = @bitCast(tiny);
298 return y + t;
127299 }
128300
129 // normalize x
130 var m: i32 = @intCast(ix0 >> 20);
131 if (m == 0) {
132 // subnormal
133 while (ix0 == 0) {
134 m -= 21;
135 ix0 |= ix1 >> 11;
136 ix1 <<= 21;
137 }
138
139 // subnormal
140 var i: u32 = 0;
141 while (ix0 & 0x00100000 == 0) : (i += 1) {
142 ix0 <<= 1;
143 }
144 m -= @as(i32, @intCast(i)) - 1;
145 ix0 |= ix1 >> @intCast(32 - i);
146 ix1 <<= @intCast(i);
147 }
301 return y;
302}
148303
149 // unbias exponent
150 m -= 1023;
151 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
152 if (m & 1 != 0) {
153 ix0 += ix0 + (ix1 >> 31);
154 ix1 = ix1 +% ix1;
155 }
156 m >>= 1;
157
158 // sqrt(x) bit by bit
159 ix0 += ix0 + (ix1 >> 31);
160 ix1 = ix1 +% ix1;
161
162 var q: u32 = 0;
163 var q1: u32 = 0;
164 var s0: u32 = 0;
165 var s1: u32 = 0;
166 var r: u32 = 0x00200000;
167 var t: u32 = undefined;
168 var t1: u32 = undefined;
169
170 while (r != 0) {
171 t = s0 +% r;
172 if (t <= ix0) {
173 s0 = t + r;
174 ix0 -= t;
175 q += r;
176 }
177 ix0 = ix0 +% ix0 +% (ix1 >> 31);
178 ix1 = ix1 +% ix1;
179 r >>= 1;
304pub fn __sqrtx(x: f80) callconv(.c) f80 {
305 var ix: u80 = @bitCast(x);
306 var top = ix >> 64;
307
308 // special case handling.
309 if (top -% 0x0001 >= 0x7FFF - 0x0001) {
310 @branchHint(.unlikely);
311 // x < 0x1p-16382 or inf or nan.
312 if (ix & 0x7FFF_FFFF_FFFF_FFFF_FFFF == 0) return x;
313 if (ix == 0x7FFF_8000_0000_0000_0000) return x;
314 if (ix > 0x7FFF_8000_0000_0000_0000) return math.nan(f80);
315 // x is subnormal, normalize it.
316 ix = @bitCast(x * 0x1p63);
317 top = (ix >> 64) -% 63;
180318 }
181319
182 r = sign;
183 while (r != 0) {
184 t1 = s1 +% r;
185 t = s0;
186 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
187 s1 = t1 +% r;
188 if (t1 & sign == sign and s1 & sign == 0) {
189 s0 += 1;
190 }
191 ix0 -= t;
192 if (ix1 < t1) {
193 ix0 -= 1;
194 }
195 ix1 = ix1 -% t1;
196 q1 += r;
197 }
198 ix0 = ix0 +% ix0 +% (ix1 >> 31);
199 ix1 = ix1 +% ix1;
200 r >>= 1;
320 // argument reduction:
321 // x = 4^e m; with integer e, and m in [1, 4)
322 // m: fixed point representation [2.78]
323 // 2^e is the exponent part of the result.
324 const even = (top & 1) != 0;
325 const m = if (even) (ix << 15) & 0x7FFF_FFFF_FFFF_FFFF_FFFF else ix << 16;
326 top = (top +% 0x3FFF) >> 1;
327
328 // approximate r ~ 1/sqrt(m) and s ~ sqrt(m) when m in [1,4)
329 // the fixed point representations are
330 // m: 2.30 r: 0.32, s: 2.30, d: 2.30, u: 2.30, three: 2.30
331 // and after switching to 64 bit
332 // m: 2.62 r: 0.64, s: 2.62, d: 2.62, u: 2.62, three: 2.62
333 // and after switching to 80 bit
334 // m: 2.78 r: 0.80, s: 2.78, d: 2.78, u: 2.78, three: 2.78
335 const three: struct { u32, u64, u80 } = .{
336 0xC000_0000,
337 0xC000_0000_0000_0000,
338 0xC000_0000_0000_0000_0000,
339 };
340 var r: struct { u32, u64, u80 } = undefined;
341 var s: struct { u32, u64, u80 } = undefined;
342 var d: struct { u32, u64, u80 } = undefined;
343 var u: struct { u32, u64, u80 } = undefined;
344 var i: usize = @intCast((ix >> 57) & 0x3F);
345 if (even) i += 64;
346 r[0] = @intCast(__rsqrt_tab[i]);
347 r[0] <<= 16;
348 // |r sqrt(m) - 1| < 0x1p-8
349 s[0] = mul32(@intCast(m >> 48), r[0]);
350 d[0] = mul32(s[0], r[0]);
351 u[0] = three[0] - d[0];
352 r[0] = mul32(u[0], r[0]) << 1;
353 // |r sqrt(m) - 1| < 0x1.7bp-16, switch to 64bit
354 r[1] = @intCast(r[0]);
355 r[1] <<= 32;
356 s[1] = mul64(@intCast(m >> 16), r[1]);
357 d[1] = mul64(s[1], r[1]);
358 u[1] = three[1] - d[1];
359 r[1] = mul64(u[1], r[1]) << 1;
360 // |r sqrt(m) - 1| < 0x1.a5p-31
361 s[1] = mul64(u[1], s[1]) << 1;
362 d[1] = mul64(s[1], r[1]);
363 u[1] = three[1] - d[1];
364 r[1] = mul64(u[1], r[1]) << 1;
365 // |r sqrt(m) - 1| < 0x1.c001p-59, switch to 80bit
366 r[2] = @intCast(r[1]);
367 r[2] <<= 16;
368 s[2] = mul80(m, r[2]);
369 d[2] = mul80(s[2], r[2]);
370 u[2] = three[2] - d[2];
371 s[2] = mul80(u[2], s[2]); // repr: 3.77
372 s[2] = (s[2] - 4) >> 14; // repr: 17.63
373 // s < sqrt(m) < s + 1 ULP + tiny
374
375 // compute nearest rounded result:
376 // the nearest result to 63 bits is either s or s+0x1p-63,
377 // we can decide by comparing (2^63 s + 0.5)^2 to 2^126 m
378 const d0 = (m << 48) -% mul80_tail(s[2], s[2]);
379 const d1 = s[2] -% d0;
380 const d2 = d1 +% s[2] +% 1;
381 s[2] += d1 >> 79;
382 s[2] &= 0x0000_7FFF_FFFF_FFFF_FFFF;
383 s[2] |= 0x0000_8000_0000_0000_0000;
384 s[2] |= top << 64;
385 const y: f80 = @bitCast(s[2]);
386
387 // handle rounding modes and inexact exception:
388 // only (s+1)^2 == 2^48 m case is exact otherwise
389 // add a tiny value to cause the fenv effects.
390 if (d2 != 0) {
391 @branchHint(.likely);
392 var tiny: u80 = 0x0001_8000_0000_0000_0000;
393 tiny |= (d1 ^ d2) & 0x8000_0000_0000_0000_0000;
394 const t: f80 = @bitCast(tiny);
395 return y + t;
201396 }
202397
203 // rounding direction
204 if (ix0 | ix1 != 0) {
205 var z = 1.0 - tiny; // raise inexact
206 if (z >= 1.0) {
207 z = 1.0 + tiny;
208 if (q1 == 0xFFFFFFFF) {
209 q1 = 0;
210 q += 1;
211 } else if (z > 1.0) {
212 if (q1 == 0xFFFFFFFE) {
213 q += 1;
214 }
215 q1 += 2;
216 } else {
217 q1 += q1 & 1;
218 }
219 }
220 }
398 return y;
399}
221400
222 ix0 = (q >> 1) + 0x3FE00000;
223 ix1 = q1 >> 1;
224 if (q & 1 != 0) {
225 ix1 |= 0x80000000;
401pub fn sqrtq(x: f128) callconv(.c) f128 {
402 var ix: u128 = @bitCast(x);
403 var top = ix >> 112;
404
405 // special case handling.
406 if (top -% 0x0001 >= 0x7FFF - 0x0001) {
407 @branchHint(.unlikely);
408 // x < 0x1p-16382 or inf or nan.
409 if (ix & 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF == 0) return x;
410 if (ix == 0x7FFF_0000_0000_0000_0000_0000_0000_0000) return x;
411 if (ix > 0x7FFF_0000_0000_0000_0000_0000_0000_0000) return math.nan(f128);
412 // x is subnormal, normalize it.
413 ix = @bitCast(x * 0x1p112);
414 top = (ix >> 112) -% 112;
226415 }
227416
228 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
229 // behaviour at least.
230 var iix0: i32 = @intCast(ix0);
231 iix0 = iix0 +% (m << 20);
232
233 const uz = (@as(u64, @intCast(iix0)) << 32) | ix1;
234 return @bitCast(uz);
235}
236
237pub fn __sqrtx(x: f80) callconv(.c) f80 {
238 // TODO: more efficient implementation
239 return @floatCast(sqrtq(x));
240}
417 // argument reduction:
418 // x = 4^e m; with integer e, and m in [1, 4)
419 // m: fixed point representation [2.126]
420 // 2^e is the exponent part of the result.
421 const even = (top & 1) != 0;
422 const m = if (even) (ix << 14) & 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF else (ix << 15) | 0x8000_0000_0000_0000_0000_0000_0000_0000;
423 top = (top +% 0x3FFF) >> 1;
424
425 // approximate r ~ 1/sqrt(m) and s ~ sqrt(m) when m in [1,4)
426 // the fixed point representations are
427 // m: 2.30 r: 0.32, s: 2.30, d: 2.30, u: 2.30, three: 2.30
428 // and after switching to 64 bit
429 // m: 2.62 r: 0.64, s: 2.62, d: 2.62, u: 2.62, three: 2.62
430 // and after switching to 128 bit
431 // m: 2.126 r: 0.128, s: 2.126, d: 2.126, u: 2.126, three: 2.126
432 const three: struct { u32, u64, u128 } = .{
433 0xC000_0000,
434 0xC000_0000_0000_0000,
435 0xC000_0000_0000_0000_0000_0000_0000_0000,
436 };
437 var r: struct { u32, u64, u128 } = undefined;
438 var s: struct { u32, u64, u128 } = undefined;
439 var d: struct { u32, u64, u128 } = undefined;
440 var u: struct { u32, u64, u128 } = undefined;
441 const i: usize = @intCast((ix >> 106) & 0x7F);
442 r[0] = @intCast(__rsqrt_tab[i]);
443 r[0] <<= 16;
444 // |r sqrt(m) - 1| < 0x1p-8
445 s[0] = mul32(@intCast(m >> 96), r[0]);
446 d[0] = mul32(s[0], r[0]);
447 u[0] = three[0] - d[0];
448 r[0] = mul32(u[0], r[0]) << 1;
449 // |r sqrt(m) - 1| < 0x1.7bp-16, switch to 64bit
450 r[1] = @intCast(r[0]);
451 r[1] <<= 32;
452 s[1] = mul64(@intCast(m >> 64), r[1]);
453 d[1] = mul64(s[1], r[1]);
454 u[1] = three[1] - d[1];
455 r[1] = mul64(u[1], r[1]) << 1;
456 // |r sqrt(m) - 1| < 0x1.a5p-31
457 s[1] = mul64(u[1], s[1]) << 1;
458 d[1] = mul64(s[1], r[1]);
459 u[1] = three[1] - d[1];
460 r[1] = mul64(u[1], r[1]) << 1;
461 // |r sqrt(m) - 1| < 0x1.c001p-59, switch to 128bit
462 r[2] = @intCast(r[1]);
463 r[2] <<= 64;
464 s[2] = mul128(m, r[2]);
465 d[2] = mul128(s[2], r[2]);
466 u[2] = three[2] - d[2];
467 s[2] = mul128(u[2], s[2]); // repr: 3.125
468 // -0x1p-116 < s - sqrt(m) < 0x3.8001p-125
469 s[2] = (s[2] - 4) >> 13; // repr: 16.122
470 // s < sqrt(m) < s + 1 ULP + tiny
471
472 // compute nearest rounded result:
473 // the nearest result to 122 bits is either s or s+0x1p-122,
474 // we can decide by comparing (2^122 s + 0.5)^2 to 2^244 m
475 const d0 = (m << 98) -% s[2] *% s[2];
476 const d1 = s[2] -% d0;
477 const d2 = d1 +% s[2] +% 1;
478 s[2] += d1 >> 127;
479 s[2] &= 0x0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF;
480 s[2] |= top << 112;
481 const y: f128 = @bitCast(s[2]);
482
483 // handle rounding modes and inexact exception:
484 // only (s+1)^2 == 2^98 m case is exact otherwise
485 // add a tiny value to cause the fenv effects.
486 if (d2 != 0) {
487 @branchHint(.likely);
488 var tiny: u128 = 0x0001_0000_0000_0000_0000_0000_0000_0000;
489 tiny |= (d1 ^ d2) & 0x8000_0000_0000_0000_0000_0000_0000_0000;
490 const t: f128 = @bitCast(tiny);
491 return y + t;
492 }
241493
242pub fn sqrtq(x: f128) callconv(.c) f128 {
243 // TODO: more correct implementation
244 return sqrt(@floatCast(x));
494 return y;
245495}
246496
247497fn _Qp_sqrt(c: *f128, a: *f128) callconv(.c) void {
......@@ -259,60 +509,242 @@ pub fn sqrtl(x: c_longdouble) callconv(.c) c_longdouble {
259509 }
260510}
261511
262test "sqrtf" {
263 const V = [_]f32{
264 0.0,
265 4.089288054930154,
266 7.538757127071935,
267 8.97780793672623,
268 5.304443821913729,
269 5.682408965311888,
270 0.5846878579110049,
271 3.650338664297043,
272 0.3178091951800732,
273 7.1505232436382835,
274 3.6589165881946464,
275 };
512const __rsqrt_tab: [128]u16 = .{
513 0xB451, 0xB2F0, 0xB196, 0xB044, 0xAEF9, 0xADB6, 0xAC79, 0xAB43,
514 0xAA14, 0xA8EB, 0xA7C8, 0xA6AA, 0xA592, 0xA480, 0xA373, 0xA26B,
515 0xA168, 0xA06A, 0x9F70, 0x9E7B, 0x9D8A, 0x9C9D, 0x9BB5, 0x9AD1,
516 0x99F0, 0x9913, 0x983A, 0x9765, 0x9693, 0x95C4, 0x94F8, 0x9430,
517 0x936B, 0x92A9, 0x91EA, 0x912E, 0x9075, 0x8FBE, 0x8F0A, 0x8E59,
518 0x8DAA, 0x8CFE, 0x8C54, 0x8BAC, 0x8B07, 0x8A64, 0x89C4, 0x8925,
519 0x8889, 0x87EE, 0x8756, 0x86C0, 0x862B, 0x8599, 0x8508, 0x8479,
520 0x83EC, 0x8361, 0x82D8, 0x8250, 0x81C9, 0x8145, 0x80C2, 0x8040,
521 0xFF02, 0xFD0E, 0xFB25, 0xF947, 0xF773, 0xF5AA, 0xF3EA, 0xF234,
522 0xF087, 0xEEE3, 0xED47, 0xEBB3, 0xEA27, 0xE8A3, 0xE727, 0xE5B2,
523 0xE443, 0xE2DC, 0xE17A, 0xE020, 0xDECB, 0xDD7D, 0xDC34, 0xDAF1,
524 0xD9B3, 0xD87B, 0xD748, 0xD61A, 0xD4F1, 0xD3CD, 0xD2AD, 0xD192,
525 0xD07B, 0xCF69, 0xCE5B, 0xCD51, 0xCC4A, 0xCB48, 0xCA4A, 0xC94F,
526 0xC858, 0xC764, 0xC674, 0xC587, 0xC49D, 0xC3B7, 0xC2D4, 0xC1F4,
527 0xC116, 0xC03C, 0xBF65, 0xBE90, 0xBDBE, 0xBCEF, 0xBC23, 0xBB59,
528 0xBA91, 0xB9CC, 0xB90A, 0xB84A, 0xB78C, 0xB6D0, 0xB617, 0xB560,
529};
530
531inline fn mul16(a: u16, b: u16) u16 {
532 return @intCast(@as(u32, @intCast(a)) * @as(u32, @intCast(b)) >> 16);
533}
534
535inline fn mul32(a: u32, b: u32) u32 {
536 return @intCast(@as(u64, @intCast(a)) * @as(u64, @intCast(b)) >> 32);
537}
538
539inline fn mul64(a: u64, b: u64) u64 {
540 return @intCast(@as(u128, @intCast(a)) * @as(u128, @intCast(b)) >> 64);
541}
542
543inline fn mul80(a: u80, b: u80) u80 {
544 const ahi = a >> 40;
545 const alo = a & 0xFF_FFFF_FFFF;
546 const bhi = b >> 40;
547 const blo = b & 0xFF_FFFF_FFFF;
548 return ahi * bhi + (ahi * blo >> 40) + (alo * bhi >> 40);
549}
550
551inline fn mul128(a: u128, b: u128) u128 {
552 const ahi = a >> 64;
553 const alo = a & 0xFFFF_FFFF_FFFF_FFFF;
554 const bhi = b >> 64;
555 const blo = b & 0xFFFF_FFFF_FFFF_FFFF;
556 return ahi * bhi + (ahi * blo >> 64) + (alo * bhi >> 64);
557}
558
559inline fn mul80_tail(a: u80, b: u80) u80 {
560 const ahi = a >> 40;
561 const alo = a & 0xFF_FFFF_FFFF;
562 const bhi = b >> 40;
563 const blo = b & 0xFF_FFFF_FFFF;
564 return alo * blo +% ((ahi * blo) << 40) +% ((alo * bhi) << 40);
565}
276566
277 // Note that @sqrt will either generate the sqrt opcode (if supported by the
278 // target ISA) or a call to `sqrtf` otherwise.
279 for (V) |val|
280 try std.testing.expectEqual(@sqrt(val), sqrtf(val));
567test "__sqrth" {
568 // sqrt(±0) is ±0
569 try std.testing.expectEqual(__sqrth(0x0.0p0), 0x0.0p0);
570 try std.testing.expectEqual(__sqrth(-0x0.0p0), -0x0.0p0);
571 // sqrt(+max) is finite
572 try std.testing.expectEqual(__sqrth(0x1.FFCp15), 0x1.FFCp7);
573 // sqrt(4)=2
574 try std.testing.expectEqual(__sqrth(0x1p2), 0x1p1);
575 // sqrt(x) for x=1, 1±ulp
576 try std.testing.expectEqual(__sqrth(0x1p0), 0x1p0);
577 try std.testing.expectEqual(__sqrth(0x1.004p0), 0x1p0);
578 try std.testing.expectEqual(__sqrth(0x1.FF8p-1), 0x1.FFCp-1);
579 // sqrt(+min) is non-zero
580 try std.testing.expectEqual(__sqrth(0x1p-14), 0x1p-7);
581 // sqrt(min subnormal) is non-zero
582 try std.testing.expectEqual(__sqrth(0x0.004p-14), 0x1p-12);
583 // sqrt(inf) is inf
584 try std.testing.expect(math.isInf(__sqrth(math.inf(f16))));
585 // sqrt(nan) is nan
586 try std.testing.expect(math.isNan(__sqrth(math.nan(f16))));
587 // sqrt(-ve) is nan
588 try std.testing.expect(math.isNan(__sqrth(-0x1p-14)));
589 try std.testing.expect(math.isNan(__sqrth(-0x1p+0)));
590 try std.testing.expect(math.isNan(__sqrth(-math.inf(f16))));
591 // random arguments
592 try std.testing.expectEqual(__sqrth(0x1.1p14), 0x1.08p7);
593 try std.testing.expectEqual(__sqrth(0x1.C9p-12), 0x1.56p-6);
594 try std.testing.expectEqual(__sqrth(0x1.CE8p-7), 0x1.E68p-4);
595 try std.testing.expectEqual(__sqrth(0x1.134p-7), 0x1.778p-4);
596 try std.testing.expectEqual(__sqrth(0x1.E9Cp-10), 0x1.62p-5);
597 try std.testing.expectEqual(__sqrth(0x1.3Dp9), 0x1.92Cp4);
598 try std.testing.expectEqual(__sqrth(0x1.AA4p8), 0x1.4A4p4);
599 try std.testing.expectEqual(__sqrth(0x1.8A8p4), 0x1.3DCp2);
600 try std.testing.expectEqual(__sqrth(0x1.8Fp-7), 0x1.C4p-4);
601 try std.testing.expectEqual(__sqrth(0x1.584p-11), 0x1.A3Cp-6);
281602}
282603
283test "sqrtf special" {
284 try std.testing.expect(math.isPositiveInf(sqrtf(math.inf(f32))));
285 try std.testing.expect(sqrtf(0.0) == 0.0);
286 try std.testing.expect(sqrtf(-0.0) == -0.0);
287 try std.testing.expect(math.isNan(sqrtf(-1.0)));
604test "sqrtf" {
605 // sqrt(±0) is ±0
606 try std.testing.expectEqual(sqrtf(0x0.0p0), 0x0.0p0);
607 try std.testing.expectEqual(sqrtf(-0x0.0p0), -0x0.0p0);
608 // sqrt(+max) is finite
609 try std.testing.expectEqual(sqrtf(0x1.FFFFFEp127), 0x1.FFFFFEp63);
610 // sqrt(4)=2
611 try std.testing.expectEqual(sqrtf(0x1p2), 0x1p1);
612 // sqrt(x) for x=1, 1±ulp
613 try std.testing.expectEqual(sqrtf(0x1p0), 0x1p0);
614 try std.testing.expectEqual(sqrtf(0x1.000002p0), 0x1p0);
615 try std.testing.expectEqual(sqrtf(0x1.FFFFFEp-1), 0x1.FFFFFEp-1);
616 // sqrt(+min) is non-zero
617 try std.testing.expectEqual(sqrtf(0x1p-126), 0x1p-63);
618 // sqrt(min subnormal) is non-zero
619 try std.testing.expectEqual(sqrtf(0x0.000002p-126), 0x1.6a09e6p-75);
620 // sqrt(inf) is inf
621 try std.testing.expect(math.isInf(sqrtf(math.inf(f32))));
622 // sqrt(nan) is nan
288623 try std.testing.expect(math.isNan(sqrtf(math.nan(f32))));
624 // sqrt(-ve) is nan
625 try std.testing.expect(math.isNan(sqrtf(-0x1p-149)));
626 try std.testing.expect(math.isNan(sqrtf(-0x1p0)));
627 try std.testing.expect(math.isNan(sqrtf(-math.inf(f32))));
628 // random arguments
629 try std.testing.expectEqual(sqrtf(0x1.4DD57Ep77), 0x1.9D6DA8p38);
630 try std.testing.expectEqual(sqrtf(0x1.871848p102), 0x1.3C6AFAp51);
631 try std.testing.expectEqual(sqrtf(0x1.A1D748p-112), 0x1.470EFCp-56);
632 try std.testing.expectEqual(sqrtf(0x1.E626C2p18), 0x1.60C80Ep9);
633 try std.testing.expectEqual(sqrtf(0x1.E80E66p-29), 0x1.F3E282p-15);
634 try std.testing.expectEqual(sqrtf(0x1.B47204p89), 0x1.D8B732p44);
635 try std.testing.expectEqual(sqrtf(0x1.77F45p15), 0x1.B6BC3Ap7);
636 try std.testing.expectEqual(sqrtf(0x1.AD5F5p-48), 0x1.4B8A72p-24);
637 try std.testing.expectEqual(sqrtf(0x1.91A39p-76), 0x1.40A7A8p-38);
638 try std.testing.expectEqual(sqrtf(0x1.DAE088p79), 0x1.ED16DCp39);
289639}
290640
291641test "sqrt" {
292 const V = [_]f64{
293 0.0,
294 4.089288054930154,
295 7.538757127071935,
296 8.97780793672623,
297 5.304443821913729,
298 5.682408965311888,
299 0.5846878579110049,
300 3.650338664297043,
301 0.3178091951800732,
302 7.1505232436382835,
303 3.6589165881946464,
304 };
642 // sqrt(±0) is ±0
643 try std.testing.expectEqual(sqrt(0x0.0p0), 0x0.0p0);
644 try std.testing.expectEqual(sqrt(-0x0.0p0), -0x0.0p0);
645 // sqrt(+max) is finite
646 try std.testing.expectEqual(sqrt(math.floatMax(f64)), 0x1.FFFFFFFFFFFFFp511);
647 // sqrt(4)=2
648 try std.testing.expectEqual(sqrt(0x1p2), 0x1p1);
649 // sqrt(x) for x=1, 1±ulp
650 try std.testing.expectEqual(sqrt(0x1p0), 0x1p0);
651 try std.testing.expectEqual(sqrt(0x1p0 + math.floatEps(f64)), 0x1p0);
652 try std.testing.expectEqual(sqrt(0x1p0 - math.floatEps(f64)), 0x1.FFFFFFFFFFFFFp-1);
653 // sqrt(+min) is non-zero
654 try std.testing.expectEqual(sqrt(math.floatMin(f64)), 0x1p-511);
655 // sqrt(min subnormal) is non-zero
656 try std.testing.expectEqual(sqrt(math.floatTrueMin(f64)), 0x1p-537);
657 // sqrt(inf) is inf
658 try std.testing.expect(math.isInf(sqrt(math.inf(f64))));
659 // sqrt(nan) is nan
660 try std.testing.expect(math.isNan(sqrt(math.nan(f64))));
661 // sqrt(-ve) is nan
662 try std.testing.expect(math.isNan(sqrt(-0x1p-1074)));
663 try std.testing.expect(math.isNan(sqrt(-0x1p0)));
664 try std.testing.expect(math.isNan(sqrt(-math.inf(f64))));
665 // random arguments
666 try std.testing.expectEqual(sqrt(0x1.27D3510D4789Bp471), 0x1.852E97E58CFB7p235);
667 try std.testing.expectEqual(sqrt(0x1.8C4FCD5A07846p791), 0x1.C27504E56D938p395);
668 try std.testing.expectEqual(sqrt(0x1.B1B69324F96E7p-137), 0x1.D73BD0414D8BFp-69);
669 try std.testing.expectEqual(sqrt(0x1.1CBD179A811FEp278), 0x1.0DFCB9A114A61p139);
670 try std.testing.expectEqual(sqrt(0x1.1D0C7EFB04A56p917), 0x1.7E0708A25DDCDp458);
671 try std.testing.expectEqual(sqrt(0x1.21B355DA8C94Bp-249), 0x1.8121CBE2608E3p-125);
672 try std.testing.expectEqual(sqrt(0x1.63024D4C5E987p487), 0x1.AA56AEA589DCDp243);
673 try std.testing.expectEqual(sqrt(0x1.45AC3BE941F6Ep339), 0x1.9857F3F453E2Dp169);
674 try std.testing.expectEqual(sqrt(0x1.3B719C733AA24p267), 0x1.91E12E3AC8F71p133);
675 try std.testing.expectEqual(sqrt(0x1.0B150433A2275p357), 0x1.71CAB87F8277Cp178);
676}
305677
306 // Note that @sqrt will either generate the sqrt opcode (if supported by the
307 // target ISA) or a call to `sqrtf` otherwise.
308 for (V) |val|
309 try std.testing.expectEqual(@sqrt(val), sqrt(val));
678test "__sqrtx" {
679 // sqrt(±0) is ±0
680 try std.testing.expectEqual(__sqrtx(0x0.0p0), 0x0.0p0);
681 try std.testing.expectEqual(__sqrtx(-0x0.0p0), -0x0.0p0);
682 // sqrt(+max) is finite
683 try std.testing.expectEqual(__sqrtx(math.floatMax(f80)), 0x1.FFFFFFFFFFFFFFFEp8191);
684 // sqrt(4)=2
685 try std.testing.expectEqual(__sqrtx(0x1p2), 0x1p1);
686 // sqrt(x) for x=1, 1±ulp
687 try std.testing.expectEqual(__sqrtx(0x1p0), 0x1p0);
688 try std.testing.expectEqual(__sqrtx(0x1p0 + math.floatEps(f80)), 0x1p0);
689 try std.testing.expectEqual(__sqrtx(0x1p0 - math.floatEps(f80)), 0x1.FFFFFFFFFFFFFFFEp-1);
690 // sqrt(+min) is non-zero
691 try std.testing.expectEqual(__sqrtx(math.floatMin(f80)), 0x1p-8191);
692 // sqrt(min subnormal) is non-zero
693 try std.testing.expectEqual(__sqrtx(math.floatTrueMin(f80)), 0x1.6A09E667F3BCC908p-8223);
694 // sqrt(inf) is inf
695 try std.testing.expect(math.isInf(__sqrtx(math.inf(f80))));
696 // sqrt(nan) is nan
697 try std.testing.expect(math.isNan(__sqrtx(math.nan(f80))));
698 // sqrt(-ve) is nan
699 try std.testing.expect(math.isNan(__sqrtx(-0x1p-16442)));
700 try std.testing.expect(math.isNan(__sqrtx(-0x1p0)));
701 try std.testing.expect(math.isNan(__sqrtx(-math.inf(f80))));
702 // random arguments
703 try std.testing.expectEqual(__sqrtx(0x1.087F3953486918A4p15482), 0x1.0436BBE03D02F32p7741);
704 try std.testing.expectEqual(__sqrtx(0x1.530CF9E2AE84D8Fp-6330), 0x1.269CFEF51933BE58p-3165);
705 try std.testing.expectEqual(__sqrtx(0x1.3F971515EADD574Ap5713), 0x1.9483232AB780B006p2856);
706 try std.testing.expectEqual(__sqrtx(0x1.4CC0DC7379222954p864), 0x1.23DD4D0A4758C2Cp432);
707 try std.testing.expectEqual(__sqrtx(0x1.920E5649559A839Ep-3181), 0x1.C5B5BC0F98DD83D2p-1591);
708 try std.testing.expectEqual(__sqrtx(0x1.2E59726F87CD1746p-629), 0x1.8973327E95CB350Cp-315);
709 try std.testing.expectEqual(__sqrtx(0x1.D3A16391F57B4D64p-9034), 0x1.59FF08B7DEEF5DB2p-4517);
710 try std.testing.expectEqual(__sqrtx(0x1.E7053D8DAA49BCEEp-11411), 0x1.F35AA3EA5E18E344p-5706);
711 try std.testing.expectEqual(__sqrtx(0x1.797ED0B05DD4A984p7521), 0x1.B7A22E40C6A7867Ap3760);
712 try std.testing.expectEqual(__sqrtx(0x1.FC50806445C7226Ap15371), 0x1.FE2766142653F5BEp7685);
310713}
311714
312test "sqrt special" {
313 try std.testing.expect(math.isPositiveInf(sqrt(math.inf(f64))));
314 try std.testing.expect(sqrt(0.0) == 0.0);
315 try std.testing.expect(sqrt(-0.0) == -0.0);
316 try std.testing.expect(math.isNan(sqrt(-1.0)));
317 try std.testing.expect(math.isNan(sqrt(math.nan(f64))));
715test "sqrtq" {
716 // sqrt(±0) is ±0
717 try std.testing.expectEqual(sqrtq(0x0.0p0), 0x0.0p0);
718 try std.testing.expectEqual(sqrtq(-0x0.0p0), -0x0.0p0);
719 // sqrt(+max) is finite
720 try std.testing.expectEqual(sqrtq(math.floatMax(f128)), 0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp8191);
721 // sqrt(4)=2
722 try std.testing.expectEqual(sqrtq(0x1p2), 0x1p1);
723 // sqrt(x) for x=1, 1±ulp
724 try std.testing.expectEqual(sqrtq(0x1p0), 0x1p0);
725 try std.testing.expectEqual(sqrtq(0x1p0 + math.floatEps(f128)), 0x1p0);
726 try std.testing.expectEqual(sqrtq(0x1p0 - math.floatEps(f128)), 0x1.FFFFFFFFFFFFFFFFFFFFFFFFFFFFp-1);
727 // sqrt(+min) is non-zero
728 try std.testing.expectEqual(sqrtq(math.floatMin(f128)), 0x1p-8191);
729 // sqrt(min subnormal) is non-zero
730 try std.testing.expectEqual(sqrtq(math.floatTrueMin(f128)), 0x1p-8247);
731 // sqrt(inf) is inf
732 try std.testing.expect(math.isInf(sqrtq(math.inf(f128))));
733 // sqrt(nan) is nan
734 try std.testing.expect(math.isNan(sqrtq(math.nan(f128))));
735 // sqrt(-ve) is nan
736 try std.testing.expect(math.isNan(sqrtq(-0x1p-16442)));
737 try std.testing.expect(math.isNan(sqrtq(-0x1p0)));
738 try std.testing.expect(math.isNan(sqrtq(-math.inf(f128))));
739 // random arguments
740 try std.testing.expectEqual(sqrtq(0x1.B6942D29A331751600C9F3AF7E5Fp3363), 0x1.D9DE9AFEF0F2D25586A50CA39D4Dp1681);
741 try std.testing.expectEqual(sqrtq(0x1.5E65C405F84D471A8070ADD7A42Dp11765), 0x1.A78F7F9452B4D9EC2403C81D9D42p5882);
742 try std.testing.expectEqual(sqrtq(0x1.B42334D68F8016D8AE6F5E22B044p-5624), 0x1.4E247A7F2FF2A325E9377BB09C8p-2812);
743 try std.testing.expectEqual(sqrtq(0x1.E61715047F80F2E0B9382B38E06Bp10062), 0x1.60C25D9DFDC0116B78EF5AFDE0E9p5031);
744 try std.testing.expectEqual(sqrtq(0x1.2ED0B53B494CB55A7B04E653D40Ep-1026), 0x1.166CE78D658D2453D700B04C5748p-513);
745 try std.testing.expectEqual(sqrtq(0x1.1BA756B9790E78A4E6F0B083AA89p1835), 0x1.7D1767EA3303DB7A46940033988p917);
746 try std.testing.expectEqual(sqrtq(0x1.5B6C574319C1120335C8E1609704p4512), 0x1.2A3A8A415BB1648C548FBA2A4182p2256);
747 try std.testing.expectEqual(sqrtq(0x1.FF91E8CDEE1552A2B74E77B602Ep14953), 0x1.FFC8F171267D4FE75CBE7AB4D851p7476);
748 try std.testing.expectEqual(sqrtq(0x1.9B1837CFC629A1B6B1BB97099E7Dp2892), 0x1.4468511B909EAF8641BD59105A6Bp1446);
749 try std.testing.expectEqual(sqrtq(0x1.0E2115475E64A92340914E7F7B37p-13951), 0x1.73E536F82F414134012F55BA5368p-6976);
318750}