authorgravatar for lewis.gaul@gmail.comLewis Gaul <lewis.gaul@gmail.com> 2021-10-26 23:57:58+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-10-26 18:57:58-04:00
logf890de62948a0134a8d703dd2f1ee37701ec4c00
tree5151dadec62428299befc0d21f3f1464c4402d0a
parentad5b90ab1092cb50b38e4a1e6bbed463a38ef7cf
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Fix bug in exp2() (#9999)

* Fix bug in exp2_64 to handle negative values (bad translation from C) * Apply fix to exp2_32() as well, and modify comment on musl behaviour * Use +%= instead of @addWithOverflow()

1 files changed, 21 insertions(+), 11 deletions(-)

lib/std/math/exp2.zig+21-11
......@@ -78,13 +78,15 @@ fn exp2_32(x: f32) f32 {
7878 return 1.0 + x;
7979 }
8080
81 // NOTE: musl relies on unsafe behaviours which are replicated below
82 // (addition/bit-shift overflow). Appears that this produces the
83 // intended result but should confirm how GCC/Clang handle this to ensure.
84
8185 var uf = x + redux;
8286 var i_0 = @bitCast(u32, uf);
83 i_0 += tblsiz / 2;
87 i_0 +%= tblsiz / 2;
8488
8589 const k = i_0 / tblsiz;
86 // NOTE: musl relies on undefined overflow shift behaviour. Appears that this produces the
87 // intended result but should confirm how GCC/Clang handle this to ensure.
8890 const uk = @bitCast(f64, @as(u64, 0x3FF + k) << 52);
8991 i_0 &= tblsiz - 1;
9092 uf -= redux;
......@@ -357,7 +359,7 @@ const exp2dt = [_]f64{
357359};
358360
359361fn exp2_64(x: f64) f64 {
360 const tblsiz = @intCast(u32, exp2dt.len / 2);
362 const tblsiz: u32 = @intCast(u32, exp2dt.len / 2);
361363 const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz);
362364 const P1: f64 = 0x1.62e42fefa39efp-1;
363365 const P2: f64 = 0x1.ebfbdff82c575p-3;
......@@ -400,22 +402,27 @@ fn exp2_64(x: f64) f64 {
400402 return 1.0 + x;
401403 }
402404
405 // NOTE: musl relies on unsafe behaviours which are replicated below
406 // (addition overflow, division truncation, casting). Appears that this
407 // produces the intended result but should confirm how GCC/Clang handle this
408 // to ensure.
409
403410 // reduce x
404 var uf = x + redux;
411 var uf: f64 = x + redux;
405412 // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here
406 var i_0 = @truncate(u32, @bitCast(u64, uf));
407 i_0 += tblsiz / 2;
413 var i_0: u32 = @truncate(u32, @bitCast(u64, uf));
414 i_0 +%= tblsiz / 2;
408415
409416 const k: u32 = i_0 / tblsiz * tblsiz;
410 const ik = @bitCast(i32, k / tblsiz);
417 const ik: i32 = @divTrunc(@bitCast(i32, k), tblsiz);
411418 i_0 %= tblsiz;
412419 uf -= redux;
413420
414421 // r = exp2(y) = exp2t[i_0] * p(z - eps[i])
415 var z = x - uf;
416 const t = exp2dt[@intCast(usize, 2 * i_0)];
422 var z: f64 = x - uf;
423 const t: f64 = exp2dt[@intCast(usize, 2 * i_0)];
417424 z -= exp2dt[@intCast(usize, 2 * i_0 + 1)];
418 const r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5))));
425 const r: f64 = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5))));
419426
420427 return math.scalbn(r, ik);
421428}
......@@ -433,6 +440,7 @@ test "math.exp2_32" {
433440 try expect(math.approxEqAbs(f32, exp2_32(0.8923), 1.856133, epsilon));
434441 try expect(math.approxEqAbs(f32, exp2_32(1.5), 2.828427, epsilon));
435442 try expect(math.approxEqAbs(f32, exp2_32(37.45), 187747237888, epsilon));
443 try expect(math.approxEqAbs(f32, exp2_32(-1), 0.5, epsilon));
436444}
437445
438446test "math.exp2_64" {
......@@ -442,6 +450,8 @@ test "math.exp2_64" {
442450 try expect(math.approxEqAbs(f64, exp2_64(0.2), 1.148698, epsilon));
443451 try expect(math.approxEqAbs(f64, exp2_64(0.8923), 1.856133, epsilon));
444452 try expect(math.approxEqAbs(f64, exp2_64(1.5), 2.828427, epsilon));
453 try expect(math.approxEqAbs(f64, exp2_64(-1), 0.5, epsilon));
454 try expect(math.approxEqAbs(f64, exp2_64(-0x1.a05cc754481d1p-2), 0x1.824056efc687cp-1, epsilon));
445455}
446456
447457test "math.exp2_32.special" {