authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-06-11 09:23:48+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-11 14:04:46+03:00
log56c0a66ce30094d199420a3f2b2d2e759c1b0613
tree6886f18e18b63384d5345ccd226dcb28cd2a0820
parent2ce033f4159409db66866e28a3591256ea0dfadc

std: Fix tanh for negative inputs

It turns out the code was not ported correctly from C and produced wrong results for negative input values. As a bonus fix the NaN codepath by adding yet another missing piece of code. Spotted in #9047

1 files changed, 25 insertions(+), 33 deletions(-)

lib/std/math/tanh.zig+25-33
......@@ -37,89 +37,75 @@ fn tanh32(x: f32) f32 {
3737 const u = @bitCast(u32, x);
3838 const ux = u & 0x7FFFFFFF;
3939 const ax = @bitCast(f32, ux);
40 const sign = (u >> 31) != 0;
4041
4142 var t: f32 = undefined;
4243
43 if (x == 0.0 or math.isNan(x)) {
44 return x;
45 }
46
4744 // |x| < log(3) / 2 ~= 0.5493 or nan
4845 if (ux > 0x3F0C9F54) {
4946 // |x| > 10
5047 if (ux > 0x41200000) {
51 t = 1.0;
48 t = 1.0 + 0 / x;
5249 } else {
53 t = math.expm1(2 * x);
50 t = math.expm1(2 * ax);
5451 t = 1 - 2 / (t + 2);
5552 }
5653 }
5754 // |x| > log(5 / 3) / 2 ~= 0.2554
5855 else if (ux > 0x3E82C578) {
59 t = math.expm1(2 * x);
56 t = math.expm1(2 * ax);
6057 t = t / (t + 2);
6158 }
6259 // |x| >= 0x1.0p-126
6360 else if (ux >= 0x00800000) {
64 t = math.expm1(-2 * x);
61 t = math.expm1(-2 * ax);
6562 t = -t / (t + 2);
6663 }
6764 // |x| is subnormal
6865 else {
69 math.doNotOptimizeAway(x * x);
70 t = x;
66 math.doNotOptimizeAway(ax * ax);
67 t = ax;
7168 }
7269
73 if (u >> 31 != 0) {
74 return -t;
75 } else {
76 return t;
77 }
70 return if (sign) -t else t;
7871}
7972
8073fn tanh64(x: f64) f64 {
8174 const u = @bitCast(u64, x);
82 const w = @intCast(u32, u >> 32);
83 const ax = @bitCast(f64, u & (maxInt(u64) >> 1));
75 const ux = u & 0x7FFFFFFFFFFFFFFF;
76 const w = @intCast(u32, ux >> 32);
77 const ax = @bitCast(f64, ux);
78 const sign = (u >> 63) != 0;
8479
8580 var t: f64 = undefined;
8681
87 // TODO: Shouldn't need these checks.
88 if (x == 0.0 or math.isNan(x)) {
89 return x;
90 }
91
9282 // |x| < log(3) / 2 ~= 0.5493 or nan
9383 if (w > 0x3FE193EA) {
9484 // |x| > 20 or nan
9585 if (w > 0x40340000) {
96 t = 1.0;
86 t = 1.0 - 0 / ax;
9787 } else {
98 t = math.expm1(2 * x);
88 t = math.expm1(2 * ax);
9989 t = 1 - 2 / (t + 2);
10090 }
10191 }
10292 // |x| > log(5 / 3) / 2 ~= 0.2554
10393 else if (w > 0x3FD058AE) {
104 t = math.expm1(2 * x);
94 t = math.expm1(2 * ax);
10595 t = t / (t + 2);
10696 }
10797 // |x| >= 0x1.0p-1022
10898 else if (w >= 0x00100000) {
109 t = math.expm1(-2 * x);
99 t = math.expm1(-2 * ax);
110100 t = -t / (t + 2);
111101 }
112102 // |x| is subnormal
113103 else {
114 math.doNotOptimizeAway(@floatCast(f32, x));
115 t = x;
104 math.doNotOptimizeAway(@floatCast(f32, ax));
105 t = ax;
116106 }
117107
118 if (u >> 63 != 0) {
119 return -t;
120 } else {
121 return t;
122 }
108 return if (sign) -t else t;
123109}
124110
125111test "math.tanh" {
......@@ -135,6 +121,9 @@ test "math.tanh32" {
135121 try expect(math.approxEqAbs(f32, tanh32(0.8923), 0.712528, epsilon));
136122 try expect(math.approxEqAbs(f32, tanh32(1.5), 0.905148, epsilon));
137123 try expect(math.approxEqAbs(f32, tanh32(37.45), 1.0, epsilon));
124 try expect(math.approxEqAbs(f32, tanh32(-0.8923), -0.712528, epsilon));
125 try expect(math.approxEqAbs(f32, tanh32(-1.5), -0.905148, epsilon));
126 try expect(math.approxEqAbs(f32, tanh32(-37.45), -1.0, epsilon));
138127}
139128
140129test "math.tanh64" {
......@@ -145,6 +134,9 @@ test "math.tanh64" {
145134 try expect(math.approxEqAbs(f64, tanh64(0.8923), 0.712528, epsilon));
146135 try expect(math.approxEqAbs(f64, tanh64(1.5), 0.905148, epsilon));
147136 try expect(math.approxEqAbs(f64, tanh64(37.45), 1.0, epsilon));
137 try expect(math.approxEqAbs(f64, tanh64(-0.8923), -0.712528, epsilon));
138 try expect(math.approxEqAbs(f64, tanh64(-1.5), -0.905148, epsilon));
139 try expect(math.approxEqAbs(f64, tanh64(-37.45), -1.0, epsilon));
148140}
149141
150142test "math.tanh32.special" {