| ... | ... | @@ -37,89 +37,75 @@ fn tanh32(x: f32) f32 { |
| 37 | 37 | const u = @bitCast(u32, x); |
| 38 | 38 | const ux = u & 0x7FFFFFFF; |
| 39 | 39 | const ax = @bitCast(f32, ux); |
| 40 | const sign = (u >> 31) != 0; |
| 40 | 41 | |
| 41 | 42 | var t: f32 = undefined; |
| 42 | 43 | |
| 43 | | if (x == 0.0 or math.isNan(x)) { |
| 44 | | return x; |
| 45 | | } |
| 46 | | |
| 47 | 44 | // |x| < log(3) / 2 ~= 0.5493 or nan |
| 48 | 45 | if (ux > 0x3F0C9F54) { |
| 49 | 46 | // |x| > 10 |
| 50 | 47 | if (ux > 0x41200000) { |
| 51 | | t = 1.0; |
| 48 | t = 1.0 + 0 / x; |
| 52 | 49 | } else { |
| 53 | | t = math.expm1(2 * x); |
| 50 | t = math.expm1(2 * ax); |
| 54 | 51 | t = 1 - 2 / (t + 2); |
| 55 | 52 | } |
| 56 | 53 | } |
| 57 | 54 | // |x| > log(5 / 3) / 2 ~= 0.2554 |
| 58 | 55 | else if (ux > 0x3E82C578) { |
| 59 | | t = math.expm1(2 * x); |
| 56 | t = math.expm1(2 * ax); |
| 60 | 57 | t = t / (t + 2); |
| 61 | 58 | } |
| 62 | 59 | // |x| >= 0x1.0p-126 |
| 63 | 60 | else if (ux >= 0x00800000) { |
| 64 | | t = math.expm1(-2 * x); |
| 61 | t = math.expm1(-2 * ax); |
| 65 | 62 | t = -t / (t + 2); |
| 66 | 63 | } |
| 67 | 64 | // |x| is subnormal |
| 68 | 65 | else { |
| 69 | | math.doNotOptimizeAway(x * x); |
| 70 | | t = x; |
| 66 | math.doNotOptimizeAway(ax * ax); |
| 67 | t = ax; |
| 71 | 68 | } |
| 72 | 69 | |
| 73 | | if (u >> 31 != 0) { |
| 74 | | return -t; |
| 75 | | } else { |
| 76 | | return t; |
| 77 | | } |
| 70 | return if (sign) -t else t; |
| 78 | 71 | } |
| 79 | 72 | |
| 80 | 73 | fn tanh64(x: f64) f64 { |
| 81 | 74 | 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; |
| 84 | 79 | |
| 85 | 80 | var t: f64 = undefined; |
| 86 | 81 | |
| 87 | | // TODO: Shouldn't need these checks. |
| 88 | | if (x == 0.0 or math.isNan(x)) { |
| 89 | | return x; |
| 90 | | } |
| 91 | | |
| 92 | 82 | // |x| < log(3) / 2 ~= 0.5493 or nan |
| 93 | 83 | if (w > 0x3FE193EA) { |
| 94 | 84 | // |x| > 20 or nan |
| 95 | 85 | if (w > 0x40340000) { |
| 96 | | t = 1.0; |
| 86 | t = 1.0 - 0 / ax; |
| 97 | 87 | } else { |
| 98 | | t = math.expm1(2 * x); |
| 88 | t = math.expm1(2 * ax); |
| 99 | 89 | t = 1 - 2 / (t + 2); |
| 100 | 90 | } |
| 101 | 91 | } |
| 102 | 92 | // |x| > log(5 / 3) / 2 ~= 0.2554 |
| 103 | 93 | else if (w > 0x3FD058AE) { |
| 104 | | t = math.expm1(2 * x); |
| 94 | t = math.expm1(2 * ax); |
| 105 | 95 | t = t / (t + 2); |
| 106 | 96 | } |
| 107 | 97 | // |x| >= 0x1.0p-1022 |
| 108 | 98 | else if (w >= 0x00100000) { |
| 109 | | t = math.expm1(-2 * x); |
| 99 | t = math.expm1(-2 * ax); |
| 110 | 100 | t = -t / (t + 2); |
| 111 | 101 | } |
| 112 | 102 | // |x| is subnormal |
| 113 | 103 | else { |
| 114 | | math.doNotOptimizeAway(@floatCast(f32, x)); |
| 115 | | t = x; |
| 104 | math.doNotOptimizeAway(@floatCast(f32, ax)); |
| 105 | t = ax; |
| 116 | 106 | } |
| 117 | 107 | |
| 118 | | if (u >> 63 != 0) { |
| 119 | | return -t; |
| 120 | | } else { |
| 121 | | return t; |
| 122 | | } |
| 108 | return if (sign) -t else t; |
| 123 | 109 | } |
| 124 | 110 | |
| 125 | 111 | test "math.tanh" { |
| ... | ... | @@ -135,6 +121,9 @@ test "math.tanh32" { |
| 135 | 121 | try expect(math.approxEqAbs(f32, tanh32(0.8923), 0.712528, epsilon)); |
| 136 | 122 | try expect(math.approxEqAbs(f32, tanh32(1.5), 0.905148, epsilon)); |
| 137 | 123 | 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)); |
| 138 | 127 | } |
| 139 | 128 | |
| 140 | 129 | test "math.tanh64" { |
| ... | ... | @@ -145,6 +134,9 @@ test "math.tanh64" { |
| 145 | 134 | try expect(math.approxEqAbs(f64, tanh64(0.8923), 0.712528, epsilon)); |
| 146 | 135 | try expect(math.approxEqAbs(f64, tanh64(1.5), 0.905148, epsilon)); |
| 147 | 136 | 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)); |
| 148 | 140 | } |
| 149 | 141 | |
| 150 | 142 | test "math.tanh32.special" { |