| author | |
| committer | |
| log | 4c16f9a3c35b23b9917f2a27b91ba8cd20e6fd82 |
| tree | 778f0f06734f7dc17e9269ee1cf5b513f7b504c0 |
| parent | 865b53f2860405a718262abf9a794d2bf9529dbc |
This covers the majority of the functions as covered by the C99
specification for a math library.
Code is adapted primarily from musl libc, with the pow and standard
trigonometric functions adapted from the Go stdlib.
Changes:
- Remove assert expose in index and import as needed.
- Add float log function and merge with existing base 2 integer
implementation.
See https://github.com/tiehuis/zig-fmath.
See #374.47 files changed, 6150 insertions(+), 174 deletions(-)
std/math/_expo2.zig created+28| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | ||
| 3 | pub fn expo2(x: var) -> @typeOf(x) { | |
| 4 | const T = @typeOf(x); | |
| 5 | switch (T) { | |
| 6 | f32 => expo2f(x), | |
| 7 | f64 => expo2d(x), | |
| 8 | else => @compileError("expo2 not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | fn expo2f(x: f32) -> f32 { | |
| 13 | const k: u32 = 235; | |
| 14 | const kln2 = 0x1.45C778p+7; | |
| 15 | ||
| 16 | const u = (0x7F + k / 2) << 23; | |
| 17 | const scale = @bitCast(f32, u); | |
| 18 | math.exp(x - kln2) * scale * scale | |
| 19 | } | |
| 20 | ||
| 21 | fn expo2d(x: f64) -> f64 { | |
| 22 | const k: u32 = 2043; | |
| 23 | const kln2 = 0x1.62066151ADD8BP+10; | |
| 24 | ||
| 25 | const u = (0x3FF + k / 2) << 20; | |
| 26 | const scale = @bitCast(f64, u64(u) << 32); | |
| 27 | math.exp(x - kln2) * scale * scale | |
| 28 | } |
std/math/acos.zig created+165| ... | ... | @@ -0,0 +1,165 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn acos(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(acos32, x), | |
| 8 | f64 => @inlineCall(acos64, x), | |
| 9 | else => @compileError("acos not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn r32(z: f32) -> f32 { | |
| 14 | const pS0 = 1.6666586697e-01; | |
| 15 | const pS1 = -4.2743422091e-02; | |
| 16 | const pS2 = -8.6563630030e-03; | |
| 17 | const qS1 = -7.0662963390e-01; | |
| 18 | ||
| 19 | const p = z * (pS0 + z * (pS1 + z * pS2)); | |
| 20 | const q = 1.0 + z * qS1; | |
| 21 | p / q | |
| 22 | } | |
| 23 | ||
| 24 | fn acos32(x: f32) -> f32 { | |
| 25 | const pio2_hi = 1.5707962513e+00; | |
| 26 | const pio2_lo = 7.5497894159e-08; | |
| 27 | ||
| 28 | const hx: u32 = @bitCast(u32, x); | |
| 29 | const ix: u32 = hx & 0x7FFFFFFF; | |
| 30 | ||
| 31 | // |x| >= 1 or nan | |
| 32 | if (ix >= 0x3F800000) { | |
| 33 | if (ix == 0x3F800000) { | |
| 34 | if (hx >> 31 != 0) { | |
| 35 | return 2.0 * pio2_hi + 0x1.0p-120; | |
| 36 | } else { | |
| 37 | return 0; | |
| 38 | } | |
| 39 | } else { | |
| 40 | return 0 / (x - x); | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | // |x| < 0.5 | |
| 45 | if (ix < 0x3F000000) { | |
| 46 | if (ix <= 0x32800000) { // |x| < 2^(-26) | |
| 47 | return pio2_hi + 0x1.0p-120; | |
| 48 | } else { | |
| 49 | return pio2_hi - (x - (pio2_lo - x * r32(x * x))); | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | // x < -0.5 | |
| 54 | if (hx >> 31 != 0) { | |
| 55 | const z = (1 + x) * 0.5; | |
| 56 | const s = math.sqrt(z); | |
| 57 | const w = r32(z) * s - pio2_lo; | |
| 58 | return 2 * (pio2_hi - (s + w)); | |
| 59 | } | |
| 60 | ||
| 61 | // x > 0.5 | |
| 62 | const z = (1.0 - x) * 0.5; | |
| 63 | const s = math.sqrt(z); | |
| 64 | const jx = @bitCast(u32, s); | |
| 65 | const df = @bitCast(f32, jx & 0xFFFFF000); | |
| 66 | const c = (z - df * df) / (s + df); | |
| 67 | const w = r32(z) * s + c; | |
| 68 | 2 * (df + w) | |
| 69 | } | |
| 70 | ||
| 71 | fn r64(z: f64) -> f64 { | |
| 72 | const pS0: f64 = 1.66666666666666657415e-01; | |
| 73 | const pS1: f64 = -3.25565818622400915405e-01; | |
| 74 | const pS2: f64 = 2.01212532134862925881e-01; | |
| 75 | const pS3: f64 = -4.00555345006794114027e-02; | |
| 76 | const pS4: f64 = 7.91534994289814532176e-04; | |
| 77 | const pS5: f64 = 3.47933107596021167570e-05; | |
| 78 | const qS1: f64 = -2.40339491173441421878e+00; | |
| 79 | const qS2: f64 = 2.02094576023350569471e+00; | |
| 80 | const qS3: f64 = -6.88283971605453293030e-01; | |
| 81 | const qS4: f64 = 7.70381505559019352791e-02; | |
| 82 | ||
| 83 | const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5))))); | |
| 84 | const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4))); | |
| 85 | p / q | |
| 86 | } | |
| 87 | ||
| 88 | fn acos64(x: f64) -> f64 { | |
| 89 | const pio2_hi: f64 = 1.57079632679489655800e+00; | |
| 90 | const pio2_lo: f64 = 6.12323399573676603587e-17; | |
| 91 | ||
| 92 | const ux = @bitCast(u64, x); | |
| 93 | const hx = u32(ux >> 32); | |
| 94 | const ix = hx & 0x7FFFFFFF; | |
| 95 | ||
| 96 | // |x| >= 1 or nan | |
| 97 | if (ix >= 0x3FF00000) { | |
| 98 | const lx = u32(ux & 0xFFFFFFFF); | |
| 99 | ||
| 100 | // acos(1) = 0, acos(-1) = pi | |
| 101 | if ((ix - 0x3FF00000) | lx == 0) { | |
| 102 | if (hx >> 31 != 0) { | |
| 103 | return 2 * pio2_hi + 0x1.0p-120; | |
| 104 | } else { | |
| 105 | return 0; | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | return 0 / (x - x); | |
| 110 | } | |
| 111 | ||
| 112 | // |x| < 0.5 | |
| 113 | if (ix < 0x3FE00000) { | |
| 114 | // |x| < 2^(-57) | |
| 115 | if (ix <= 0x3C600000) { | |
| 116 | return pio2_hi + 0x1.0p-120; | |
| 117 | } else { | |
| 118 | return pio2_hi - (x - (pio2_lo - x * r64(x * x))); | |
| 119 | } | |
| 120 | } | |
| 121 | ||
| 122 | // x < -0.5 | |
| 123 | if (hx >> 31 != 0) { | |
| 124 | const z = (1.0 + x) * 0.5; | |
| 125 | const s = math.sqrt(z); | |
| 126 | const w = r64(z) * s - pio2_lo; | |
| 127 | return 2 * (pio2_hi - (s + w)); | |
| 128 | } | |
| 129 | ||
| 130 | // x > 0.5 | |
| 131 | const z = (1.0 - x) * 0.5; | |
| 132 | const s = math.sqrt(z); | |
| 133 | const jx = @bitCast(u64, s); | |
| 134 | const df = @bitCast(f64, jx & 0xFFFFFFFF00000000); | |
| 135 | const c = (z - df * df) / (s + df); | |
| 136 | const w = r64(z) * s + c; | |
| 137 | 2 * (df + w) | |
| 138 | } | |
| 139 | ||
| 140 | test "acos" { | |
| 141 | assert(acos(f32(0.0)) == acos32(0.0)); | |
| 142 | assert(acos(f64(0.0)) == acos64(0.0)); | |
| 143 | } | |
| 144 | ||
| 145 | test "acos32" { | |
| 146 | const epsilon = 0.000001; | |
| 147 | ||
| 148 | assert(math.approxEq(f32, acos32(0.0), 1.570796, epsilon)); | |
| 149 | assert(math.approxEq(f32, acos32(0.2), 1.369438, epsilon)); | |
| 150 | assert(math.approxEq(f32, acos32(0.3434), 1.220262, epsilon)); | |
| 151 | assert(math.approxEq(f32, acos32(0.5), 1.047198, epsilon)); | |
| 152 | assert(math.approxEq(f32, acos32(0.8923), 0.468382, epsilon)); | |
| 153 | assert(math.approxEq(f32, acos32(-0.2), 1.772154, epsilon)); | |
| 154 | } | |
| 155 | ||
| 156 | test "acos64" { | |
| 157 | const epsilon = 0.000001; | |
| 158 | ||
| 159 | assert(math.approxEq(f64, acos64(0.0), 1.570796, epsilon)); | |
| 160 | assert(math.approxEq(f64, acos64(0.2), 1.369438, epsilon)); | |
| 161 | assert(math.approxEq(f64, acos64(0.3434), 1.220262, epsilon)); | |
| 162 | assert(math.approxEq(f64, acos64(0.5), 1.047198, epsilon)); | |
| 163 | assert(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon)); | |
| 164 | assert(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon)); | |
| 165 | } |
std/math/acosh.zig created+71| ... | ... | @@ -0,0 +1,71 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn acosh(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(acoshf, x), | |
| 8 | f64 => @inlineCall(acoshd, x), | |
| 9 | else => @compileError("acosh not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | // acosh(x) = log(x + sqrt(x * x - 1)) | |
| 14 | fn acoshf(x: f32) -> f32 { | |
| 15 | const u = @bitCast(u32, x); | |
| 16 | const i = u & 0x7FFFFFFF; | |
| 17 | ||
| 18 | // |x| < 2, invalid if x < 1 or nan | |
| 19 | if (i < 0x3F800000 + (1 << 23)) { | |
| 20 | math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1))) | |
| 21 | } | |
| 22 | // |x| < 0x1p12 | |
| 23 | else if (i < 0x3F800000 + (12 << 23)) { | |
| 24 | math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1))) | |
| 25 | } | |
| 26 | // |x| >= 0x1p12 | |
| 27 | else { | |
| 28 | math.ln(x) + 0.693147180559945309417232121458176568 | |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | fn acoshd(x: f64) -> f64 { | |
| 33 | const u = @bitCast(u64, x); | |
| 34 | const e = (u >> 52) & 0x7FF; | |
| 35 | ||
| 36 | // |x| < 2, invalid if x < 1 or nan | |
| 37 | if (e < 0x3FF + 1) { | |
| 38 | math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1))) | |
| 39 | } | |
| 40 | // |x| < 0x1p26 | |
| 41 | else if (e < 0x3FF + 26) { | |
| 42 | math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1))) | |
| 43 | } | |
| 44 | // |x| >= 0x1p26 or nan | |
| 45 | else { | |
| 46 | math.ln(x) + 0.693147180559945309417232121458176568 | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | test "acosh" { | |
| 51 | assert(acosh(f32(1.5)) == acoshf(1.5)); | |
| 52 | assert(acosh(f64(1.5)) == acoshd(1.5)); | |
| 53 | } | |
| 54 | ||
| 55 | test "acoshf" { | |
| 56 | const epsilon = 0.000001; | |
| 57 | ||
| 58 | assert(math.approxEq(f32, acoshf(1.5), 0.962424, epsilon)); | |
| 59 | assert(math.approxEq(f32, acoshf(37.45), 4.315976, epsilon)); | |
| 60 | assert(math.approxEq(f32, acoshf(89.123), 5.183133, epsilon)); | |
| 61 | assert(math.approxEq(f32, acoshf(123123.234375), 12.414088, epsilon)); | |
| 62 | } | |
| 63 | ||
| 64 | test "acoshd" { | |
| 65 | const epsilon = 0.000001; | |
| 66 | ||
| 67 | assert(math.approxEq(f64, acoshd(1.5), 0.962424, epsilon)); | |
| 68 | assert(math.approxEq(f64, acoshd(37.45), 4.315976, epsilon)); | |
| 69 | assert(math.approxEq(f64, acoshd(89.123), 5.183133, epsilon)); | |
| 70 | assert(math.approxEq(f64, acoshd(123123.234375), 12.414088, epsilon)); | |
| 71 | } |
std/math/asin.zig created+157| ... | ... | @@ -0,0 +1,157 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn asin(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(asin32, x), | |
| 8 | f64 => @inlineCall(asin64, x), | |
| 9 | else => @compileError("asin not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn r32(z: f32) -> f32 { | |
| 14 | const pS0 = 1.6666586697e-01; | |
| 15 | const pS1 = -4.2743422091e-02; | |
| 16 | const pS2 = -8.6563630030e-03; | |
| 17 | const qS1 = -7.0662963390e-01; | |
| 18 | ||
| 19 | const p = z * (pS0 + z * (pS1 + z * pS2)); | |
| 20 | const q = 1.0 + z * qS1; | |
| 21 | p / q | |
| 22 | } | |
| 23 | ||
| 24 | fn asin32(x: f32) -> f32 { | |
| 25 | const pio2 = 1.570796326794896558e+00; | |
| 26 | ||
| 27 | const hx: u32 = @bitCast(u32, x); | |
| 28 | const ix: u32 = hx & 0x7FFFFFFF; | |
| 29 | ||
| 30 | // |x| >= 1 | |
| 31 | if (ix >= 0x3F800000) { | |
| 32 | // |x| >= 1 | |
| 33 | if (ix == 0x3F800000) { | |
| 34 | return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact | |
| 35 | } else { | |
| 36 | return 0 / (x - x); // asin(|x| > 1) is nan | |
| 37 | } | |
| 38 | } | |
| 39 | ||
| 40 | // |x| < 0.5 | |
| 41 | if (ix < 0x3F000000) { | |
| 42 | // 0x1p-126 <= |x| < 0x1p-12 | |
| 43 | if (ix < 0x39800000 and ix >= 0x00800000) { | |
| 44 | return x; | |
| 45 | } else { | |
| 46 | return x + x * r32(x * x); | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | // 1 > |x| >= 0.5 | |
| 51 | const z = (1 - math.fabs(x)) * 0.5; | |
| 52 | const s = math.sqrt(z); | |
| 53 | const fx = pio2 - 2 * (s + s * r32(z)); | |
| 54 | ||
| 55 | if (hx >> 31 != 0) { | |
| 56 | -fx | |
| 57 | } else { | |
| 58 | fx | |
| 59 | } | |
| 60 | } | |
| 61 | ||
| 62 | fn r64(z: f64) -> f64 { | |
| 63 | const pS0: f64 = 1.66666666666666657415e-01; | |
| 64 | const pS1: f64 = -3.25565818622400915405e-01; | |
| 65 | const pS2: f64 = 2.01212532134862925881e-01; | |
| 66 | const pS3: f64 = -4.00555345006794114027e-02; | |
| 67 | const pS4: f64 = 7.91534994289814532176e-04; | |
| 68 | const pS5: f64 = 3.47933107596021167570e-05; | |
| 69 | const qS1: f64 = -2.40339491173441421878e+00; | |
| 70 | const qS2: f64 = 2.02094576023350569471e+00; | |
| 71 | const qS3: f64 = -6.88283971605453293030e-01; | |
| 72 | const qS4: f64 = 7.70381505559019352791e-02; | |
| 73 | ||
| 74 | const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5))))); | |
| 75 | const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4))); | |
| 76 | p / q | |
| 77 | } | |
| 78 | ||
| 79 | fn asin64(x: f64) -> f64 { | |
| 80 | const pio2_hi: f64 = 1.57079632679489655800e+00; | |
| 81 | const pio2_lo: f64 = 6.12323399573676603587e-17; | |
| 82 | ||
| 83 | const ux = @bitCast(u64, x); | |
| 84 | const hx = u32(ux >> 32); | |
| 85 | const ix = hx & 0x7FFFFFFF; | |
| 86 | ||
| 87 | // |x| >= 1 or nan | |
| 88 | if (ix >= 0x3FF00000) { | |
| 89 | const lx = u32(ux & 0xFFFFFFFF); | |
| 90 | ||
| 91 | // asin(1) = +-pi/2 with inexact | |
| 92 | if ((ix - 0x3FF00000) | lx == 0) { | |
| 93 | return x * pio2_hi + 0x1.0p-120; | |
| 94 | } else { | |
| 95 | return 0/ (x - x); | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | // |x| < 0.5 | |
| 100 | if (ix < 0x3FE00000) { | |
| 101 | // if 0x1p-1022 <= |x| < 0x1p-26 avoid raising overflow | |
| 102 | if (ix < 0x3E500000 and ix >= 0x00100000) { | |
| 103 | return x; | |
| 104 | } else { | |
| 105 | return x + x * r64(x * x); | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | // 1 > |x| >= 0.5 | |
| 110 | const z = (1 - math.fabs(x)) * 0.5; | |
| 111 | const s = math.sqrt(z); | |
| 112 | const r = r64(z); | |
| 113 | var fx: f64 = undefined; | |
| 114 | ||
| 115 | // |x| > 0.975 | |
| 116 | if (ix >= 0x3FEF3333) { | |
| 117 | fx = pio2_hi - 2 * (s + s * r) | |
| 118 | } else { | |
| 119 | const jx = @bitCast(u64, s); | |
| 120 | const df = @bitCast(f64, jx & 0xFFFFFFFF00000000); | |
| 121 | const c = (z - df * df) / (s + df); | |
| 122 | fx = 0.5 * pio2_hi - (2 * s * r - (pio2_lo - 2 * c) - (0.5 * pio2_hi - 2 * df)); | |
| 123 | } | |
| 124 | ||
| 125 | if (hx >> 31 != 0) { | |
| 126 | -fx | |
| 127 | } else { | |
| 128 | fx | |
| 129 | } | |
| 130 | } | |
| 131 | ||
| 132 | test "asin" { | |
| 133 | assert(asin(f32(0.0)) == asin32(0.0)); | |
| 134 | assert(asin(f64(0.0)) == asin64(0.0)); | |
| 135 | } | |
| 136 | ||
| 137 | test "asin32" { | |
| 138 | const epsilon = 0.000001; | |
| 139 | ||
| 140 | assert(math.approxEq(f32, asin32(0.0), 0.0, epsilon)); | |
| 141 | assert(math.approxEq(f32, asin32(0.2), 0.201358, epsilon)); | |
| 142 | assert(math.approxEq(f32, asin32(-0.2), -0.201358, epsilon)); | |
| 143 | assert(math.approxEq(f32, asin32(0.3434), 0.350535, epsilon)); | |
| 144 | assert(math.approxEq(f32, asin32(0.5), 0.523599, epsilon)); | |
| 145 | assert(math.approxEq(f32, asin32(0.8923), 1.102415, epsilon)); | |
| 146 | } | |
| 147 | ||
| 148 | test "asin64" { | |
| 149 | const epsilon = 0.000001; | |
| 150 | ||
| 151 | assert(math.approxEq(f64, asin64(0.0), 0.0, epsilon)); | |
| 152 | assert(math.approxEq(f64, asin64(0.2), 0.201358, epsilon)); | |
| 153 | assert(math.approxEq(f64, asin64(-0.2), -0.201358, epsilon)); | |
| 154 | assert(math.approxEq(f64, asin64(0.3434), 0.350535, epsilon)); | |
| 155 | assert(math.approxEq(f64, asin64(0.5), 0.523599, epsilon)); | |
| 156 | assert(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon)); | |
| 157 | } |
std/math/asinh.zig created+95| ... | ... | @@ -0,0 +1,95 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn asinh(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(asinhf, x), | |
| 8 | f64 => @inlineCall(asinhd, x), | |
| 9 | else => @compileError("asinh not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | // asinh(x) = sign(x) * log(|x| + sqrt(x * x + 1)) ~= x - x^3/6 + o(x^5) | |
| 14 | fn asinhf(x: f32) -> f32 { | |
| 15 | const u = @bitCast(u32, x); | |
| 16 | const i = u & 0x7FFFFFFF; | |
| 17 | const s = i >> 31; | |
| 18 | ||
| 19 | var rx = @bitCast(f32, i); // |x| | |
| 20 | ||
| 21 | // |x| >= 0x1p12 or inf or nan | |
| 22 | if (i >= 0x3F800000 + (12 << 23)) { | |
| 23 | rx = math.ln(rx) + 0.69314718055994530941723212145817656; | |
| 24 | } | |
| 25 | // |x| >= 2 | |
| 26 | else if (i >= 0x3F800000 + (1 << 23)) { | |
| 27 | rx = math.ln(2 * x + 1 / (math.sqrt(x * x + 1) + x)); | |
| 28 | } | |
| 29 | // |x| >= 0x1p-12, up to 1.6ulp error | |
| 30 | else if (i >= 0x3F800000 - (12 << 23)) { | |
| 31 | rx = math.log1p(x + x * x / (math.sqrt(x * x + 1) + 1)); | |
| 32 | } | |
| 33 | // |x| < 0x1p-12, inexact if x != 0 | |
| 34 | else { | |
| 35 | math.forceEval(x + 0x1.0p120); | |
| 36 | } | |
| 37 | ||
| 38 | if (s != 0) -rx else rx | |
| 39 | } | |
| 40 | ||
| 41 | fn asinhd(x: f64) -> f64 { | |
| 42 | const u = @bitCast(u64, x); | |
| 43 | const e = (u >> 52) & 0x7FF; | |
| 44 | const s = u >> 63; | |
| 45 | ||
| 46 | var rx = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x| | |
| 47 | ||
| 48 | // |x| >= 0x1p26 or inf or nan | |
| 49 | if (e >= 0x3FF + 26) { | |
| 50 | rx = math.ln(rx) + 0.693147180559945309417232121458176568; | |
| 51 | } | |
| 52 | // |x| >= 2 | |
| 53 | else if (e >= 0x3FF + 1) { | |
| 54 | rx = math.ln(2 * x + 1 / (math.sqrt(x * x + 1) + x)); | |
| 55 | } | |
| 56 | // |x| >= 0x1p-12, up to 1.6ulp error | |
| 57 | else if (e >= 0x3FF - 26) { | |
| 58 | rx = math.log1p(x + x * x / (math.sqrt(x * x + 1) + 1)); | |
| 59 | } | |
| 60 | // |x| < 0x1p-12, inexact if x != 0 | |
| 61 | else { | |
| 62 | math.forceEval(x + 0x1.0p120); | |
| 63 | } | |
| 64 | ||
| 65 | if (s != 0) -rx else rx | |
| 66 | } | |
| 67 | ||
| 68 | test "asinh" { | |
| 69 | assert(asinh(f32(0.0)) == asinhf(0.0)); | |
| 70 | assert(asinh(f64(0.0)) == asinhd(0.0)); | |
| 71 | } | |
| 72 | ||
| 73 | test "asinhf" { | |
| 74 | const epsilon = 0.000001; | |
| 75 | ||
| 76 | assert(math.approxEq(f32, asinhf(0.0), 0.0, epsilon)); | |
| 77 | assert(math.approxEq(f32, asinhf(0.2), 0.198690, epsilon)); | |
| 78 | assert(math.approxEq(f32, asinhf(0.8923), 0.803133, epsilon)); | |
| 79 | assert(math.approxEq(f32, asinhf(1.5), 1.194763, epsilon)); | |
| 80 | assert(math.approxEq(f32, asinhf(37.45), 4.316332, epsilon)); | |
| 81 | assert(math.approxEq(f32, asinhf(89.123), 5.183196, epsilon)); | |
| 82 | assert(math.approxEq(f32, asinhf(123123.234375), 12.414088, epsilon)); | |
| 83 | } | |
| 84 | ||
| 85 | test "asinhd" { | |
| 86 | const epsilon = 0.000001; | |
| 87 | ||
| 88 | assert(math.approxEq(f64, asinhd(0.0), 0.0, epsilon)); | |
| 89 | assert(math.approxEq(f64, asinhd(0.2), 0.198690, epsilon)); | |
| 90 | assert(math.approxEq(f64, asinhd(0.8923), 0.803133, epsilon)); | |
| 91 | assert(math.approxEq(f64, asinhd(1.5), 1.194763, epsilon)); | |
| 92 | assert(math.approxEq(f64, asinhd(37.45), 4.316332, epsilon)); | |
| 93 | assert(math.approxEq(f64, asinhd(89.123), 5.183196, epsilon)); | |
| 94 | assert(math.approxEq(f64, asinhd(123123.234375), 12.414088, epsilon)); | |
| 95 | } |
std/math/atan.zig created+227| ... | ... | @@ -0,0 +1,227 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn atan(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(atan32, x), | |
| 8 | f64 => @inlineCall(atan64, x), | |
| 9 | else => @compileError("atan not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn atan32(x_: f32) -> f32 { | |
| 14 | const atanhi = []const f32 { | |
| 15 | 4.6364760399e-01, // atan(0.5)hi | |
| 16 | 7.8539812565e-01, // atan(1.0)hi | |
| 17 | 9.8279368877e-01, // atan(1.5)hi | |
| 18 | 1.5707962513e+00, // atan(inf)hi | |
| 19 | }; | |
| 20 | ||
| 21 | const atanlo = []const f32 { | |
| 22 | 5.0121582440e-09, // atan(0.5)lo | |
| 23 | 3.7748947079e-08, // atan(1.0)lo | |
| 24 | 3.4473217170e-08, // atan(1.5)lo | |
| 25 | 7.5497894159e-08, // atan(inf)lo | |
| 26 | }; | |
| 27 | ||
| 28 | const aT = []const f32 { | |
| 29 | 3.3333328366e-01, | |
| 30 | -1.9999158382e-01, | |
| 31 | 1.4253635705e-01, | |
| 32 | -1.0648017377e-01, | |
| 33 | 6.1687607318e-02, | |
| 34 | }; | |
| 35 | ||
| 36 | var x = x_; | |
| 37 | var ix: u32 = @bitCast(u32, x); | |
| 38 | const sign = ix >> 31; | |
| 39 | ix &= 0x7FFFFFFF; | |
| 40 | ||
| 41 | // |x| >= 2^26 | |
| 42 | if (ix >= 0x4C800000) { | |
| 43 | if (math.isNan(x)) { | |
| 44 | return x; | |
| 45 | } else { | |
| 46 | const z = atanhi[3] + 0x1.0p-120; | |
| 47 | return if (sign != 0) -z else z; | |
| 48 | } | |
| 49 | } | |
| 50 | ||
| 51 | var id: ?usize = undefined; | |
| 52 | ||
| 53 | // |x| < 0.4375 | |
| 54 | if (ix < 0x3EE00000) { | |
| 55 | // |x| < 2^(-12) | |
| 56 | if (ix < 0x39800000) { | |
| 57 | if (ix < 0x00800000) { | |
| 58 | math.forceEval(x * x); | |
| 59 | } | |
| 60 | return x; | |
| 61 | } | |
| 62 | id = null; | |
| 63 | } else { | |
| 64 | x = math.fabs(x); | |
| 65 | // |x| < 1.1875 | |
| 66 | if (ix < 0x3F980000) { | |
| 67 | // 7/16 <= |x| < 11/16 | |
| 68 | if (ix < 0x3F300000) { | |
| 69 | id = 0; | |
| 70 | x = (2.0 * x - 1.0) / (2.0 + x); | |
| 71 | } | |
| 72 | // 11/16 <= |x| < 19/16 | |
| 73 | else { | |
| 74 | id = 1; | |
| 75 | x = (x - 1.0) / (x + 1.0); | |
| 76 | } | |
| 77 | } | |
| 78 | else { | |
| 79 | // |x| < 2.4375 | |
| 80 | if (ix < 0x401C0000) { | |
| 81 | id = 2; | |
| 82 | x = (x - 1.5) / (1.0 + 1.5 * x); | |
| 83 | } | |
| 84 | // 2.4375 <= |x| < 2^26 | |
| 85 | else { | |
| 86 | id = 3; | |
| 87 | x = -1.0 / x; | |
| 88 | } | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | const z = x * x; | |
| 93 | const w = z * z; | |
| 94 | const s1 = z * (aT[0] + w * (aT[2] + w * aT[4])); | |
| 95 | const s2 = w * (aT[1] + w * aT[3]); | |
| 96 | ||
| 97 | if (id == null) { | |
| 98 | x - x * (s1 + s2) | |
| 99 | } else { | |
| 100 | const zz = atanhi[??id] - ((x * (s1 + s2) - atanlo[??id]) - x); | |
| 101 | if (sign != 0) -zz else zz | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | fn atan64(x_: f64) -> f64 { | |
| 106 | const atanhi = []const f64 { | |
| 107 | 4.63647609000806093515e-01, // atan(0.5)hi | |
| 108 | 7.85398163397448278999e-01, // atan(1.0)hi | |
| 109 | 9.82793723247329054082e-01, // atan(1.5)hi | |
| 110 | 1.57079632679489655800e+00, // atan(inf)hi | |
| 111 | }; | |
| 112 | ||
| 113 | const atanlo = []const f64 { | |
| 114 | 2.26987774529616870924e-17, // atan(0.5)lo | |
| 115 | 3.06161699786838301793e-17, // atan(1.0)lo | |
| 116 | 1.39033110312309984516e-17, // atan(1.5)lo | |
| 117 | 6.12323399573676603587e-17, // atan(inf)lo | |
| 118 | }; | |
| 119 | ||
| 120 | const aT = []const f64 { | |
| 121 | 3.33333333333329318027e-01, | |
| 122 | -1.99999999998764832476e-01, | |
| 123 | 1.42857142725034663711e-01, | |
| 124 | -1.11111104054623557880e-01, | |
| 125 | 9.09088713343650656196e-02, | |
| 126 | -7.69187620504482999495e-02, | |
| 127 | 6.66107313738753120669e-02, | |
| 128 | -5.83357013379057348645e-02, | |
| 129 | 4.97687799461593236017e-02, | |
| 130 | -3.65315727442169155270e-02, | |
| 131 | 1.62858201153657823623e-02, | |
| 132 | }; | |
| 133 | ||
| 134 | var x = x_; | |
| 135 | var ux = @bitCast(u64, x); | |
| 136 | var ix = u32(ux >> 32); | |
| 137 | const sign = ix >> 31; | |
| 138 | ix &= 0x7FFFFFFF; | |
| 139 | ||
| 140 | // |x| >= 2^66 | |
| 141 | if (ix >= 0x44100000) { | |
| 142 | if (math.isNan(x)) { | |
| 143 | return x; | |
| 144 | } else { | |
| 145 | const z = atanhi[3] + 0x1.0p-120; | |
| 146 | return if (sign != 0) -z else z; | |
| 147 | } | |
| 148 | } | |
| 149 | ||
| 150 | var id: ?usize = undefined; | |
| 151 | ||
| 152 | // |x| < 0.4375 | |
| 153 | if (ix < 0x3DFC0000) { | |
| 154 | // |x| < 2^(-27) | |
| 155 | if (ix < 0x3E400000) { | |
| 156 | if (ix < 0x00100000) { | |
| 157 | math.forceEval(f32(x)); | |
| 158 | } | |
| 159 | return x; | |
| 160 | } | |
| 161 | id = null; | |
| 162 | } else { | |
| 163 | x = math.fabs(x); | |
| 164 | // |x| < 1.1875 | |
| 165 | if (ix < 0x3FF30000) { | |
| 166 | // 7/16 <= |x| < 11/16 | |
| 167 | if (ix < 0x3FE60000) { | |
| 168 | id = 0; | |
| 169 | x = (2.0 * x - 1.0) / (2.0 + x); | |
| 170 | } | |
| 171 | // 11/16 <= |x| < 19/16 | |
| 172 | else { | |
| 173 | id = 1; | |
| 174 | x = (x - 1.0) / (x + 1.0); | |
| 175 | } | |
| 176 | } | |
| 177 | else { | |
| 178 | // |x| < 2.4375 | |
| 179 | if (ix < 0x40038000) { | |
| 180 | id = 2; | |
| 181 | x = (x - 1.5) / (1.0 + 1.5 * x); | |
| 182 | } | |
| 183 | // 2.4375 <= |x| < 2^66 | |
| 184 | else { | |
| 185 | id = 3; | |
| 186 | x = -1.0 / x; | |
| 187 | } | |
| 188 | } | |
| 189 | } | |
| 190 | ||
| 191 | const z = x * x; | |
| 192 | const w = z * z; | |
| 193 | const s1 = z * (aT[0] + w * (aT[2] + w * (aT[4] + w * (aT[6] + w * (aT[8] + w * aT[10]))))); | |
| 194 | const s2 = w * (aT[1] + w * (aT[3] + w * (aT[5] + w * (aT[7] + w * aT[9])))); | |
| 195 | ||
| 196 | if (id == null) { | |
| 197 | x - x * (s1 + s2) | |
| 198 | } else { | |
| 199 | const zz = atanhi[??id] - ((x * (s1 + s2) - atanlo[??id]) - x); | |
| 200 | if (sign != 0) -zz else zz | |
| 201 | } | |
| 202 | } | |
| 203 | ||
| 204 | test "atan" { | |
| 205 | assert(atan(f32(0.2)) == atan32(0.2)); | |
| 206 | assert(atan(f64(0.2)) == atan64(0.2)); | |
| 207 | } | |
| 208 | ||
| 209 | test "atan32" { | |
| 210 | const epsilon = 0.000001; | |
| 211 | ||
| 212 | assert(math.approxEq(f32, atan32(0.2), 0.197396, epsilon)); | |
| 213 | assert(math.approxEq(f32, atan32(-0.2), -0.197396, epsilon)); | |
| 214 | assert(math.approxEq(f32, atan32(0.3434), 0.330783, epsilon)); | |
| 215 | assert(math.approxEq(f32, atan32(0.8923), 0.728545, epsilon)); | |
| 216 | assert(math.approxEq(f32, atan32(1.5), 0.982794, epsilon)); | |
| 217 | } | |
| 218 | ||
| 219 | test "atan64" { | |
| 220 | const epsilon = 0.000001; | |
| 221 | ||
| 222 | assert(math.approxEq(f64, atan64(0.2), 0.197396, epsilon)); | |
| 223 | assert(math.approxEq(f64, atan64(-0.2), -0.197396, epsilon)); | |
| 224 | assert(math.approxEq(f64, atan64(0.3434), 0.330783, epsilon)); | |
| 225 | assert(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon)); | |
| 226 | assert(math.approxEq(f64, atan64(1.5), 0.982794, epsilon)); | |
| 227 | } |
std/math/atan2.zig created+214| ... | ... | @@ -0,0 +1,214 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn atan2(comptime T: type, x: T, y: T) -> T { | |
| 5 | switch (T) { | |
| 6 | f32 => @inlineCall(atan2f, x, y), | |
| 7 | f64 => @inlineCall(atan2d, x, y), | |
| 8 | else => @compileError("atan2 not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | fn atan2f(y: f32, x: f32) -> f32 { | |
| 13 | const pi: f32 = 3.1415927410e+00; | |
| 14 | const pi_lo: f32 = -8.7422776573e-08; | |
| 15 | ||
| 16 | if (math.isNan(x) or math.isNan(y)) { | |
| 17 | return x + y; | |
| 18 | } | |
| 19 | ||
| 20 | var ix = @bitCast(u32, x); | |
| 21 | var iy = @bitCast(u32, y); | |
| 22 | ||
| 23 | // x = 1.0 | |
| 24 | if (ix == 0x3F800000) { | |
| 25 | return math.atan(y); | |
| 26 | } | |
| 27 | ||
| 28 | // 2 * sign(x) + sign(y) | |
| 29 | const m = ((iy >> 31) & 1) | ((ix >> 30) & 2); | |
| 30 | ix &= 0x7FFFFFFF; | |
| 31 | iy &= 0x7FFFFFFF; | |
| 32 | ||
| 33 | if (iy == 0) { | |
| 34 | switch (m) { | |
| 35 | 0, 1 => return y, // atan(+-0, +...) | |
| 36 | 2 => return pi, // atan(+0, -...) | |
| 37 | 3 => return -pi, // atan(-0, -...) | |
| 38 | else => unreachable, | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | if (ix == 0) { | |
| 43 | if (m & 1 != 0) { | |
| 44 | return -pi / 2; | |
| 45 | } else { | |
| 46 | return pi / 2; | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | if (ix == 0x7F800000) { | |
| 51 | if (iy == 0x7F800000) { | |
| 52 | switch (m) { | |
| 53 | 0 => return pi / 4, // atan(+inf, +inf) | |
| 54 | 1 => return -pi / 4, // atan(-inf, +inf) | |
| 55 | 2 => return 3*pi / 4, // atan(+inf, -inf) | |
| 56 | 3 => return -3*pi / 4, // atan(-inf, -inf) | |
| 57 | else => unreachable, | |
| 58 | } | |
| 59 | } else { | |
| 60 | switch (m) { | |
| 61 | 0 => return 0.0, // atan(+..., +inf) | |
| 62 | 1 => return -0.0, // atan(-..., +inf) | |
| 63 | 2 => return pi, // atan(+..., -inf) | |
| 64 | 3 => return -pi, // atan(-...f, -inf) | |
| 65 | else => unreachable, | |
| 66 | } | |
| 67 | } | |
| 68 | } | |
| 69 | ||
| 70 | // |y / x| > 0x1p26 | |
| 71 | if (ix + (26 << 23) < iy or iy == 0x7F800000) { | |
| 72 | if (m & 1 != 0) { | |
| 73 | return -pi / 2; | |
| 74 | } else { | |
| 75 | return pi / 2; | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | // z = atan(|y / x|) with correct underflow | |
| 80 | var z = { | |
| 81 | if ((m & 2) != 0 and iy + (26 << 23) < ix) { | |
| 82 | 0.0 | |
| 83 | } else { | |
| 84 | math.atan(math.fabs(y / x)) | |
| 85 | } | |
| 86 | }; | |
| 87 | ||
| 88 | switch (m) { | |
| 89 | 0 => return z, // atan(+, +) | |
| 90 | 1 => return -z, // atan(-, +) | |
| 91 | 2 => return pi - (z - pi_lo), // atan(+, -) | |
| 92 | 3 => return (z - pi_lo) - pi, // atan(-, -) | |
| 93 | else => unreachable, | |
| 94 | } | |
| 95 | } | |
| 96 | ||
| 97 | fn atan2d(y: f64, x: f64) -> f64 { | |
| 98 | const pi: f64 = 3.1415926535897931160E+00; | |
| 99 | const pi_lo: f64 = 1.2246467991473531772E-16; | |
| 100 | ||
| 101 | if (math.isNan(x) or math.isNan(y)) { | |
| 102 | return x + y; | |
| 103 | } | |
| 104 | ||
| 105 | var ux = @bitCast(u64, x); | |
| 106 | var ix = u32(ux >> 32); | |
| 107 | var lx = u32(ux & 0xFFFFFFFF); | |
| 108 | ||
| 109 | var uy = @bitCast(u64, y); | |
| 110 | var iy = u32(uy >> 32); | |
| 111 | var ly = u32(uy & 0xFFFFFFFF); | |
| 112 | ||
| 113 | // x = 1.0 | |
| 114 | if ((ix -% 0x3FF00000) | lx == 0) { | |
| 115 | return math.atan(y); | |
| 116 | } | |
| 117 | ||
| 118 | // 2 * sign(x) + sign(y) | |
| 119 | const m = ((iy >> 31) & 1) | ((ix >> 30) & 2); | |
| 120 | ix &= 0x7FFFFFFF; | |
| 121 | iy &= 0x7FFFFFFF; | |
| 122 | ||
| 123 | if (iy | ly == 0) { | |
| 124 | switch (m) { | |
| 125 | 0, 1 => return y, // atan(+-0, +...) | |
| 126 | 2 => return pi, // atan(+0, -...) | |
| 127 | 3 => return -pi, // atan(-0, -...) | |
| 128 | else => unreachable, | |
| 129 | } | |
| 130 | } | |
| 131 | ||
| 132 | if (ix | lx == 0) { | |
| 133 | if (m & 1 != 0) { | |
| 134 | return -pi / 2; | |
| 135 | } else { | |
| 136 | return pi / 2; | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 140 | if (ix == 0x7FF00000) { | |
| 141 | if (iy == 0x7FF00000) { | |
| 142 | switch (m) { | |
| 143 | 0 => return pi / 4, // atan(+inf, +inf) | |
| 144 | 1 => return -pi / 4, // atan(-inf, +inf) | |
| 145 | 2 => return 3*pi / 4, // atan(+inf, -inf) | |
| 146 | 3 => return -3*pi / 4, // atan(-inf, -inf) | |
| 147 | else => unreachable, | |
| 148 | } | |
| 149 | } else { | |
| 150 | switch (m) { | |
| 151 | 0 => return 0.0, // atan(+..., +inf) | |
| 152 | 1 => return -0.0, // atan(-..., +inf) | |
| 153 | 2 => return pi, // atan(+..., -inf) | |
| 154 | 3 => return -pi, // atan(-...f, -inf) | |
| 155 | else => unreachable, | |
| 156 | } | |
| 157 | } | |
| 158 | } | |
| 159 | ||
| 160 | // |y / x| > 0x1p64 | |
| 161 | if (ix +% (64 << 20) < iy or iy == 0x7FF00000) { | |
| 162 | if (m & 1 != 0) { | |
| 163 | return -pi / 2; | |
| 164 | } else { | |
| 165 | return pi / 2; | |
| 166 | } | |
| 167 | } | |
| 168 | ||
| 169 | // z = atan(|y / x|) with correct underflow | |
| 170 | var z = { | |
| 171 | if ((m & 2) != 0 and iy +% (64 << 20) < ix) { | |
| 172 | 0.0 | |
| 173 | } else { | |
| 174 | math.atan(math.fabs(y / x)) | |
| 175 | } | |
| 176 | }; | |
| 177 | ||
| 178 | switch (m) { | |
| 179 | 0 => return z, // atan(+, +) | |
| 180 | 1 => return -z, // atan(-, +) | |
| 181 | 2 => return pi - (z - pi_lo), // atan(+, -) | |
| 182 | 3 => return (z - pi_lo) - pi, // atan(-, -) | |
| 183 | else => unreachable, | |
| 184 | } | |
| 185 | } | |
| 186 | ||
| 187 | test "atan2" { | |
| 188 | assert(atan2(f32, 0.2, 0.21) == atan2f(0.2, 0.21)); | |
| 189 | assert(atan2(f64, 0.2, 0.21) == atan2d(0.2, 0.21)); | |
| 190 | } | |
| 191 | ||
| 192 | test "atan2f" { | |
| 193 | const epsilon = 0.000001; | |
| 194 | ||
| 195 | assert(math.approxEq(f32, atan2f(0.0, 0.0), 0.0, epsilon)); | |
| 196 | assert(math.approxEq(f32, atan2f(0.2, 0.2), 0.785398, epsilon)); | |
| 197 | assert(math.approxEq(f32, atan2f(-0.2, 0.2), -0.785398, epsilon)); | |
| 198 | assert(math.approxEq(f32, atan2f(0.2, -0.2), 2.356194, epsilon)); | |
| 199 | assert(math.approxEq(f32, atan2f(-0.2, -0.2), -2.356194, epsilon)); | |
| 200 | assert(math.approxEq(f32, atan2f(0.34, -0.4), 2.437099, epsilon)); | |
| 201 | assert(math.approxEq(f32, atan2f(0.34, 1.243), 0.267001, epsilon)); | |
| 202 | } | |
| 203 | ||
| 204 | test "atan2d" { | |
| 205 | const epsilon = 0.000001; | |
| 206 | ||
| 207 | assert(math.approxEq(f64, atan2d(0.0, 0.0), 0.0, epsilon)); | |
| 208 | assert(math.approxEq(f64, atan2d(0.2, 0.2), 0.785398, epsilon)); | |
| 209 | assert(math.approxEq(f64, atan2d(-0.2, 0.2), -0.785398, epsilon)); | |
| 210 | assert(math.approxEq(f64, atan2d(0.2, -0.2), 2.356194, epsilon)); | |
| 211 | assert(math.approxEq(f64, atan2d(-0.2, -0.2), -2.356194, epsilon)); | |
| 212 | assert(math.approxEq(f64, atan2d(0.34, -0.4), 2.437099, epsilon)); | |
| 213 | assert(math.approxEq(f64, atan2d(0.34, 1.243), 0.267001, epsilon)); | |
| 214 | } |
std/math/atanh.zig created+85| ... | ... | @@ -0,0 +1,85 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn atanh(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(atanhf, x), | |
| 8 | f64 => @inlineCall(atanhd, x), | |
| 9 | else => @compileError("atanh not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | // atanh(x) = log((1 + x) / (1 - x)) / 2 = log1p(2x / (1 - x)) / 2 ~= x + x^3 / 3 + o(x^5) | |
| 14 | fn atanhf(x: f32) -> f32 { | |
| 15 | const u = @bitCast(u32, x); | |
| 16 | const i = u & 0x7FFFFFFF; | |
| 17 | const s = u >> 31; | |
| 18 | ||
| 19 | var y = @bitCast(f32, i); // |x| | |
| 20 | ||
| 21 | if (u < 0x3F800000 - (1 << 23)) { | |
| 22 | if (u < 0x3F800000 - (32 << 23)) { | |
| 23 | // underflow | |
| 24 | if (u < (1 << 23)) { | |
| 25 | math.forceEval(y * y) | |
| 26 | } | |
| 27 | } | |
| 28 | // |x| < 0.5 | |
| 29 | else { | |
| 30 | y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y)); | |
| 31 | } | |
| 32 | } else { | |
| 33 | // avoid overflow | |
| 34 | y = 0.5 * math.log1p(2 * (y / (1 - y))); | |
| 35 | } | |
| 36 | ||
| 37 | if (s != 0) -y else y | |
| 38 | } | |
| 39 | ||
| 40 | fn atanhd(x: f64) -> f64 { | |
| 41 | const u = @bitCast(u64, x); | |
| 42 | const e = (u >> 52) & 0x7FF; | |
| 43 | const s = u >> 63; | |
| 44 | ||
| 45 | var y = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x| | |
| 46 | ||
| 47 | if (e < 0x3FF - 1) { | |
| 48 | if (e < 0x3FF - 32) { | |
| 49 | // underflow | |
| 50 | if (e == 0) { | |
| 51 | math.forceEval(f32(y)); | |
| 52 | } | |
| 53 | } | |
| 54 | // |x| < 0.5 | |
| 55 | else { | |
| 56 | y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y)); | |
| 57 | } | |
| 58 | } else { | |
| 59 | // avoid overflow | |
| 60 | y = 0.5 * math.log1p(2 * (y / (1 - y))); | |
| 61 | } | |
| 62 | ||
| 63 | if (s != 0) -y else y | |
| 64 | } | |
| 65 | ||
| 66 | test "atanh" { | |
| 67 | assert(atanh(f32(0.0)) == atanhf(0.0)); | |
| 68 | assert(atanh(f64(0.0)) == atanhd(0.0)); | |
| 69 | } | |
| 70 | ||
| 71 | test "atanhf" { | |
| 72 | const epsilon = 0.000001; | |
| 73 | ||
| 74 | assert(math.approxEq(f32, atanhf(0.0), 0.0, epsilon)); | |
| 75 | assert(math.approxEq(f32, atanhf(0.2), 0.202733, epsilon)); | |
| 76 | assert(math.approxEq(f32, atanhf(0.8923), 1.433099, epsilon)); | |
| 77 | } | |
| 78 | ||
| 79 | test "atanhd" { | |
| 80 | const epsilon = 0.000001; | |
| 81 | ||
| 82 | assert(math.approxEq(f64, atanhd(0.0), 0.0, epsilon)); | |
| 83 | assert(math.approxEq(f64, atanhd(0.2), 0.202733, epsilon)); | |
| 84 | assert(math.approxEq(f64, atanhd(0.8923), 1.433099, epsilon)); | |
| 85 | } |
std/math/cbrt.zig created+134| ... | ... | @@ -0,0 +1,134 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn cbrt(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(cbrt32, x), | |
| 8 | f64 => @inlineCall(cbrt64, x), | |
| 9 | else => @compileError("cbrt not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn cbrt32(x: f32) -> f32 { | |
| 14 | const B1: u32 = 709958130; // (127 - 127.0 / 3 - 0.03306235651) * 2^23 | |
| 15 | const B2: u32 = 642849266; // (127 - 127.0 / 3 - 24 / 3 - 0.03306235651) * 2^23 | |
| 16 | ||
| 17 | var u = @bitCast(u32, x); | |
| 18 | var hx = u & 0x7FFFFFFF; | |
| 19 | ||
| 20 | // cbrt(nan, inf) = itself | |
| 21 | if (hx >= 0x7F800000) { | |
| 22 | return x + x; | |
| 23 | } | |
| 24 | ||
| 25 | // cbrt to ~5bits | |
| 26 | if (hx < 0x00800000) { | |
| 27 | // cbrt(+-0) = itself | |
| 28 | if (hx == 0) { | |
| 29 | return x; | |
| 30 | } | |
| 31 | u = @bitCast(u32, x * 0x1.0p24); | |
| 32 | hx = u & 0x7FFFFFFF; | |
| 33 | hx = hx / 3 + B2; | |
| 34 | } else { | |
| 35 | hx = hx / 3 + B1; | |
| 36 | } | |
| 37 | ||
| 38 | u &= 0x80000000; | |
| 39 | u |= hx; | |
| 40 | ||
| 41 | // first step newton to 16 bits | |
| 42 | var t: f64 = @bitCast(f32, u); | |
| 43 | var r: f64 = t * t * t; | |
| 44 | t = t * (f64(x) + x + r) / (x + r + r); | |
| 45 | ||
| 46 | // second step newton to 47 bits | |
| 47 | r = t * t * t; | |
| 48 | t = t * (f64(x) + x + r) / (x + r + r); | |
| 49 | ||
| 50 | f32(t) | |
| 51 | } | |
| 52 | ||
| 53 | fn cbrt64(x: f64) -> f64 { | |
| 54 | const B1: u32 = 715094163; // (1023 - 1023 / 3 - 0.03306235651 * 2^20 | |
| 55 | const B2: u32 = 696219795; // (1023 - 1023 / 3 - 54 / 3 - 0.03306235651 * 2^20 | |
| 56 | ||
| 57 | // |1 / cbrt(x) - p(x)| < 2^(23.5) | |
| 58 | const P0: f64 = 1.87595182427177009643; | |
| 59 | const P1: f64 = -1.88497979543377169875; | |
| 60 | const P2: f64 = 1.621429720105354466140; | |
| 61 | const P3: f64 = -0.758397934778766047437; | |
| 62 | const P4: f64 = 0.145996192886612446982; | |
| 63 | ||
| 64 | var u = @bitCast(u64, x); | |
| 65 | var hx = u32(u >> 32) & 0x7FFFFFFF; | |
| 66 | ||
| 67 | // cbrt(nan, inf) = itself | |
| 68 | if (hx >= 0x7FF00000) { | |
| 69 | return x + x; | |
| 70 | } | |
| 71 | ||
| 72 | // cbrt to ~5bits | |
| 73 | if (hx < 0x00100000) { | |
| 74 | u = @bitCast(u64, x * 0x1.0p54); | |
| 75 | hx = u32(u >> 32) & 0x7FFFFFFF; | |
| 76 | ||
| 77 | // cbrt(0) is itself | |
| 78 | if (hx == 0) { | |
| 79 | return 0; | |
| 80 | } | |
| 81 | hx = hx / 3 + B2; | |
| 82 | } else { | |
| 83 | hx = hx / 3 + B1; | |
| 84 | } | |
| 85 | ||
| 86 | u &= 1 << 63; | |
| 87 | u |= u64(hx) << 32; | |
| 88 | var t = @bitCast(f64, u); | |
| 89 | ||
| 90 | // cbrt to 23 bits | |
| 91 | // cbrt(x) = t * cbrt(x / t^3) ~= t * P(t^3 / x) | |
| 92 | var r = (t * t) * (t / x); | |
| 93 | t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4)); | |
| 94 | ||
| 95 | // Round t away from 0 to 23 bits | |
| 96 | u = @bitCast(u64, t); | |
| 97 | u = (u + 0x80000000) & 0xFFFFFFFFC0000000; | |
| 98 | t = @bitCast(f64, u); | |
| 99 | ||
| 100 | // one step newton to 53 bits | |
| 101 | const s = t * t; | |
| 102 | var q = x / s; | |
| 103 | var w = t + t; | |
| 104 | q = (q - t) / (w + q); | |
| 105 | ||
| 106 | t + t * q | |
| 107 | } | |
| 108 | ||
| 109 | test "cbrt" { | |
| 110 | assert(cbrt(f32(0.0)) == cbrt32(0.0)); | |
| 111 | assert(cbrt(f64(0.0)) == cbrt64(0.0)); | |
| 112 | } | |
| 113 | ||
| 114 | test "cbrt32" { | |
| 115 | const epsilon = 0.000001; | |
| 116 | ||
| 117 | assert(cbrt32(0.0) == 0.0); | |
| 118 | assert(math.approxEq(f32, cbrt32(0.2), 0.584804, epsilon)); | |
| 119 | assert(math.approxEq(f32, cbrt32(0.8923), 0.962728, epsilon)); | |
| 120 | assert(math.approxEq(f32, cbrt32(1.5), 1.144714, epsilon)); | |
| 121 | assert(math.approxEq(f32, cbrt32(37.45), 3.345676, epsilon)); | |
| 122 | assert(math.approxEq(f32, cbrt32(123123.234375), 49.748501, epsilon)); | |
| 123 | } | |
| 124 | ||
| 125 | test "cbrt64" { | |
| 126 | const epsilon = 0.000001; | |
| 127 | ||
| 128 | assert(cbrt64(0.0) == 0.0); | |
| 129 | assert(math.approxEq(f64, cbrt64(0.2), 0.584804, epsilon)); | |
| 130 | assert(math.approxEq(f64, cbrt64(0.8923), 0.962728, epsilon)); | |
| 131 | assert(math.approxEq(f64, cbrt64(1.5), 1.144714, epsilon)); | |
| 132 | assert(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon)); | |
| 133 | assert(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon)); | |
| 134 | } |
std/math/ceil.zig created+89| ... | ... | @@ -0,0 +1,89 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const math = @import("index.zig"); | |
| 3 | const assert = @import("../debug.zig").assert; | |
| 4 | ||
| 5 | pub fn ceil(x: var) -> @typeOf(x) { | |
| 6 | const T = @typeOf(x); | |
| 7 | switch (T) { | |
| 8 | f32 => @inlineCall(ceil32, x), | |
| 9 | f64 => @inlineCall(ceil64, x), | |
| 10 | else => @compileError("ceil not implemented for " ++ @typeName(T)), | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | fn ceil32(x: f32) -> f32 { | |
| 15 | var u = @bitCast(u32, x); | |
| 16 | var e = i32((u >> 23) & 0xFF) - 0x7F; | |
| 17 | var m: u32 = undefined; | |
| 18 | ||
| 19 | if (e >= 23) { | |
| 20 | return x; | |
| 21 | } | |
| 22 | else if (e >= 0) { | |
| 23 | m = 0x007FFFFF >> u32(e); | |
| 24 | if (u & m == 0) { | |
| 25 | return x; | |
| 26 | } | |
| 27 | math.forceEval(x + 0x1.0p120); | |
| 28 | if (u >> 31 == 0) { | |
| 29 | u += m; | |
| 30 | } | |
| 31 | u &= ~m; | |
| 32 | @bitCast(f32, u) | |
| 33 | } else { | |
| 34 | math.forceEval(x + 0x1.0p120); | |
| 35 | if (u >> 31 != 0) { | |
| 36 | return -0.0; | |
| 37 | } else { | |
| 38 | 1.0 | |
| 39 | } | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | fn ceil64(x: f64) -> f64 { | |
| 44 | const u = @bitCast(u64, x); | |
| 45 | const e = (u >> 52) & 0x7FF; | |
| 46 | var y: f64 = undefined; | |
| 47 | ||
| 48 | if (e >= 0x3FF+52 or x == 0) { | |
| 49 | return x; | |
| 50 | } | |
| 51 | ||
| 52 | if (u >> 63 != 0) { | |
| 53 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 54 | y = x - math.f64_toint + math.f64_toint - x; | |
| 55 | } else { | |
| 56 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 57 | y = x + math.f64_toint - math.f64_toint - x; | |
| 58 | } | |
| 59 | ||
| 60 | if (e <= 0x3FF-1) { | |
| 61 | math.forceEval(y); | |
| 62 | if (u >> 63 != 0) { | |
| 63 | return -0.0; // Compiler requires return. | |
| 64 | } else { | |
| 65 | 1.0 | |
| 66 | } | |
| 67 | } else if (y < 0) { | |
| 68 | x + y + 1 | |
| 69 | } else { | |
| 70 | x + y | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | test "ceil" { | |
| 75 | assert(ceil(f32(0.0)) == ceil32(0.0)); | |
| 76 | assert(ceil(f64(0.0)) == ceil64(0.0)); | |
| 77 | } | |
| 78 | ||
| 79 | test "ceil32" { | |
| 80 | assert(ceil32(1.3) == 2.0); | |
| 81 | assert(ceil32(-1.3) == -1.0); | |
| 82 | assert(ceil32(0.2) == 1.0); | |
| 83 | } | |
| 84 | ||
| 85 | test "ceil64" { | |
| 86 | assert(ceil64(1.3) == 2.0); | |
| 87 | assert(ceil64(-1.3) == -1.0); | |
| 88 | assert(ceil64(0.2) == 1.0); | |
| 89 | } |
std/math/copysign.zig created+47| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn copysign(comptime T: type, x: T, y: T) -> T { | |
| 5 | switch (T) { | |
| 6 | f32 => @inlineCall(copysign32, x, y), | |
| 7 | f64 => @inlineCall(copysign64, x, y), | |
| 8 | else => @compileError("copysign not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | fn copysign32(x: f32, y: f32) -> f32 { | |
| 13 | const ux = @bitCast(u32, x); | |
| 14 | const uy = @bitCast(u32, y); | |
| 15 | ||
| 16 | const h1 = ux & (@maxValue(u32) / 2); | |
| 17 | const h2 = uy & (u32(1) << 31); | |
| 18 | @bitCast(f32, h1 | h2) | |
| 19 | } | |
| 20 | ||
| 21 | fn copysign64(x: f64, y: f64) -> f64 { | |
| 22 | const ux = @bitCast(u64, x); | |
| 23 | const uy = @bitCast(u64, y); | |
| 24 | ||
| 25 | const h1 = ux & (@maxValue(u64) / 2); | |
| 26 | const h2 = uy & (u64(1) << 63); | |
| 27 | @bitCast(f64, h1 | h2) | |
| 28 | } | |
| 29 | ||
| 30 | test "copysign" { | |
| 31 | assert(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0)); | |
| 32 | assert(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0)); | |
| 33 | } | |
| 34 | ||
| 35 | test "copysign32" { | |
| 36 | assert(copysign32(5.0, 1.0) == 5.0); | |
| 37 | assert(copysign32(5.0, -1.0) == -5.0); | |
| 38 | assert(copysign32(-5.0, -1.0) == -5.0); | |
| 39 | assert(copysign32(-5.0, 1.0) == 5.0); | |
| 40 | } | |
| 41 | ||
| 42 | test "copysign64" { | |
| 43 | assert(copysign64(5.0, 1.0) == 5.0); | |
| 44 | assert(copysign64(5.0, -1.0) == -5.0); | |
| 45 | assert(copysign64(-5.0, -1.0) == -5.0); | |
| 46 | assert(copysign64(-5.0, 1.0) == 5.0); | |
| 47 | } |
std/math/cos.zig created+159| ... | ... | @@ -0,0 +1,159 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn cos(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(cos32, x), | |
| 8 | f64 => @inlineCall(cos64, x), | |
| 9 | else => @compileError("cos not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | // sin polynomial coefficients | |
| 14 | const S0 = 1.58962301576546568060E-10; | |
| 15 | const S1 = -2.50507477628578072866E-8; | |
| 16 | const S2 = 2.75573136213857245213E-6; | |
| 17 | const S3 = -1.98412698295895385996E-4; | |
| 18 | const S4 = 8.33333333332211858878E-3; | |
| 19 | const S5 = -1.66666666666666307295E-1; | |
| 20 | ||
| 21 | // cos polynomial coeffiecients | |
| 22 | const C0 = -1.13585365213876817300E-11; | |
| 23 | const C1 = 2.08757008419747316778E-9; | |
| 24 | const C2 = -2.75573141792967388112E-7; | |
| 25 | const C3 = 2.48015872888517045348E-5; | |
| 26 | const C4 = -1.38888888888730564116E-3; | |
| 27 | const C5 = 4.16666666666665929218E-2; | |
| 28 | ||
| 29 | // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. | |
| 30 | // | |
| 31 | // This may have slight differences on some edge cases and may need to replaced if so. | |
| 32 | fn cos32(x_: f32) -> f32 { | |
| 33 | const pi4a = 7.85398125648498535156e-1; | |
| 34 | const pi4b = 3.77489470793079817668E-8; | |
| 35 | const pi4c = 2.69515142907905952645E-15; | |
| 36 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 37 | ||
| 38 | var x = x_; | |
| 39 | if (math.isNan(x) or math.isInf(x)) { | |
| 40 | return math.nan(f32); | |
| 41 | } | |
| 42 | ||
| 43 | var sign = false; | |
| 44 | if (x < 0) { | |
| 45 | x = -x; | |
| 46 | } | |
| 47 | ||
| 48 | var y = math.floor(x * m4pi); | |
| 49 | var j = i64(y); | |
| 50 | ||
| 51 | if (j & 1 == 1) { | |
| 52 | j += 1; | |
| 53 | y += 1; | |
| 54 | } | |
| 55 | ||
| 56 | j &= 7; | |
| 57 | if (j > 3) { | |
| 58 | j -= 4; | |
| 59 | sign = !sign; | |
| 60 | } | |
| 61 | if (j > 1) { | |
| 62 | sign = !sign; | |
| 63 | } | |
| 64 | ||
| 65 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 66 | const w = z * z; | |
| 67 | ||
| 68 | const r = { | |
| 69 | if (j == 1 or j == 2) { | |
| 70 | z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))) | |
| 71 | } else { | |
| 72 | 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))) | |
| 73 | } | |
| 74 | }; | |
| 75 | ||
| 76 | if (sign) { | |
| 77 | -r | |
| 78 | } else { | |
| 79 | r | |
| 80 | } | |
| 81 | } | |
| 82 | ||
| 83 | fn cos64(x_: f64) -> f64 { | |
| 84 | const pi4a = 7.85398125648498535156e-1; | |
| 85 | const pi4b = 3.77489470793079817668E-8; | |
| 86 | const pi4c = 2.69515142907905952645E-15; | |
| 87 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 88 | ||
| 89 | var x = x_; | |
| 90 | if (math.isNan(x) or math.isInf(x)) { | |
| 91 | return math.nan(f64); | |
| 92 | } | |
| 93 | ||
| 94 | var sign = false; | |
| 95 | if (x < 0) { | |
| 96 | x = -x; | |
| 97 | } | |
| 98 | ||
| 99 | var y = math.floor(x * m4pi); | |
| 100 | var j = i64(y); | |
| 101 | ||
| 102 | if (j & 1 == 1) { | |
| 103 | j += 1; | |
| 104 | y += 1; | |
| 105 | } | |
| 106 | ||
| 107 | j &= 7; | |
| 108 | if (j > 3) { | |
| 109 | j -= 4; | |
| 110 | sign = !sign; | |
| 111 | } | |
| 112 | if (j > 1) { | |
| 113 | sign = !sign; | |
| 114 | } | |
| 115 | ||
| 116 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 117 | const w = z * z; | |
| 118 | ||
| 119 | const r = { | |
| 120 | if (j == 1 or j == 2) { | |
| 121 | z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))) | |
| 122 | } else { | |
| 123 | 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))) | |
| 124 | } | |
| 125 | }; | |
| 126 | ||
| 127 | if (sign) { | |
| 128 | -r | |
| 129 | } else { | |
| 130 | r | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | test "cos" { | |
| 135 | assert(cos(f32(0.0)) == cos32(0.0)); | |
| 136 | assert(cos(f64(0.0)) == cos64(0.0)); | |
| 137 | } | |
| 138 | ||
| 139 | test "cos32" { | |
| 140 | const epsilon = 0.000001; | |
| 141 | ||
| 142 | assert(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); | |
| 143 | assert(math.approxEq(f32, cos32(0.2), 0.980067, epsilon)); | |
| 144 | assert(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon)); | |
| 145 | assert(math.approxEq(f32, cos32(1.5), 0.070737, epsilon)); | |
| 146 | assert(math.approxEq(f32, cos32(37.45), 0.969132, epsilon)); | |
| 147 | assert(math.approxEq(f32, cos32(89.123), 0.400798, epsilon)); | |
| 148 | } | |
| 149 | ||
| 150 | test "cos64" { | |
| 151 | const epsilon = 0.000001; | |
| 152 | ||
| 153 | assert(math.approxEq(f64, cos64(0.0), 1.0, epsilon)); | |
| 154 | assert(math.approxEq(f64, cos64(0.2), 0.980067, epsilon)); | |
| 155 | assert(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon)); | |
| 156 | assert(math.approxEq(f64, cos64(1.5), 0.070737, epsilon)); | |
| 157 | assert(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); | |
| 158 | assert(math.approxEq(f64, cos64(89.123), 0.40080, epsilon)); | |
| 159 | } |
std/math/cosh.zig created+91| ... | ... | @@ -0,0 +1,91 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const expo2 = @import("_expo2.zig").expo2; | |
| 3 | const assert = @import("../debug.zig").assert; | |
| 4 | ||
| 5 | pub fn cosh(x: var) -> @typeOf(x) { | |
| 6 | const T = @typeOf(x); | |
| 7 | switch (T) { | |
| 8 | f32 => @inlineCall(coshf, x), | |
| 9 | f64 => @inlineCall(coshd, x), | |
| 10 | else => @compileError("cosh not implemented for " ++ @typeName(T)), | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | // cosh(x) = (exp(x) + 1 / exp(x)) / 2 | |
| 15 | // = 1 + 0.5 * (exp(x) - 1) * (exp(x) - 1) / exp(x) | |
| 16 | // = 1 + (x * x) / 2 + o(x^4) | |
| 17 | fn coshf(x: f32) -> f32 { | |
| 18 | const u = @bitCast(u32, x); | |
| 19 | const ux = u & 0x7FFFFFFF; | |
| 20 | const ax = @bitCast(f32, ux); | |
| 21 | ||
| 22 | // |x| < log(2) | |
| 23 | if (ux < 0x3F317217) { | |
| 24 | if (ux < 0x3F800000 - (12 << 23)) { | |
| 25 | math.raiseOverflow(); | |
| 26 | return 1.0; | |
| 27 | } | |
| 28 | const t = math.expm1(ax); | |
| 29 | return 1 + t * t / (2 * (1 + t)); | |
| 30 | } | |
| 31 | ||
| 32 | // |x| < log(FLT_MAX) | |
| 33 | if (ux < 0x42B17217) { | |
| 34 | const t = math.exp(ax); | |
| 35 | return 0.5 * (t + 1 / t); | |
| 36 | } | |
| 37 | ||
| 38 | // |x| > log(FLT_MAX) or nan | |
| 39 | expo2(ax) | |
| 40 | } | |
| 41 | ||
| 42 | fn coshd(x: f64) -> f64 { | |
| 43 | const u = @bitCast(u64, x); | |
| 44 | const w = u32(u >> 32); | |
| 45 | const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); | |
| 46 | ||
| 47 | // |x| < log(2) | |
| 48 | if (w < 0x3FE62E42) { | |
| 49 | if (w < 0x3FF00000 - (26 << 20)) { | |
| 50 | if (x != 0) { | |
| 51 | math.raiseInexact(); | |
| 52 | } | |
| 53 | return 1.0; | |
| 54 | } | |
| 55 | const t = math.expm1(ax); | |
| 56 | return 1 + t * t / (2 * (1 + t)); | |
| 57 | } | |
| 58 | ||
| 59 | // |x| < log(DBL_MAX) | |
| 60 | if (w < 0x40862E42) { | |
| 61 | const t = math.exp(ax); | |
| 62 | // NOTE: If x > log(0x1p26) then 1/t is not required. | |
| 63 | return 0.5 * (t + 1 / t); | |
| 64 | } | |
| 65 | ||
| 66 | // |x| > log(CBL_MAX) or nan | |
| 67 | expo2(ax) | |
| 68 | } | |
| 69 | ||
| 70 | test "cosh" { | |
| 71 | assert(cosh(f32(1.5)) == coshf(1.5)); | |
| 72 | assert(cosh(f64(1.5)) == coshd(1.5)); | |
| 73 | } | |
| 74 | ||
| 75 | test "coshf" { | |
| 76 | const epsilon = 0.000001; | |
| 77 | ||
| 78 | assert(math.approxEq(f32, coshf(0.0), 1.0, epsilon)); | |
| 79 | assert(math.approxEq(f32, coshf(0.2), 1.020067, epsilon)); | |
| 80 | assert(math.approxEq(f32, coshf(0.8923), 1.425225, epsilon)); | |
| 81 | assert(math.approxEq(f32, coshf(1.5), 2.352410, epsilon)); | |
| 82 | } | |
| 83 | ||
| 84 | test "coshd" { | |
| 85 | const epsilon = 0.000001; | |
| 86 | ||
| 87 | assert(math.approxEq(f64, coshd(0.0), 1.0, epsilon)); | |
| 88 | assert(math.approxEq(f64, coshd(0.2), 1.020067, epsilon)); | |
| 89 | assert(math.approxEq(f64, coshd(0.8923), 1.425225, epsilon)); | |
| 90 | assert(math.approxEq(f64, coshd(1.5), 2.352410, epsilon)); | |
| 91 | } |
std/math/exp.zig created+191| ... | ... | @@ -0,0 +1,191 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn exp(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(exp32, x), | |
| 8 | f64 => @inlineCall(exp64, x), | |
| 9 | else => @compileError("exp not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn exp32(x_: f32) -> f32 { | |
| 14 | const half = []const f32 { 0.5, -0.5 }; | |
| 15 | const ln2hi = 6.9314575195e-1; | |
| 16 | const ln2lo = 1.4286067653e-6; | |
| 17 | const invln2 = 1.4426950216e+0; | |
| 18 | const P1 = 1.6666625440e-1; | |
| 19 | const P2 = -2.7667332906e-3; | |
| 20 | ||
| 21 | var x = x_; | |
| 22 | var hx = @bitCast(u32, x); | |
| 23 | const sign = i32(hx >> 31); | |
| 24 | hx &= 0x7FFFFFFF; | |
| 25 | ||
| 26 | // |x| >= -87.33655 or nan | |
| 27 | if (hx >= 0x42AEAC50) { | |
| 28 | // nan | |
| 29 | if (hx > 0x7F800000) { | |
| 30 | return x; | |
| 31 | } | |
| 32 | // x >= 88.722839 | |
| 33 | if (hx >= 0x42b17218 and sign == 0) { | |
| 34 | return x * 0x1.0p127; | |
| 35 | } | |
| 36 | if (sign != 0) { | |
| 37 | math.forceEval(-0x1.0p-149 / x); // overflow | |
| 38 | // x <= -103.972084 | |
| 39 | if (hx >= 0x42CFF1B5) { | |
| 40 | return 0; | |
| 41 | } | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | var k: i32 = undefined; | |
| 46 | var hi: f32 = undefined; | |
| 47 | var lo: f32 = undefined; | |
| 48 | ||
| 49 | // |x| > 0.5 * ln2 | |
| 50 | if (hx > 0x3EB17218) { | |
| 51 | // |x| > 1.5 * ln2 | |
| 52 | if (hx > 0x3F851592) { | |
| 53 | k = i32(invln2 * x + half[usize(sign)]); | |
| 54 | } | |
| 55 | else { | |
| 56 | k = 1 - sign - sign; | |
| 57 | } | |
| 58 | ||
| 59 | const fk = f32(k); | |
| 60 | hi = x - fk * ln2hi; | |
| 61 | lo = fk * ln2lo; | |
| 62 | x = hi - lo; | |
| 63 | } | |
| 64 | // |x| > 2^(-14) | |
| 65 | else if (hx > 0x39000000) { | |
| 66 | k = 0; | |
| 67 | hi = x; | |
| 68 | lo = 0; | |
| 69 | } | |
| 70 | else { | |
| 71 | math.forceEval(0x1.0p127 + x); // inexact | |
| 72 | return 1 + x; | |
| 73 | } | |
| 74 | ||
| 75 | const xx = x * x; | |
| 76 | const c = x - xx * (P1 + xx * P2); | |
| 77 | const y = 1 + (x * c / (2 - c) - lo + hi); | |
| 78 | ||
| 79 | if (k == 0) { | |
| 80 | y | |
| 81 | } else { | |
| 82 | math.scalbn(y, k) | |
| 83 | } | |
| 84 | } | |
| 85 | ||
| 86 | fn exp64(x_: f64) -> f64 { | |
| 87 | const half = []const f64 { 0.5, -0.5 }; | |
| 88 | const ln2hi: f64 = 6.93147180369123816490e-01; | |
| 89 | const ln2lo: f64 = 1.90821492927058770002e-10; | |
| 90 | const invln2: f64 = 1.44269504088896338700e+00; | |
| 91 | const P1: f64 = 1.66666666666666019037e-01; | |
| 92 | const P2: f64 = -2.77777777770155933842e-03; | |
| 93 | const P3: f64 = 6.61375632143793436117e-05; | |
| 94 | const P4: f64 = -1.65339022054652515390e-06; | |
| 95 | const P5: f64 = 4.13813679705723846039e-08; | |
| 96 | ||
| 97 | var x = x_; | |
| 98 | var ux = @bitCast(u64, x); | |
| 99 | var hx = ux >> 32; | |
| 100 | const sign = i32(hx >> 31); | |
| 101 | hx &= 0x7FFFFFFF; | |
| 102 | ||
| 103 | // |x| >= 708.39 or nan | |
| 104 | if (hx >= 0x4086232B) { | |
| 105 | // nan | |
| 106 | if (hx > 0x7FF00000) { | |
| 107 | return x; | |
| 108 | } | |
| 109 | if (x > 709.782712893383973096) { | |
| 110 | // overflow if x != inf | |
| 111 | if (!math.isInf(x)) { | |
| 112 | math.raiseOverflow(); | |
| 113 | } | |
| 114 | return math.inf(f64); | |
| 115 | } | |
| 116 | if (x < -708.39641853226410622) { | |
| 117 | // underflow if x != -inf | |
| 118 | // math.forceEval(f32(-0x1.0p-149 / x)); | |
| 119 | if (x < -745.13321910194110842) { | |
| 120 | return 0; | |
| 121 | } | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | // argument reduction | |
| 126 | var k: i32 = undefined; | |
| 127 | var hi: f64 = undefined; | |
| 128 | var lo: f64 = undefined; | |
| 129 | ||
| 130 | // |x| > 0.5 * ln2 | |
| 131 | if (hx > 0x3EB17218) { | |
| 132 | // |x| >= 1.5 * ln2 | |
| 133 | if (hx > 0x3FF0A2B2) { | |
| 134 | k = i32(invln2 * x + half[usize(sign)]); | |
| 135 | } | |
| 136 | else { | |
| 137 | k = 1 - sign - sign; | |
| 138 | } | |
| 139 | ||
| 140 | const dk = f64(k); | |
| 141 | hi = x - dk * ln2hi; | |
| 142 | lo = dk * ln2lo; | |
| 143 | x = hi - lo; | |
| 144 | } | |
| 145 | // |x| > 2^(-28) | |
| 146 | else if (hx > 0x3E300000) { | |
| 147 | k = 0; | |
| 148 | hi = x; | |
| 149 | lo = 0; | |
| 150 | } | |
| 151 | else { | |
| 152 | // inexact if x != 0 | |
| 153 | // math.forceEval(0x1.0p1023 + x); | |
| 154 | return 1 + x; | |
| 155 | } | |
| 156 | ||
| 157 | const xx = x * x; | |
| 158 | const c = x - xx * (P1 + xx * (P2 + xx * (P3 + xx * (P4 + xx * P5)))); | |
| 159 | const y = 1 + (x * c / (2 - c) - lo + hi); | |
| 160 | ||
| 161 | if (k == 0) { | |
| 162 | y | |
| 163 | } else { | |
| 164 | math.scalbn(y, k) | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | test "exp" { | |
| 169 | assert(exp(f32(0.0)) == exp32(0.0)); | |
| 170 | assert(exp(f64(0.0)) == exp64(0.0)); | |
| 171 | } | |
| 172 | ||
| 173 | test "exp32" { | |
| 174 | const epsilon = 0.000001; | |
| 175 | ||
| 176 | assert(exp32(0.0) == 1.0); | |
| 177 | assert(math.approxEq(f32, exp32(0.0), 1.0, epsilon)); | |
| 178 | assert(math.approxEq(f32, exp32(0.2), 1.221403, epsilon)); | |
| 179 | assert(math.approxEq(f32, exp32(0.8923), 2.440737, epsilon)); | |
| 180 | assert(math.approxEq(f32, exp32(1.5), 4.481689, epsilon)); | |
| 181 | } | |
| 182 | ||
| 183 | test "exp64" { | |
| 184 | const epsilon = 0.000001; | |
| 185 | ||
| 186 | assert(exp64(0.0) == 1.0); | |
| 187 | assert(math.approxEq(f64, exp64(0.0), 1.0, epsilon)); | |
| 188 | assert(math.approxEq(f64, exp64(0.2), 1.221403, epsilon)); | |
| 189 | assert(math.approxEq(f64, exp64(0.8923), 2.440737, epsilon)); | |
| 190 | assert(math.approxEq(f64, exp64(1.5), 4.481689, epsilon)); | |
| 191 | } |
std/math/exp2.zig created+429| ... | ... | @@ -0,0 +1,429 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn exp2(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(exp2f, x), | |
| 8 | f64 => @inlineCall(exp2d, x), | |
| 9 | else => @compileError("exp2 not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | const exp2ft = []const f64 { | |
| 14 | 0x1.6a09e667f3bcdp-1, | |
| 15 | 0x1.7a11473eb0187p-1, | |
| 16 | 0x1.8ace5422aa0dbp-1, | |
| 17 | 0x1.9c49182a3f090p-1, | |
| 18 | 0x1.ae89f995ad3adp-1, | |
| 19 | 0x1.c199bdd85529cp-1, | |
| 20 | 0x1.d5818dcfba487p-1, | |
| 21 | 0x1.ea4afa2a490dap-1, | |
| 22 | 0x1.0000000000000p+0, | |
| 23 | 0x1.0b5586cf9890fp+0, | |
| 24 | 0x1.172b83c7d517bp+0, | |
| 25 | 0x1.2387a6e756238p+0, | |
| 26 | 0x1.306fe0a31b715p+0, | |
| 27 | 0x1.3dea64c123422p+0, | |
| 28 | 0x1.4bfdad5362a27p+0, | |
| 29 | 0x1.5ab07dd485429p+0, | |
| 30 | }; | |
| 31 | ||
| 32 | fn exp2f(x: f32) -> f32 { | |
| 33 | const tblsiz = u32(exp2ft.len); | |
| 34 | const redux: f32 = 0x1.8p23 / f32(tblsiz); | |
| 35 | const P1: f32 = 0x1.62e430p-1; | |
| 36 | const P2: f32 = 0x1.ebfbe0p-3; | |
| 37 | const P3: f32 = 0x1.c6b348p-5; | |
| 38 | const P4: f32 = 0x1.3b2c9cp-7; | |
| 39 | ||
| 40 | var u = @bitCast(u32, x); | |
| 41 | const ix = u & 0x7FFFFFFF; | |
| 42 | ||
| 43 | // |x| > 126 | |
| 44 | if (ix > 0x42FC0000) { | |
| 45 | // nan | |
| 46 | if (ix > 0x7F800000) { | |
| 47 | return x; | |
| 48 | } | |
| 49 | // x >= 128 | |
| 50 | if (u >= 0x43000000 and u < 0x80000000) { | |
| 51 | return x * 0x1.0p127; | |
| 52 | } | |
| 53 | // x < -126 | |
| 54 | if (u >= 0x80000000) { | |
| 55 | if (u >= 0xC3160000 or u & 0x000FFFF != 0) { | |
| 56 | math.forceEval(-0x1.0p-149 / x); | |
| 57 | } | |
| 58 | // x <= -150 | |
| 59 | if (u >= 0x3160000) { | |
| 60 | return 0; | |
| 61 | } | |
| 62 | } | |
| 63 | } | |
| 64 | // |x| <= 0x1p-25 | |
| 65 | else if (ix <= 0x33000000) { | |
| 66 | return 1.0 + x; | |
| 67 | } | |
| 68 | ||
| 69 | var uf = x + redux; | |
| 70 | var i0 = @bitCast(u32, uf); | |
| 71 | i0 += tblsiz / 2; | |
| 72 | ||
| 73 | const k = i0 / tblsiz; | |
| 74 | // NOTE: musl relies on undefined overflow shift behaviour. Appears that this produces the | |
| 75 | // intended result but should confirm how GCC/Clang handle this to ensure. | |
| 76 | const uk = @bitCast(f64, u64(0x3FF + k) <<% 52); | |
| 77 | i0 &= tblsiz - 1; | |
| 78 | uf -= redux; | |
| 79 | ||
| 80 | const z: f64 = x - uf; | |
| 81 | var r: f64 = exp2ft[i0]; | |
| 82 | const t: f64 = r * z; | |
| 83 | r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4); | |
| 84 | f32(r * uk) | |
| 85 | } | |
| 86 | ||
| 87 | const exp2dt = []f64 { | |
| 88 | // exp2(z + eps) eps | |
| 89 | 0x1.6a09e667f3d5dp-1, 0x1.9880p-44, | |
| 90 | 0x1.6b052fa751744p-1, 0x1.8000p-50, | |
| 91 | 0x1.6c012750bd9fep-1, -0x1.8780p-45, | |
| 92 | 0x1.6cfdcddd476bfp-1, 0x1.ec00p-46, | |
| 93 | 0x1.6dfb23c651a29p-1, -0x1.8000p-50, | |
| 94 | 0x1.6ef9298593ae3p-1, -0x1.c000p-52, | |
| 95 | 0x1.6ff7df9519386p-1, -0x1.fd80p-45, | |
| 96 | 0x1.70f7466f42da3p-1, -0x1.c880p-45, | |
| 97 | 0x1.71f75e8ec5fc3p-1, 0x1.3c00p-46, | |
| 98 | 0x1.72f8286eacf05p-1, -0x1.8300p-44, | |
| 99 | 0x1.73f9a48a58152p-1, -0x1.0c00p-47, | |
| 100 | 0x1.74fbd35d7ccfcp-1, 0x1.f880p-45, | |
| 101 | 0x1.75feb564267f1p-1, 0x1.3e00p-47, | |
| 102 | 0x1.77024b1ab6d48p-1, -0x1.7d00p-45, | |
| 103 | 0x1.780694fde5d38p-1, -0x1.d000p-50, | |
| 104 | 0x1.790b938ac1d00p-1, 0x1.3000p-49, | |
| 105 | 0x1.7a11473eb0178p-1, -0x1.d000p-49, | |
| 106 | 0x1.7b17b0976d060p-1, 0x1.0400p-45, | |
| 107 | 0x1.7c1ed0130c133p-1, 0x1.0000p-53, | |
| 108 | 0x1.7d26a62ff8636p-1, -0x1.6900p-45, | |
| 109 | 0x1.7e2f336cf4e3bp-1, -0x1.2e00p-47, | |
| 110 | 0x1.7f3878491c3e8p-1, -0x1.4580p-45, | |
| 111 | 0x1.80427543e1b4ep-1, 0x1.3000p-44, | |
| 112 | 0x1.814d2add1071ap-1, 0x1.f000p-47, | |
| 113 | 0x1.82589994ccd7ep-1, -0x1.1c00p-45, | |
| 114 | 0x1.8364c1eb942d0p-1, 0x1.9d00p-45, | |
| 115 | 0x1.8471a4623cab5p-1, 0x1.7100p-43, | |
| 116 | 0x1.857f4179f5bbcp-1, 0x1.2600p-45, | |
| 117 | 0x1.868d99b4491afp-1, -0x1.2c40p-44, | |
| 118 | 0x1.879cad931a395p-1, -0x1.3000p-45, | |
| 119 | 0x1.88ac7d98a65b8p-1, -0x1.a800p-45, | |
| 120 | 0x1.89bd0a4785800p-1, -0x1.d000p-49, | |
| 121 | 0x1.8ace5422aa223p-1, 0x1.3280p-44, | |
| 122 | 0x1.8be05bad619fap-1, 0x1.2b40p-43, | |
| 123 | 0x1.8cf3216b54383p-1, -0x1.ed00p-45, | |
| 124 | 0x1.8e06a5e08664cp-1, -0x1.0500p-45, | |
| 125 | 0x1.8f1ae99157807p-1, 0x1.8280p-45, | |
| 126 | 0x1.902fed0282c0ep-1, -0x1.cb00p-46, | |
| 127 | 0x1.9145b0b91ff96p-1, -0x1.5e00p-47, | |
| 128 | 0x1.925c353aa2ff9p-1, 0x1.5400p-48, | |
| 129 | 0x1.93737b0cdc64ap-1, 0x1.7200p-46, | |
| 130 | 0x1.948b82b5f98aep-1, -0x1.9000p-47, | |
| 131 | 0x1.95a44cbc852cbp-1, 0x1.5680p-45, | |
| 132 | 0x1.96bdd9a766f21p-1, -0x1.6d00p-44, | |
| 133 | 0x1.97d829fde4e2ap-1, -0x1.1000p-47, | |
| 134 | 0x1.98f33e47a23a3p-1, 0x1.d000p-45, | |
| 135 | 0x1.9a0f170ca0604p-1, -0x1.8a40p-44, | |
| 136 | 0x1.9b2bb4d53ff89p-1, 0x1.55c0p-44, | |
| 137 | 0x1.9c49182a3f15bp-1, 0x1.6b80p-45, | |
| 138 | 0x1.9d674194bb8c5p-1, -0x1.c000p-49, | |
| 139 | 0x1.9e86319e3238ep-1, 0x1.7d00p-46, | |
| 140 | 0x1.9fa5e8d07f302p-1, 0x1.6400p-46, | |
| 141 | 0x1.a0c667b5de54dp-1, -0x1.5000p-48, | |
| 142 | 0x1.a1e7aed8eb8f6p-1, 0x1.9e00p-47, | |
| 143 | 0x1.a309bec4a2e27p-1, 0x1.ad80p-45, | |
| 144 | 0x1.a42c980460a5dp-1, -0x1.af00p-46, | |
| 145 | 0x1.a5503b23e259bp-1, 0x1.b600p-47, | |
| 146 | 0x1.a674a8af46213p-1, 0x1.8880p-44, | |
| 147 | 0x1.a799e1330b3a7p-1, 0x1.1200p-46, | |
| 148 | 0x1.a8bfe53c12e8dp-1, 0x1.6c00p-47, | |
| 149 | 0x1.a9e6b5579fcd2p-1, -0x1.9b80p-45, | |
| 150 | 0x1.ab0e521356fb8p-1, 0x1.b700p-45, | |
| 151 | 0x1.ac36bbfd3f381p-1, 0x1.9000p-50, | |
| 152 | 0x1.ad5ff3a3c2780p-1, 0x1.4000p-49, | |
| 153 | 0x1.ae89f995ad2a3p-1, -0x1.c900p-45, | |
| 154 | 0x1.afb4ce622f367p-1, 0x1.6500p-46, | |
| 155 | 0x1.b0e07298db790p-1, 0x1.fd40p-45, | |
| 156 | 0x1.b20ce6c9a89a9p-1, 0x1.2700p-46, | |
| 157 | 0x1.b33a2b84f1a4bp-1, 0x1.d470p-43, | |
| 158 | 0x1.b468415b747e7p-1, -0x1.8380p-44, | |
| 159 | 0x1.b59728de5593ap-1, 0x1.8000p-54, | |
| 160 | 0x1.b6c6e29f1c56ap-1, 0x1.ad00p-47, | |
| 161 | 0x1.b7f76f2fb5e50p-1, 0x1.e800p-50, | |
| 162 | 0x1.b928cf22749b2p-1, -0x1.4c00p-47, | |
| 163 | 0x1.ba5b030a10603p-1, -0x1.d700p-47, | |
| 164 | 0x1.bb8e0b79a6f66p-1, 0x1.d900p-47, | |
| 165 | 0x1.bcc1e904bc1ffp-1, 0x1.2a00p-47, | |
| 166 | 0x1.bdf69c3f3a16fp-1, -0x1.f780p-46, | |
| 167 | 0x1.bf2c25bd71db8p-1, -0x1.0a00p-46, | |
| 168 | 0x1.c06286141b2e9p-1, -0x1.1400p-46, | |
| 169 | 0x1.c199bdd8552e0p-1, 0x1.be00p-47, | |
| 170 | 0x1.c2d1cd9fa64eep-1, -0x1.9400p-47, | |
| 171 | 0x1.c40ab5fffd02fp-1, -0x1.ed00p-47, | |
| 172 | 0x1.c544778fafd15p-1, 0x1.9660p-44, | |
| 173 | 0x1.c67f12e57d0cbp-1, -0x1.a100p-46, | |
| 174 | 0x1.c7ba88988c1b6p-1, -0x1.8458p-42, | |
| 175 | 0x1.c8f6d9406e733p-1, -0x1.a480p-46, | |
| 176 | 0x1.ca3405751c4dfp-1, 0x1.b000p-51, | |
| 177 | 0x1.cb720dcef9094p-1, 0x1.1400p-47, | |
| 178 | 0x1.ccb0f2e6d1689p-1, 0x1.0200p-48, | |
| 179 | 0x1.cdf0b555dc412p-1, 0x1.3600p-48, | |
| 180 | 0x1.cf3155b5bab3bp-1, -0x1.6900p-47, | |
| 181 | 0x1.d072d4a0789bcp-1, 0x1.9a00p-47, | |
| 182 | 0x1.d1b532b08c8fap-1, -0x1.5e00p-46, | |
| 183 | 0x1.d2f87080d8a85p-1, 0x1.d280p-46, | |
| 184 | 0x1.d43c8eacaa203p-1, 0x1.1a00p-47, | |
| 185 | 0x1.d5818dcfba491p-1, 0x1.f000p-50, | |
| 186 | 0x1.d6c76e862e6a1p-1, -0x1.3a00p-47, | |
| 187 | 0x1.d80e316c9834ep-1, -0x1.cd80p-47, | |
| 188 | 0x1.d955d71ff6090p-1, 0x1.4c00p-48, | |
| 189 | 0x1.da9e603db32aep-1, 0x1.f900p-48, | |
| 190 | 0x1.dbe7cd63a8325p-1, 0x1.9800p-49, | |
| 191 | 0x1.dd321f301b445p-1, -0x1.5200p-48, | |
| 192 | 0x1.de7d5641c05bfp-1, -0x1.d700p-46, | |
| 193 | 0x1.dfc97337b9aecp-1, -0x1.6140p-46, | |
| 194 | 0x1.e11676b197d5ep-1, 0x1.b480p-47, | |
| 195 | 0x1.e264614f5a3e7p-1, 0x1.0ce0p-43, | |
| 196 | 0x1.e3b333b16ee5cp-1, 0x1.c680p-47, | |
| 197 | 0x1.e502ee78b3fb4p-1, -0x1.9300p-47, | |
| 198 | 0x1.e653924676d68p-1, -0x1.5000p-49, | |
| 199 | 0x1.e7a51fbc74c44p-1, -0x1.7f80p-47, | |
| 200 | 0x1.e8f7977cdb726p-1, -0x1.3700p-48, | |
| 201 | 0x1.ea4afa2a490e8p-1, 0x1.5d00p-49, | |
| 202 | 0x1.eb9f4867ccae4p-1, 0x1.61a0p-46, | |
| 203 | 0x1.ecf482d8e680dp-1, 0x1.5500p-48, | |
| 204 | 0x1.ee4aaa2188514p-1, 0x1.6400p-51, | |
| 205 | 0x1.efa1bee615a13p-1, -0x1.e800p-49, | |
| 206 | 0x1.f0f9c1cb64106p-1, -0x1.a880p-48, | |
| 207 | 0x1.f252b376bb963p-1, -0x1.c900p-45, | |
| 208 | 0x1.f3ac948dd7275p-1, 0x1.a000p-53, | |
| 209 | 0x1.f50765b6e4524p-1, -0x1.4f00p-48, | |
| 210 | 0x1.f6632798844fdp-1, 0x1.a800p-51, | |
| 211 | 0x1.f7bfdad9cbe38p-1, 0x1.abc0p-48, | |
| 212 | 0x1.f91d802243c82p-1, -0x1.4600p-50, | |
| 213 | 0x1.fa7c1819e908ep-1, -0x1.b0c0p-47, | |
| 214 | 0x1.fbdba3692d511p-1, -0x1.0e00p-51, | |
| 215 | 0x1.fd3c22b8f7194p-1, -0x1.0de8p-46, | |
| 216 | 0x1.fe9d96b2a23eep-1, 0x1.e430p-49, | |
| 217 | 0x1.0000000000000p+0, 0x0.0000p+0, | |
| 218 | 0x1.00b1afa5abcbep+0, -0x1.3400p-52, | |
| 219 | 0x1.0163da9fb3303p+0, -0x1.2170p-46, | |
| 220 | 0x1.02168143b0282p+0, 0x1.a400p-52, | |
| 221 | 0x1.02c9a3e77806cp+0, 0x1.f980p-49, | |
| 222 | 0x1.037d42e11bbcap+0, -0x1.7400p-51, | |
| 223 | 0x1.04315e86e7f89p+0, 0x1.8300p-50, | |
| 224 | 0x1.04e5f72f65467p+0, -0x1.a3f0p-46, | |
| 225 | 0x1.059b0d315855ap+0, -0x1.2840p-47, | |
| 226 | 0x1.0650a0e3c1f95p+0, 0x1.1600p-48, | |
| 227 | 0x1.0706b29ddf71ap+0, 0x1.5240p-46, | |
| 228 | 0x1.07bd42b72a82dp+0, -0x1.9a00p-49, | |
| 229 | 0x1.0874518759bd0p+0, 0x1.6400p-49, | |
| 230 | 0x1.092bdf66607c8p+0, -0x1.0780p-47, | |
| 231 | 0x1.09e3ecac6f383p+0, -0x1.8000p-54, | |
| 232 | 0x1.0a9c79b1f3930p+0, 0x1.fa00p-48, | |
| 233 | 0x1.0b5586cf988fcp+0, -0x1.ac80p-48, | |
| 234 | 0x1.0c0f145e46c8ap+0, 0x1.9c00p-50, | |
| 235 | 0x1.0cc922b724816p+0, 0x1.5200p-47, | |
| 236 | 0x1.0d83b23395dd8p+0, -0x1.ad00p-48, | |
| 237 | 0x1.0e3ec32d3d1f3p+0, 0x1.bac0p-46, | |
| 238 | 0x1.0efa55fdfa9a6p+0, -0x1.4e80p-47, | |
| 239 | 0x1.0fb66affed2f0p+0, -0x1.d300p-47, | |
| 240 | 0x1.1073028d7234bp+0, 0x1.1500p-48, | |
| 241 | 0x1.11301d0125b5bp+0, 0x1.c000p-49, | |
| 242 | 0x1.11edbab5e2af9p+0, 0x1.6bc0p-46, | |
| 243 | 0x1.12abdc06c31d5p+0, 0x1.8400p-49, | |
| 244 | 0x1.136a814f2047dp+0, -0x1.ed00p-47, | |
| 245 | 0x1.1429aaea92de9p+0, 0x1.8e00p-49, | |
| 246 | 0x1.14e95934f3138p+0, 0x1.b400p-49, | |
| 247 | 0x1.15a98c8a58e71p+0, 0x1.5300p-47, | |
| 248 | 0x1.166a45471c3dfp+0, 0x1.3380p-47, | |
| 249 | 0x1.172b83c7d5211p+0, 0x1.8d40p-45, | |
| 250 | 0x1.17ed48695bb9fp+0, -0x1.5d00p-47, | |
| 251 | 0x1.18af9388c8d93p+0, -0x1.c880p-46, | |
| 252 | 0x1.1972658375d66p+0, 0x1.1f00p-46, | |
| 253 | 0x1.1a35beb6fcba7p+0, 0x1.0480p-46, | |
| 254 | 0x1.1af99f81387e3p+0, -0x1.7390p-43, | |
| 255 | 0x1.1bbe084045d54p+0, 0x1.4e40p-45, | |
| 256 | 0x1.1c82f95281c43p+0, -0x1.a200p-47, | |
| 257 | 0x1.1d4873168b9b2p+0, 0x1.3800p-49, | |
| 258 | 0x1.1e0e75eb44031p+0, 0x1.ac00p-49, | |
| 259 | 0x1.1ed5022fcd938p+0, 0x1.1900p-47, | |
| 260 | 0x1.1f9c18438cdf7p+0, -0x1.b780p-46, | |
| 261 | 0x1.2063b88628d8fp+0, 0x1.d940p-45, | |
| 262 | 0x1.212be3578a81ep+0, 0x1.8000p-50, | |
| 263 | 0x1.21f49917ddd41p+0, 0x1.b340p-45, | |
| 264 | 0x1.22bdda2791323p+0, 0x1.9f80p-46, | |
| 265 | 0x1.2387a6e7561e7p+0, -0x1.9c80p-46, | |
| 266 | 0x1.2451ffb821427p+0, 0x1.2300p-47, | |
| 267 | 0x1.251ce4fb2a602p+0, -0x1.3480p-46, | |
| 268 | 0x1.25e85711eceb0p+0, 0x1.2700p-46, | |
| 269 | 0x1.26b4565e27d16p+0, 0x1.1d00p-46, | |
| 270 | 0x1.2780e341de00fp+0, 0x1.1ee0p-44, | |
| 271 | 0x1.284dfe1f5633ep+0, -0x1.4c00p-46, | |
| 272 | 0x1.291ba7591bb30p+0, -0x1.3d80p-46, | |
| 273 | 0x1.29e9df51fdf09p+0, 0x1.8b00p-47, | |
| 274 | 0x1.2ab8a66d10e9bp+0, -0x1.27c0p-45, | |
| 275 | 0x1.2b87fd0dada3ap+0, 0x1.a340p-45, | |
| 276 | 0x1.2c57e39771af9p+0, -0x1.0800p-46, | |
| 277 | 0x1.2d285a6e402d9p+0, -0x1.ed00p-47, | |
| 278 | 0x1.2df961f641579p+0, -0x1.4200p-48, | |
| 279 | 0x1.2ecafa93e2ecfp+0, -0x1.4980p-45, | |
| 280 | 0x1.2f9d24abd8822p+0, -0x1.6300p-46, | |
| 281 | 0x1.306fe0a31b625p+0, -0x1.2360p-44, | |
| 282 | 0x1.31432edeea50bp+0, -0x1.0df8p-40, | |
| 283 | 0x1.32170fc4cd7b8p+0, -0x1.2480p-45, | |
| 284 | 0x1.32eb83ba8e9a2p+0, -0x1.5980p-45, | |
| 285 | 0x1.33c08b2641766p+0, 0x1.ed00p-46, | |
| 286 | 0x1.3496266e3fa27p+0, -0x1.c000p-50, | |
| 287 | 0x1.356c55f929f0fp+0, -0x1.0d80p-44, | |
| 288 | 0x1.36431a2de88b9p+0, 0x1.2c80p-45, | |
| 289 | 0x1.371a7373aaa39p+0, 0x1.0600p-45, | |
| 290 | 0x1.37f26231e74fep+0, -0x1.6600p-46, | |
| 291 | 0x1.38cae6d05d838p+0, -0x1.ae00p-47, | |
| 292 | 0x1.39a401b713ec3p+0, -0x1.4720p-43, | |
| 293 | 0x1.3a7db34e5a020p+0, 0x1.8200p-47, | |
| 294 | 0x1.3b57fbfec6e95p+0, 0x1.e800p-44, | |
| 295 | 0x1.3c32dc313a8f2p+0, 0x1.f800p-49, | |
| 296 | 0x1.3d0e544ede122p+0, -0x1.7a00p-46, | |
| 297 | 0x1.3dea64c1234bbp+0, 0x1.6300p-45, | |
| 298 | 0x1.3ec70df1c4eccp+0, -0x1.8a60p-43, | |
| 299 | 0x1.3fa4504ac7e8cp+0, -0x1.cdc0p-44, | |
| 300 | 0x1.40822c367a0bbp+0, 0x1.5b80p-45, | |
| 301 | 0x1.4160a21f72e95p+0, 0x1.ec00p-46, | |
| 302 | 0x1.423fb27094646p+0, -0x1.3600p-46, | |
| 303 | 0x1.431f5d950a920p+0, 0x1.3980p-45, | |
| 304 | 0x1.43ffa3f84b9ebp+0, 0x1.a000p-48, | |
| 305 | 0x1.44e0860618919p+0, -0x1.6c00p-48, | |
| 306 | 0x1.45c2042a7d201p+0, -0x1.bc00p-47, | |
| 307 | 0x1.46a41ed1d0016p+0, -0x1.2800p-46, | |
| 308 | 0x1.4786d668b3326p+0, 0x1.0e00p-44, | |
| 309 | 0x1.486a2b5c13c00p+0, -0x1.d400p-45, | |
| 310 | 0x1.494e1e192af04p+0, 0x1.c200p-47, | |
| 311 | 0x1.4a32af0d7d372p+0, -0x1.e500p-46, | |
| 312 | 0x1.4b17dea6db801p+0, 0x1.7800p-47, | |
| 313 | 0x1.4bfdad53629e1p+0, -0x1.3800p-46, | |
| 314 | 0x1.4ce41b817c132p+0, 0x1.0800p-47, | |
| 315 | 0x1.4dcb299fddddbp+0, 0x1.c700p-45, | |
| 316 | 0x1.4eb2d81d8ab96p+0, -0x1.ce00p-46, | |
| 317 | 0x1.4f9b2769d2d02p+0, 0x1.9200p-46, | |
| 318 | 0x1.508417f4531c1p+0, -0x1.8c00p-47, | |
| 319 | 0x1.516daa2cf662ap+0, -0x1.a000p-48, | |
| 320 | 0x1.5257de83f51eap+0, 0x1.a080p-43, | |
| 321 | 0x1.5342b569d4edap+0, -0x1.6d80p-45, | |
| 322 | 0x1.542e2f4f6ac1ap+0, -0x1.2440p-44, | |
| 323 | 0x1.551a4ca5d94dbp+0, 0x1.83c0p-43, | |
| 324 | 0x1.56070dde9116bp+0, 0x1.4b00p-45, | |
| 325 | 0x1.56f4736b529dep+0, 0x1.15a0p-43, | |
| 326 | 0x1.57e27dbe2c40ep+0, -0x1.9e00p-45, | |
| 327 | 0x1.58d12d497c76fp+0, -0x1.3080p-45, | |
| 328 | 0x1.59c0827ff0b4cp+0, 0x1.dec0p-43, | |
| 329 | 0x1.5ab07dd485427p+0, -0x1.4000p-51, | |
| 330 | 0x1.5ba11fba87af4p+0, 0x1.0080p-44, | |
| 331 | 0x1.5c9268a59460bp+0, -0x1.6c80p-45, | |
| 332 | 0x1.5d84590998e3fp+0, 0x1.69a0p-43, | |
| 333 | 0x1.5e76f15ad20e1p+0, -0x1.b400p-46, | |
| 334 | 0x1.5f6a320dcebcap+0, 0x1.7700p-46, | |
| 335 | 0x1.605e1b976dcb8p+0, 0x1.6f80p-45, | |
| 336 | 0x1.6152ae6cdf715p+0, 0x1.1000p-47, | |
| 337 | 0x1.6247eb03a5531p+0, -0x1.5d00p-46, | |
| 338 | 0x1.633dd1d1929b5p+0, -0x1.2d00p-46, | |
| 339 | 0x1.6434634ccc313p+0, -0x1.a800p-49, | |
| 340 | 0x1.652b9febc8efap+0, -0x1.8600p-45, | |
| 341 | 0x1.6623882553397p+0, 0x1.1fe0p-40, | |
| 342 | 0x1.671c1c708328ep+0, -0x1.7200p-44, | |
| 343 | 0x1.68155d44ca97ep+0, 0x1.6800p-49, | |
| 344 | 0x1.690f4b19e9471p+0, -0x1.9780p-45, | |
| 345 | }; | |
| 346 | ||
| 347 | fn exp2d(x: f64) -> f64 { | |
| 348 | const tblsiz = u32(exp2dt.len / 2); | |
| 349 | const redux: f64 = 0x1.8p52 / f64(tblsiz); | |
| 350 | const P1: f64 = 0x1.62e42fefa39efp-1; | |
| 351 | const P2: f64 = 0x1.ebfbdff82c575p-3; | |
| 352 | const P3: f64 = 0x1.c6b08d704a0a6p-5; | |
| 353 | const P4: f64 = 0x1.3b2ab88f70400p-7; | |
| 354 | const P5: f64 = 0x1.5d88003875c74p-10; | |
| 355 | ||
| 356 | const ux = @bitCast(u64, x); | |
| 357 | const ix = u32(ux >> 32) & 0x7FFFFFFF; | |
| 358 | ||
| 359 | // |x| >= 1022 or nan | |
| 360 | if (ix >= 0x408FF000) { | |
| 361 | // x >= 1024 or nan | |
| 362 | if (ix >= 0x40900000 and ux >> 63 == 0) { | |
| 363 | math.raiseOverflow(); | |
| 364 | return math.inf(f64); | |
| 365 | } | |
| 366 | // -inf or -nan | |
| 367 | if (ix >= 0x7FF00000) { | |
| 368 | return -1 / x; | |
| 369 | } | |
| 370 | // x <= -1022 | |
| 371 | if (ux >> 63 != 0) { | |
| 372 | // underflow | |
| 373 | if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) { | |
| 374 | math.forceEval(f32(-0x1.0p-149 / x)); | |
| 375 | } | |
| 376 | if (x <= -1075) { | |
| 377 | return 0; | |
| 378 | } | |
| 379 | } | |
| 380 | } | |
| 381 | // |x| < 0x1p-54 | |
| 382 | else if (ix < 0x3C900000) { | |
| 383 | return 1.0 + x; | |
| 384 | } | |
| 385 | ||
| 386 | // reduce x | |
| 387 | var uf = x + redux; | |
| 388 | // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here | |
| 389 | var i0 = @truncate(u32, @bitCast(u64, uf)); | |
| 390 | i0 += tblsiz / 2; | |
| 391 | ||
| 392 | const k: u32 = i0 / tblsiz * tblsiz; | |
| 393 | const ik = @bitCast(i32, k / tblsiz); | |
| 394 | i0 %= tblsiz; | |
| 395 | uf -= redux; | |
| 396 | ||
| 397 | // r = exp2(y) = exp2t[i0] * p(z - eps[i]) | |
| 398 | var z = x - uf; | |
| 399 | const t = exp2dt[2 * i0]; | |
| 400 | z -= exp2dt[2 * i0 + 1]; | |
| 401 | const r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5)))); | |
| 402 | ||
| 403 | math.scalbn(r, ik) | |
| 404 | } | |
| 405 | ||
| 406 | test "exp2" { | |
| 407 | assert(exp2(f32(0.8923)) == exp2f(0.8923)); | |
| 408 | assert(exp2(f64(0.8923)) == exp2d(0.8923)); | |
| 409 | } | |
| 410 | ||
| 411 | test "exp2f" { | |
| 412 | const epsilon = 0.000001; | |
| 413 | ||
| 414 | assert(exp2f(0.0) == 1.0); | |
| 415 | assert(math.approxEq(f32, exp2f(0.2), 1.148698, epsilon)); | |
| 416 | assert(math.approxEq(f32, exp2f(0.8923), 1.856133, epsilon)); | |
| 417 | assert(math.approxEq(f32, exp2f(1.5), 2.828427, epsilon)); | |
| 418 | assert(math.approxEq(f32, exp2f(37.45), 187747237888, epsilon)); | |
| 419 | } | |
| 420 | ||
| 421 | test "exp2d" { | |
| 422 | const epsilon = 0.000001; | |
| 423 | ||
| 424 | assert(exp2d(0.0) == 1.0); | |
| 425 | assert(math.approxEq(f64, exp2d(0.2), 1.148698, epsilon)); | |
| 426 | assert(math.approxEq(f64, exp2d(0.8923), 1.856133, epsilon)); | |
| 427 | assert(math.approxEq(f64, exp2d(1.5), 2.828427, epsilon)); | |
| 428 | // assert(math.approxEq(f64, exp2d(37.45), 18379273786760560.000000, epsilon)); | |
| 429 | } |
std/math/expm1.zig created+282| ... | ... | @@ -0,0 +1,282 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn expm1(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(expm1f, x), | |
| 8 | f64 => @inlineCall(expm1d, x), | |
| 9 | else => @compileError("exp1m not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn expm1f(x_: f32) -> f32 { | |
| 14 | const o_threshold: f32 = 8.8721679688e+01; | |
| 15 | const ln2_hi: f32 = 6.9313812256e-01; | |
| 16 | const ln2_lo: f32 = 9.0580006145e-06; | |
| 17 | const invln2: f32 = 1.4426950216e+00; | |
| 18 | const Q1: f32 = -3.3333212137e-2; | |
| 19 | const Q2: f32 = 1.5807170421e-3; | |
| 20 | ||
| 21 | var x = x_; | |
| 22 | const ux = @bitCast(u32, x); | |
| 23 | const hx = ux & 0x7FFFFFFF; | |
| 24 | const sign = hx >> 31; | |
| 25 | ||
| 26 | // |x| >= 27 * ln2 | |
| 27 | if (hx >= 0x4195B844) { | |
| 28 | // nan | |
| 29 | if (hx > 0x7F800000) { | |
| 30 | return x; | |
| 31 | } | |
| 32 | if (sign != 0) { | |
| 33 | return -1; | |
| 34 | } | |
| 35 | if (x > o_threshold) { | |
| 36 | x *= 0x1.0p127; | |
| 37 | return x; | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | var hi: f32 = undefined; | |
| 42 | var lo: f32 = undefined; | |
| 43 | var c: f32 = undefined; | |
| 44 | var k: i32 = undefined; | |
| 45 | ||
| 46 | // |x| > 0.5 * ln2 | |
| 47 | if (hx > 0x3EB17218) { | |
| 48 | // |x| < 1.5 * ln2 | |
| 49 | if (hx < 0x3F851592) { | |
| 50 | if (sign == 0) { | |
| 51 | hi = x - ln2_hi; | |
| 52 | lo = ln2_lo; | |
| 53 | k = 1; | |
| 54 | } else { | |
| 55 | hi = x + ln2_hi; | |
| 56 | lo = -ln2_lo; | |
| 57 | k = -1; | |
| 58 | } | |
| 59 | } else { | |
| 60 | var kf = invln2 * x; | |
| 61 | if (sign != 0) { | |
| 62 | kf -= 0.5; | |
| 63 | } else { | |
| 64 | kf += 0.5; | |
| 65 | } | |
| 66 | ||
| 67 | k = i32(kf); | |
| 68 | const t = f32(k); | |
| 69 | hi = x - t * ln2_hi; | |
| 70 | lo = t * ln2_lo; | |
| 71 | } | |
| 72 | ||
| 73 | x = hi - lo; | |
| 74 | c = (hi - x) - lo; | |
| 75 | } | |
| 76 | // |x| < 2^(-25) | |
| 77 | else if (hx < 0x33000000) { | |
| 78 | if (hx < 0x00800000) { | |
| 79 | math.forceEval(x * x); | |
| 80 | } | |
| 81 | return x; | |
| 82 | } | |
| 83 | else { | |
| 84 | k = 0; | |
| 85 | } | |
| 86 | ||
| 87 | const hfx = 0.5 * x; | |
| 88 | const hxs = x * hfx; | |
| 89 | const r1 = 1.0 + hxs * (Q1 + hxs * Q2); | |
| 90 | const t = 3.0 - r1 * hfx; | |
| 91 | var e = hxs * ((r1 - t) / (6.0 - x * t)); | |
| 92 | ||
| 93 | // c is 0 | |
| 94 | if (k == 0) { | |
| 95 | return x - (x * e - hxs); | |
| 96 | } | |
| 97 | ||
| 98 | e = x * (e - c) - c; | |
| 99 | e -= hxs; | |
| 100 | ||
| 101 | // exp(x) ~ 2^k (x_reduced - e + 1) | |
| 102 | if (k == -1) { | |
| 103 | return 0.5 * (x - e) - 0.5; | |
| 104 | } | |
| 105 | if (k == 1) { | |
| 106 | if (x < -0.25) { | |
| 107 | return -2.0 * (e - (x + 0.5)); | |
| 108 | } else { | |
| 109 | return 1.0 + 2.0 * (x - e); | |
| 110 | } | |
| 111 | } | |
| 112 | ||
| 113 | const twopk = @bitCast(f32, u32(0x7F + k) << 23); | |
| 114 | ||
| 115 | if (k < 0 or k > 56) { | |
| 116 | var y = x - e + 1.0; | |
| 117 | if (k == 128) { | |
| 118 | y = y * 2.0 * 0x1.0p127; | |
| 119 | } else { | |
| 120 | y = y * twopk; | |
| 121 | } | |
| 122 | ||
| 123 | return y - 1.0; | |
| 124 | } | |
| 125 | ||
| 126 | const uf = @bitCast(f32, u32(0x7F - k) << 23); | |
| 127 | if (k < 23) { | |
| 128 | return (x - e + (1 - uf)) * twopk; | |
| 129 | } else { | |
| 130 | return (x - (e + uf) + 1) * twopk; | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | fn expm1d(x_: f64) -> f64 { | |
| 135 | const o_threshold: f64 = 7.09782712893383973096e+02; | |
| 136 | const ln2_hi: f64 = 6.93147180369123816490e-01; | |
| 137 | const ln2_lo: f64 = 1.90821492927058770002e-10; | |
| 138 | const invln2: f64 = 1.44269504088896338700e+00; | |
| 139 | const Q1: f64 = -3.33333333333331316428e-02; | |
| 140 | const Q2: f64 = 1.58730158725481460165e-03; | |
| 141 | const Q3: f64 = -7.93650757867487942473e-05; | |
| 142 | const Q4: f64 = 4.00821782732936239552e-06; | |
| 143 | const Q5: f64 = -2.01099218183624371326e-07; | |
| 144 | ||
| 145 | var x = x_; | |
| 146 | const ux = @bitCast(u64, x); | |
| 147 | const hx = u32(ux >> 32) & 0x7FFFFFFF; | |
| 148 | const sign = hx >> 63; | |
| 149 | ||
| 150 | // |x| >= 56 * ln2 | |
| 151 | if (hx >= 0x4043687A) { | |
| 152 | // exp1md(nan) = nan | |
| 153 | if (hx > 0x7FF00000) { | |
| 154 | return x; | |
| 155 | } | |
| 156 | // exp1md(-ve) = -1 | |
| 157 | if (sign != 0) { | |
| 158 | return -1; | |
| 159 | } | |
| 160 | if (x > o_threshold) { | |
| 161 | math.raiseOverflow(); | |
| 162 | return math.nan(f64); | |
| 163 | } | |
| 164 | } | |
| 165 | ||
| 166 | var hi: f64 = undefined; | |
| 167 | var lo: f64 = undefined; | |
| 168 | var c: f64 = undefined; | |
| 169 | var k: i32 = undefined; | |
| 170 | ||
| 171 | // |x| > 0.5 * ln2 | |
| 172 | if (hx > 0x3FD62E42) { | |
| 173 | // |x| < 1.5 * ln2 | |
| 174 | if (hx < 0x3FF0A2B2) { | |
| 175 | if (sign == 0) { | |
| 176 | hi = x - ln2_hi; | |
| 177 | lo = ln2_lo; | |
| 178 | k = 1; | |
| 179 | } else { | |
| 180 | hi = x + ln2_hi; | |
| 181 | lo = -ln2_lo; | |
| 182 | k = -1; | |
| 183 | } | |
| 184 | } else { | |
| 185 | var kf = invln2 * x; | |
| 186 | if (sign != 0) { | |
| 187 | kf -= 0.5; | |
| 188 | } else { | |
| 189 | kf += 0.5; | |
| 190 | } | |
| 191 | ||
| 192 | k = i32(kf); | |
| 193 | const t = f64(k); | |
| 194 | hi = x - t * ln2_hi; | |
| 195 | lo = t * ln2_lo; | |
| 196 | } | |
| 197 | ||
| 198 | x = hi - lo; | |
| 199 | c = (hi - x) - lo; | |
| 200 | } | |
| 201 | // |x| < 2^(-54) | |
| 202 | else if (hx < 0x3C900000) { | |
| 203 | if (hx < 0x00100000) { | |
| 204 | math.forceEval(f32(x)); | |
| 205 | } | |
| 206 | return x; | |
| 207 | } | |
| 208 | else { | |
| 209 | k = 0; | |
| 210 | } | |
| 211 | ||
| 212 | const hfx = 0.5 * x; | |
| 213 | const hxs = x * hfx; | |
| 214 | const r1 = 1.0 + hxs * (Q1 + hxs * (Q2 + hxs * (Q3 + hxs * (Q4 + hxs * Q5)))); | |
| 215 | const t = 3.0 - r1 * hfx; | |
| 216 | var e = hxs * ((r1 - t) / (6.0 - x * t)); | |
| 217 | ||
| 218 | // c is 0 | |
| 219 | if (k == 0) { | |
| 220 | return x - (x * e - hxs); | |
| 221 | } | |
| 222 | ||
| 223 | e = x * (e - c) - c; | |
| 224 | e -= hxs; | |
| 225 | ||
| 226 | // exp(x) ~ 2^k (x_reduced - e + 1) | |
| 227 | if (k == -1) { | |
| 228 | return 0.5 * (x - e) - 0.5; | |
| 229 | } | |
| 230 | if (k == 1) { | |
| 231 | if (x < -0.25) { | |
| 232 | return -2.0 * (e - (x + 0.5)); | |
| 233 | } else { | |
| 234 | return 1.0 + 2.0 * (x - e); | |
| 235 | } | |
| 236 | } | |
| 237 | ||
| 238 | const twopk = @bitCast(f64, u64(0x3FF + k) << 52); | |
| 239 | ||
| 240 | if (k < 0 or k > 56) { | |
| 241 | var y = x - e + 1.0; | |
| 242 | if (k == 1024) { | |
| 243 | y = y * 2.0; // TODO: * 0x1.0p1023; | |
| 244 | } else { | |
| 245 | y = y * twopk; | |
| 246 | } | |
| 247 | ||
| 248 | return y - 1.0; | |
| 249 | } | |
| 250 | ||
| 251 | const uf = @bitCast(f64, u64(0x3FF - k) << 52); | |
| 252 | if (k < 20) { | |
| 253 | return (x - e + (1 - uf)) * twopk; | |
| 254 | } else { | |
| 255 | return (x - (e + uf) + 1) * twopk; | |
| 256 | } | |
| 257 | } | |
| 258 | ||
| 259 | test "exp1m" { | |
| 260 | assert(expm1(f32(0.0)) == expm1f(0.0)); | |
| 261 | assert(expm1(f64(0.0)) == expm1d(0.0)); | |
| 262 | } | |
| 263 | ||
| 264 | test "expm1f" { | |
| 265 | const epsilon = 0.000001; | |
| 266 | ||
| 267 | assert(expm1f(0.0) == 0.0); | |
| 268 | assert(math.approxEq(f32, expm1f(0.0), 0.0, epsilon)); | |
| 269 | assert(math.approxEq(f32, expm1f(0.2), 0.221403, epsilon)); | |
| 270 | assert(math.approxEq(f32, expm1f(0.8923), 1.440737, epsilon)); | |
| 271 | assert(math.approxEq(f32, expm1f(1.5), 3.481689, epsilon)); | |
| 272 | } | |
| 273 | ||
| 274 | test "expm1d" { | |
| 275 | const epsilon = 0.000001; | |
| 276 | ||
| 277 | assert(expm1d(0.0) == 0.0); | |
| 278 | assert(math.approxEq(f64, expm1d(0.0), 0.0, epsilon)); | |
| 279 | assert(math.approxEq(f64, expm1d(0.2), 0.221403, epsilon)); | |
| 280 | assert(math.approxEq(f64, expm1d(0.8923), 1.440737, epsilon)); | |
| 281 | assert(math.approxEq(f64, expm1d(1.5), 3.481689, epsilon)); | |
| 282 | } |
std/math/fabs.zig+3-14| ... | ... | @@ -1,10 +1,11 @@ |
| 1 | const math = @import("index.zig"); | |
| 1 | 2 | const assert = @import("../debug.zig").assert; |
| 2 | 3 | |
| 3 | 4 | pub fn fabs(x: var) -> @typeOf(x) { |
| 4 | 5 | const T = @typeOf(x); |
| 5 | 6 | switch (T) { |
| 6 | f32 => fabs32(x), | |
| 7 | f64 => fabs64(x), | |
| 7 | f32 => @inlineCall(fabs32, x), | |
| 8 | f64 => @inlineCall(fabs64, x), | |
| 8 | 9 | else => @compileError("fabs not implemented for " ++ @typeName(T)), |
| 9 | 10 | } |
| 10 | 11 | } |
| ... | ... | @@ -24,26 +25,14 @@ fn fabs64(x: f64) -> f64 { |
| 24 | 25 | test "fabs" { |
| 25 | 26 | assert(fabs(f32(1.0)) == fabs32(1.0)); |
| 26 | 27 | assert(fabs(f64(1.0)) == fabs64(1.0)); |
| 27 | comptime { | |
| 28 | assert(fabs(f32(1.0)) == fabs32(1.0)); | |
| 29 | assert(fabs(f64(1.0)) == fabs64(1.0)); | |
| 30 | } | |
| 31 | 28 | } |
| 32 | 29 | |
| 33 | 30 | test "fabs32" { |
| 34 | 31 | assert(fabs64(1.0) == 1.0); |
| 35 | 32 | assert(fabs64(-1.0) == 1.0); |
| 36 | comptime { | |
| 37 | assert(fabs64(1.0) == 1.0); | |
| 38 | assert(fabs64(-1.0) == 1.0); | |
| 39 | } | |
| 40 | 33 | } |
| 41 | 34 | |
| 42 | 35 | test "fabs64" { |
| 43 | 36 | assert(fabs64(1.0) == 1.0); |
| 44 | 37 | assert(fabs64(-1.0) == 1.0); |
| 45 | comptime { | |
| 46 | assert(fabs64(1.0) == 1.0); | |
| 47 | assert(fabs64(-1.0) == 1.0); | |
| 48 | } | |
| 49 | 38 | } |
std/math/floor.zig created+89| ... | ... | @@ -0,0 +1,89 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | const math = @import("index.zig"); | |
| 4 | ||
| 5 | pub fn floor(x: var) -> @typeOf(x) { | |
| 6 | const T = @typeOf(x); | |
| 7 | switch (T) { | |
| 8 | f32 => @inlineCall(floor32, x), | |
| 9 | f64 => @inlineCall(floor64, x), | |
| 10 | else => @compileError("floor not implemented for " ++ @typeName(T)), | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | fn floor32(x: f32) -> f32 { | |
| 15 | var u = @bitCast(u32, x); | |
| 16 | const e = i32((u >> 23) & 0xFF) - 0x7F; | |
| 17 | var m: u32 = undefined; | |
| 18 | ||
| 19 | if (e >= 23) { | |
| 20 | return x; | |
| 21 | } | |
| 22 | ||
| 23 | if (e >= 0) { | |
| 24 | m = 0x007FFFFF >> u32(e); | |
| 25 | if (u & m == 0) { | |
| 26 | return x; | |
| 27 | } | |
| 28 | math.forceEval(x + 0x1.0p120); | |
| 29 | if (u >> 31 != 0) { | |
| 30 | u += m; | |
| 31 | } | |
| 32 | @bitCast(f32, u & ~m) | |
| 33 | } else { | |
| 34 | math.forceEval(x + 0x1.0p120); | |
| 35 | if (u >> 31 == 0) { | |
| 36 | return 0.0; // Compiler requires return | |
| 37 | } else { | |
| 38 | -1.0 | |
| 39 | } | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | fn floor64(x: f64) -> f64 { | |
| 44 | const u = @bitCast(u64, x); | |
| 45 | const e = (u >> 52) & 0x7FF; | |
| 46 | var y: f64 = undefined; | |
| 47 | ||
| 48 | if (e >= 0x3FF+52 or x == 0) { | |
| 49 | return x; | |
| 50 | } | |
| 51 | ||
| 52 | if (u >> 63 != 0) { | |
| 53 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 54 | y = x - math.f64_toint + math.f64_toint - x; | |
| 55 | } else { | |
| 56 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 57 | y = x + math.f64_toint - math.f64_toint - x; | |
| 58 | } | |
| 59 | ||
| 60 | if (e <= 0x3FF-1) { | |
| 61 | math.forceEval(y); | |
| 62 | if (u >> 63 != 0) { | |
| 63 | return -1.0; // Compiler requires return. | |
| 64 | } else { | |
| 65 | 0.0 | |
| 66 | } | |
| 67 | } else if (y > 0) { | |
| 68 | x + y - 1 | |
| 69 | } else { | |
| 70 | x + y | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | test "floor" { | |
| 75 | assert(floor(f32(1.3)) == floor32(1.3)); | |
| 76 | assert(floor(f64(1.3)) == floor64(1.3)); | |
| 77 | } | |
| 78 | ||
| 79 | test "floor32" { | |
| 80 | assert(floor32(1.3) == 1.0); | |
| 81 | assert(floor32(-1.3) == -2.0); | |
| 82 | assert(floor32(0.2) == 0.0); | |
| 83 | } | |
| 84 | ||
| 85 | test "floor64" { | |
| 86 | assert(floor64(1.3) == 1.0); | |
| 87 | assert(floor64(-1.3) == -2.0); | |
| 88 | assert(floor64(0.2) == 0.0); | |
| 89 | } |
std/math/fma.zig created+160| ... | ... | @@ -0,0 +1,160 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn fma(comptime T: type, x: T, y: T, z: T) -> T { | |
| 5 | switch (T) { | |
| 6 | f32 => @inlineCall(fma32, x, y, z), | |
| 7 | f64 => @inlineCall(fma64, x, y ,z), | |
| 8 | else => @compileError("fma not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | fn fma32(x: f32, y: f32, z: f32) -> f32 { | |
| 13 | const xy = f64(x) * y; | |
| 14 | const xy_z = xy + z; | |
| 15 | const u = @bitCast(u64, xy_z); | |
| 16 | const e = (u >> 52) & 0x7FF; | |
| 17 | ||
| 18 | if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) { | |
| 19 | f32(xy_z) | |
| 20 | } else { | |
| 21 | // TODO: Handle inexact case with double-rounding | |
| 22 | f32(xy_z) | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | fn fma64(x: f64, y: f64, z: f64) -> f64 { | |
| 27 | if (!math.isFinite(x) or !math.isFinite(y)) { | |
| 28 | return x * y + z; | |
| 29 | } | |
| 30 | if (!math.isFinite(z)) { | |
| 31 | return z; | |
| 32 | } | |
| 33 | if (x == 0.0 or y == 0.0) { | |
| 34 | return x * y + z; | |
| 35 | } | |
| 36 | if (z == 0.0) { | |
| 37 | return x * y; | |
| 38 | } | |
| 39 | ||
| 40 | const x1 = math.frexp(x); | |
| 41 | var ex = x1.exponent; | |
| 42 | var xs = x1.significand; | |
| 43 | const x2 = math.frexp(y); | |
| 44 | var ey = x2.exponent; | |
| 45 | var ys = x2.significand; | |
| 46 | const x3 = math.frexp(z); | |
| 47 | var ez = x3.exponent; | |
| 48 | var zs = x3.significand; | |
| 49 | ||
| 50 | var spread = ex + ey - ez; | |
| 51 | if (spread <= 53 * 2) { | |
| 52 | zs = math.scalbn(zs, -spread); | |
| 53 | } else { | |
| 54 | zs = math.copysign(f64, math.f64_min, zs); | |
| 55 | } | |
| 56 | ||
| 57 | const xy = dd_mul(xs, ys); | |
| 58 | const r = dd_add(xy.hi, zs); | |
| 59 | spread = ex + ey; | |
| 60 | ||
| 61 | if (r.hi == 0.0) { | |
| 62 | return xy.hi + zs + math.scalbn(xy.lo, spread); | |
| 63 | } | |
| 64 | ||
| 65 | const adj = add_adjusted(r.lo, xy.lo); | |
| 66 | if (spread + math.ilogb(r.hi) > -1023) { | |
| 67 | math.scalbn(r.hi + adj, spread) | |
| 68 | } else { | |
| 69 | add_and_denorm(r.hi, adj, spread) | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | const dd = struct { hi: f64, lo: f64, }; | |
| 74 | ||
| 75 | fn dd_add(a: f64, b: f64) -> dd { | |
| 76 | var ret: dd = undefined; | |
| 77 | ret.hi = a + b; | |
| 78 | const s = ret.hi - a; | |
| 79 | ret.lo = (a - (ret.hi - s)) + (b - s); | |
| 80 | ret | |
| 81 | } | |
| 82 | ||
| 83 | fn dd_mul(a: f64, b: f64) -> dd { | |
| 84 | var ret: dd = undefined; | |
| 85 | const split: f64 = 0x1.0p27 + 1.0; | |
| 86 | ||
| 87 | var p = a * split; | |
| 88 | var ha = a - p; | |
| 89 | ha += p; | |
| 90 | var la = a - ha; | |
| 91 | ||
| 92 | p = b * split; | |
| 93 | var hb = b - p; | |
| 94 | hb += p; | |
| 95 | var lb = b - hb; | |
| 96 | ||
| 97 | p = ha * hb; | |
| 98 | var q = ha * lb + la * hb; | |
| 99 | ||
| 100 | ret.hi = p + q; | |
| 101 | ret.lo = p - ret.hi + q + la * lb; | |
| 102 | ret | |
| 103 | } | |
| 104 | ||
| 105 | fn add_adjusted(a: f64, b: f64) -> f64 { | |
| 106 | var sum = dd_add(a, b); | |
| 107 | if (sum.lo != 0) { | |
| 108 | var uhii = @bitCast(u64, sum.hi); | |
| 109 | if (uhii & 1 == 0) { | |
| 110 | // hibits += copysign(1.0, sum.hi, sum.lo) | |
| 111 | const uloi = @bitCast(u64, sum.lo); | |
| 112 | uhii += 1 - ((uhii ^ uloi) >> 62); | |
| 113 | sum.hi = @bitCast(f64, uhii); | |
| 114 | } | |
| 115 | } | |
| 116 | sum.hi | |
| 117 | } | |
| 118 | ||
| 119 | fn add_and_denorm(a: f64, b: f64, scale: i32) -> f64 { | |
| 120 | var sum = dd_add(a, b); | |
| 121 | if (sum.lo != 0) { | |
| 122 | var uhii = @bitCast(u64, sum.hi); | |
| 123 | const bits_lost = -i32((uhii >> 52) & 0x7FF) - scale + 1; | |
| 124 | if ((bits_lost != 1) == (uhii & 1 != 0)) { | |
| 125 | const uloi = @bitCast(u64, sum.lo); | |
| 126 | uhii += 1 - (((uhii ^ uloi) >> 62) & 2); | |
| 127 | sum.hi = @bitCast(f64, uhii); | |
| 128 | } | |
| 129 | } | |
| 130 | math.scalbn(sum.hi, scale) | |
| 131 | } | |
| 132 | ||
| 133 | test "fma" { | |
| 134 | assert(fma(f32, 0.0, 1.0, 1.0) == fma32(0.0, 1.0, 1.0)); | |
| 135 | assert(fma(f64, 0.0, 1.0, 1.0) == fma64(0.0, 1.0, 1.0)); | |
| 136 | } | |
| 137 | ||
| 138 | test "fma32" { | |
| 139 | const epsilon = 0.000001; | |
| 140 | ||
| 141 | assert(math.approxEq(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon)); | |
| 142 | assert(math.approxEq(f32, fma32(0.2, 5.0, 9.124), 10.124, epsilon)); | |
| 143 | assert(math.approxEq(f32, fma32(0.8923, 5.0, 9.124), 13.5855, epsilon)); | |
| 144 | assert(math.approxEq(f32, fma32(1.5, 5.0, 9.124), 16.624, epsilon)); | |
| 145 | assert(math.approxEq(f32, fma32(37.45, 5.0, 9.124), 196.374004, epsilon)); | |
| 146 | assert(math.approxEq(f32, fma32(89.123, 5.0, 9.124), 454.739005, epsilon)); | |
| 147 | assert(math.approxEq(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); | |
| 148 | } | |
| 149 | ||
| 150 | test "fma64" { | |
| 151 | const epsilon = 0.000001; | |
| 152 | ||
| 153 | assert(math.approxEq(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon)); | |
| 154 | assert(math.approxEq(f64, fma64(0.2, 5.0, 9.124), 10.124, epsilon)); | |
| 155 | assert(math.approxEq(f64, fma64(0.8923, 5.0, 9.124), 13.5855, epsilon)); | |
| 156 | assert(math.approxEq(f64, fma64(1.5, 5.0, 9.124), 16.624, epsilon)); | |
| 157 | assert(math.approxEq(f64, fma64(37.45, 5.0, 9.124), 196.374, epsilon)); | |
| 158 | assert(math.approxEq(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon)); | |
| 159 | assert(math.approxEq(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); | |
| 160 | } |
std/math/fmod.zig created+190| ... | ... | @@ -0,0 +1,190 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn fmod(comptime T: type, x: T, y: T) -> T { | |
| 5 | switch (T) { | |
| 6 | f32 => @inlineCall(fmod32, x, y), | |
| 7 | f64 => @inlineCall(fmod64, x, y), | |
| 8 | else => @compileError("fmod not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | fn fmod32(x: f32, y: f32) -> f32 { | |
| 13 | var ux = @bitCast(u32, x); | |
| 14 | var uy = @bitCast(u32, y); | |
| 15 | var ex = i32(ux >> 23) & 0xFF; | |
| 16 | var ey = i32(ux >> 23) & 0xFF; | |
| 17 | const sx = ux & 0x80000000; | |
| 18 | ||
| 19 | if (uy << 1 == 0 or math.isNan(y) or ex == 0xFF) { | |
| 20 | return (x * y) / (x * y); | |
| 21 | } | |
| 22 | if (ux << 1 <= uy << 1) { | |
| 23 | if (ux << 1 == uy << 1) { | |
| 24 | return 0 * x; | |
| 25 | } else { | |
| 26 | return x; | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | // normalize x and y | |
| 31 | if (ex == 0) { | |
| 32 | var i = ux << 9; | |
| 33 | while (i >> 31 == 0) : (i <<= 1) { | |
| 34 | ex -= 1; | |
| 35 | } | |
| 36 | ux <<= u32(-ex + 1); | |
| 37 | } else { | |
| 38 | ux &= @maxValue(u32) >> 9; | |
| 39 | ux |= 1 << 23; | |
| 40 | } | |
| 41 | ||
| 42 | if (ey == 0) { | |
| 43 | var i = uy << 9; | |
| 44 | while (i >> 31 == 0) : (i <<= 1) { | |
| 45 | ey -= 1; | |
| 46 | } | |
| 47 | uy <<= u32(-ey + 1); | |
| 48 | } else { | |
| 49 | uy &= @maxValue(u32) >> 9; | |
| 50 | uy |= 1 << 23; | |
| 51 | } | |
| 52 | ||
| 53 | // x mod y | |
| 54 | while (ex > ey) : (ex -= 1) { | |
| 55 | const i = ux - uy; | |
| 56 | if (i >> 31 == 0) { | |
| 57 | if (i == 0) { | |
| 58 | return 0 * x; | |
| 59 | } | |
| 60 | ux = i; | |
| 61 | } | |
| 62 | ux <<= 1; | |
| 63 | } | |
| 64 | { | |
| 65 | const i = ux - uy; | |
| 66 | if (i >> 31 == 0) { | |
| 67 | if (i == 0) { | |
| 68 | return 0 * x; | |
| 69 | } | |
| 70 | ux = i; | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | while (ux >> 23 == 0) : (ux <<= 1) { | |
| 75 | ex -= 1; | |
| 76 | } | |
| 77 | ||
| 78 | // scale result up | |
| 79 | if (ex > 0) { | |
| 80 | ux -= 1 << 23; | |
| 81 | ux |= u32(ex) << 23; | |
| 82 | } else { | |
| 83 | ux >>= u32(-ex + 1); | |
| 84 | } | |
| 85 | ||
| 86 | ux |= sx; | |
| 87 | @bitCast(f32, ux) | |
| 88 | } | |
| 89 | ||
| 90 | fn fmod64(x: f64, y: f64) -> f64 { | |
| 91 | var ux = @bitCast(u64, x); | |
| 92 | var uy = @bitCast(u64, y); | |
| 93 | var ex = i32(ux >> 52) & 0x7FF; | |
| 94 | var ey = i32(ux >> 52) & 0x7FF; | |
| 95 | const sx = ux >> 63; | |
| 96 | ||
| 97 | if (uy << 1 == 0 or math.isNan(y) or ex == 0x7FF) { | |
| 98 | return (x * y) / (x * y); | |
| 99 | } | |
| 100 | if (ux << 1 <= uy << 1) { | |
| 101 | if (ux << 1 == uy << 1) { | |
| 102 | return 0 * x; | |
| 103 | } else { | |
| 104 | return x; | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | // normalize x and y | |
| 109 | if (ex == 0) { | |
| 110 | var i = ux << 12; | |
| 111 | while (i >> 63 == 0) : (i <<= 1) { | |
| 112 | ex -= 1; | |
| 113 | } | |
| 114 | ux <<= u64(-ex + 1); | |
| 115 | } else { | |
| 116 | ux &= @maxValue(u64) >> 12; | |
| 117 | ux |= 1 << 52; | |
| 118 | } | |
| 119 | ||
| 120 | if (ey == 0) { | |
| 121 | var i = uy << 12; | |
| 122 | while (i >> 63 == 0) : (i <<= 1) { | |
| 123 | ey -= 1; | |
| 124 | } | |
| 125 | uy <<= u64(-ey + 1); | |
| 126 | } else { | |
| 127 | uy &= @maxValue(u64) >> 12; | |
| 128 | uy |= 1 << 52; | |
| 129 | } | |
| 130 | ||
| 131 | // x mod y | |
| 132 | while (ex > ey) : (ex -= 1) { | |
| 133 | const i = ux - uy; | |
| 134 | if (i >> 63 == 0) { | |
| 135 | if (i == 0) { | |
| 136 | return 0 * x; | |
| 137 | } | |
| 138 | ux = i; | |
| 139 | } | |
| 140 | ux <<= 1; | |
| 141 | } | |
| 142 | { | |
| 143 | const i = ux - uy; | |
| 144 | if (i >> 63 == 0) { | |
| 145 | if (i == 0) { | |
| 146 | return 0 * x; | |
| 147 | } | |
| 148 | ux = i; | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | while (ux >> 52 == 0) : (ux <<= 1) { | |
| 153 | ex -= 1; | |
| 154 | } | |
| 155 | ||
| 156 | // scale result up | |
| 157 | if (ex > 0) { | |
| 158 | ux -= 1 << 52; | |
| 159 | ux |= u64(ex) << 52; | |
| 160 | } else { | |
| 161 | ux >>= u64(-ex + 1); | |
| 162 | } | |
| 163 | ||
| 164 | ux |= sx << 63; | |
| 165 | @bitCast(f64, ux) | |
| 166 | } | |
| 167 | ||
| 168 | // duplicate symbol clash with `fmod` test name | |
| 169 | test "fmod_" { | |
| 170 | assert(fmod(f32, 1.3, 2.5) == fmod32(1.3, 2.5)); | |
| 171 | assert(fmod(f64, 1.3, 2.5) == fmod64(1.3, 2.5)); | |
| 172 | } | |
| 173 | ||
| 174 | test "fmod32" { | |
| 175 | const epsilon = 0.000001; | |
| 176 | ||
| 177 | assert(math.approxEq(f32, fmod32(5.2, 2.0), 1.2, epsilon)); | |
| 178 | assert(math.approxEq(f32, fmod32(18.5, 4.2), 1.7, epsilon)); | |
| 179 | assert(math.approxEq(f32, fmod32(23, 48.34), 23.0, epsilon)); | |
| 180 | assert(math.approxEq(f32, fmod32(123.340890, 2398.2314), 123.340889, epsilon)); | |
| 181 | } | |
| 182 | ||
| 183 | test "fmod64" { | |
| 184 | const epsilon = 0.000001; | |
| 185 | ||
| 186 | assert(math.approxEq(f64, fmod64(5.2, 2.0), 1.2, epsilon)); | |
| 187 | assert(math.approxEq(f64, fmod64(18.5, 4.2), 1.7, epsilon)); | |
| 188 | assert(math.approxEq(f64, fmod64(23, 48.34), 23.0, epsilon)); | |
| 189 | assert(math.approxEq(f64, fmod64(123.340890, 2398.2314), 123.340889, epsilon)); | |
| 190 | } |
std/math/frexp.zig+68-44| ... | ... | @@ -1,89 +1,113 @@ |
| 1 | const assert = @import("../debug.zig").assert; | |
| 2 | 1 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | 3 | |
| 4 | pub fn frexp(x: var, e: &i32) -> @typeOf(x) { | |
| 4 | fn frexp_result(comptime T: type) -> type { | |
| 5 | struct { | |
| 6 | significand: T, | |
| 7 | exponent: i32, | |
| 8 | } | |
| 9 | } | |
| 10 | pub const frexp32_result = frexp_result(f32); | |
| 11 | pub const frexp64_result = frexp_result(f64); | |
| 12 | ||
| 13 | pub fn frexp(x: var) -> frexp_result(@typeOf(x)) { | |
| 5 | 14 | const T = @typeOf(x); |
| 6 | 15 | switch (T) { |
| 7 | f32 => frexp32(x, e), | |
| 8 | f64 => frexp64(x, e), | |
| 16 | f32 => @inlineCall(frexp32, x), | |
| 17 | f64 => @inlineCall(frexp64, x), | |
| 9 | 18 | else => @compileError("frexp not implemented for " ++ @typeName(T)), |
| 10 | 19 | } |
| 11 | 20 | } |
| 12 | 21 | |
| 13 | fn frexp32(x_: f32, e: &i32) -> f32 { | |
| 14 | var x = x_; | |
| 22 | fn frexp32(x: f32) -> frexp32_result { | |
| 23 | var result: frexp32_result = undefined; | |
| 24 | ||
| 15 | 25 | var y = @bitCast(u32, x); |
| 16 | const ee = i32(y >> 23) & 0xFF; | |
| 26 | const e = i32(y >> 23) & 0xFF; | |
| 17 | 27 | |
| 18 | if (ee == 0) { | |
| 28 | if (e == 0) { | |
| 19 | 29 | if (x != 0) { |
| 20 | x = frexp32(x * 0x1.0p64, e); | |
| 21 | *e -= 64; | |
| 30 | // subnormal | |
| 31 | result = frexp32(x * 0x1.0p64); | |
| 32 | result.exponent -= 64; | |
| 22 | 33 | } else { |
| 23 | *e = 0; | |
| 34 | // frexp(+-0) = (+-0, 0) | |
| 35 | result.significand = x; | |
| 36 | result.exponent = 0; | |
| 24 | 37 | } |
| 25 | return x; | |
| 26 | } else if (ee == 0xFF) { | |
| 27 | return x; | |
| 38 | return result; | |
| 39 | } else if (e == 0xFF) { | |
| 40 | // frexp(nan) = (nan, 0) | |
| 41 | result.significand = x; | |
| 42 | result.exponent = 0; | |
| 43 | return result; | |
| 28 | 44 | } |
| 29 | 45 | |
| 30 | *e = ee - 0x7E; | |
| 46 | result.exponent = e - 0x7E; | |
| 31 | 47 | y &= 0x807FFFFF; |
| 32 | 48 | y |= 0x3F000000; |
| 33 | @bitCast(f32, y) | |
| 49 | result.significand = @bitCast(f32, y); | |
| 50 | result | |
| 34 | 51 | } |
| 35 | 52 | |
| 36 | fn frexp64(x_: f64, e: &i32) -> f64 { | |
| 37 | var x = x_; | |
| 53 | fn frexp64(x: f64) -> frexp64_result { | |
| 54 | var result: frexp64_result = undefined; | |
| 55 | ||
| 38 | 56 | var y = @bitCast(u64, x); |
| 39 | const ee = i32(y >> 52) & 0x7FF; | |
| 57 | const e = i32(y >> 52) & 0x7FF; | |
| 40 | 58 | |
| 41 | if (ee == 0) { | |
| 59 | if (e == 0) { | |
| 42 | 60 | if (x != 0) { |
| 43 | x = frexp64(x * 0x1.0p64, e); | |
| 44 | *e -= 64; | |
| 61 | // subnormal | |
| 62 | result = frexp64(x * 0x1.0p64); | |
| 63 | result.exponent -= 64; | |
| 45 | 64 | } else { |
| 46 | *e = 0; | |
| 65 | // frexp(+-0) = (+-0, 0) | |
| 66 | result.significand = x; | |
| 67 | result.exponent = 0; | |
| 47 | 68 | } |
| 48 | return x; | |
| 49 | } else if (ee == 0x7FF) { | |
| 50 | return x; | |
| 69 | return result; | |
| 70 | } else if (e == 0x7FF) { | |
| 71 | // frexp(nan) = (nan, 0) | |
| 72 | result.significand = x; | |
| 73 | return result; | |
| 51 | 74 | } |
| 52 | 75 | |
| 53 | *e = ee - 0x3FE; | |
| 76 | result.exponent = e - 0x3FE; | |
| 54 | 77 | y &= 0x800FFFFFFFFFFFFF; |
| 55 | 78 | y |= 0x3FE0000000000000; |
| 56 | @bitCast(f64, y) | |
| 79 | result.significand = @bitCast(f64, y); | |
| 80 | result | |
| 57 | 81 | } |
| 58 | 82 | |
| 59 | 83 | test "frexp" { |
| 60 | var i0: i32 = undefined; | |
| 61 | var i1: i32 = undefined; | |
| 84 | const a = frexp(f32(1.3)); | |
| 85 | const b = frexp32(1.3); | |
| 86 | assert(a.significand == b.significand and a.exponent == b.exponent); | |
| 62 | 87 | |
| 63 | assert(frexp(f32(1.3), &i0) == frexp32(1.3, &i1)); | |
| 64 | assert(frexp(f64(1.3), &i0) == frexp64(1.3, &i1)); | |
| 88 | const c = frexp(f64(1.3)); | |
| 89 | const d = frexp64(1.3); | |
| 90 | assert(c.significand == d.significand and c.exponent == d.exponent); | |
| 65 | 91 | } |
| 66 | 92 | |
| 67 | 93 | test "frexp32" { |
| 68 | 94 | const epsilon = 0.000001; |
| 69 | var i: i32 = undefined; | |
| 70 | var d: f32 = undefined; | |
| 95 | var r: frexp32_result = undefined; | |
| 71 | 96 | |
| 72 | d = frexp32(1.3, &i); | |
| 73 | assert(math.approxEq(f32, d, 0.65, epsilon) and i == 1); | |
| 97 | r = frexp32(1.3); | |
| 98 | assert(math.approxEq(f32, r.significand, 0.65, epsilon) and r.exponent == 1); | |
| 74 | 99 | |
| 75 | d = frexp32(78.0234, &i); | |
| 76 | assert(math.approxEq(f32, d, 0.609558, epsilon) and i == 7); | |
| 100 | r = frexp32(78.0234); | |
| 101 | assert(math.approxEq(f32, r.significand, 0.609558, epsilon) and r.exponent == 7); | |
| 77 | 102 | } |
| 78 | 103 | |
| 79 | 104 | test "frexp64" { |
| 80 | 105 | const epsilon = 0.000001; |
| 81 | var i: i32 = undefined; | |
| 82 | var d: f64 = undefined; | |
| 106 | var r: frexp64_result = undefined; | |
| 83 | 107 | |
| 84 | d = frexp64(1.3, &i); | |
| 85 | assert(math.approxEq(f64, d, 0.65, epsilon) and i == 1); | |
| 108 | r = frexp64(1.3); | |
| 109 | assert(math.approxEq(f64, r.significand, 0.65, epsilon) and r.exponent == 1); | |
| 86 | 110 | |
| 87 | d = frexp64(78.0234, &i); | |
| 88 | assert(math.approxEq(f64, d, 0.609558, epsilon) and i == 7); | |
| 111 | r = frexp64(78.0234); | |
| 112 | assert(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7); | |
| 89 | 113 | } |
std/math/hypot.zig created+135| ... | ... | @@ -0,0 +1,135 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn hypot(comptime T: type, x: T, y: T) -> T { | |
| 5 | switch (T) { | |
| 6 | f32 => @inlineCall(hypot32, x, y), | |
| 7 | f64 => @inlineCall(hypot64, x, y), | |
| 8 | else => @compileError("hypot not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | fn hypot32(x: f32, y: f32) -> f32 { | |
| 13 | var ux = @bitCast(u32, x); | |
| 14 | var uy = @bitCast(u32, y); | |
| 15 | ||
| 16 | ux &= @maxValue(u32) >> 1; | |
| 17 | uy &= @maxValue(u32) >> 1; | |
| 18 | if (ux < uy) { | |
| 19 | const tmp = ux; | |
| 20 | ux = uy; | |
| 21 | uy = tmp; | |
| 22 | } | |
| 23 | ||
| 24 | var xx = @bitCast(f32, ux); | |
| 25 | var yy = @bitCast(f32, uy); | |
| 26 | if (uy == 0xFF << 23) { | |
| 27 | return yy; | |
| 28 | } | |
| 29 | if (ux >= 0xFF << 23 or uy == 0 or ux - uy >= (25 << 23)) { | |
| 30 | return xx + yy; | |
| 31 | } | |
| 32 | ||
| 33 | var z: f32 = 1.0; | |
| 34 | if (ux >= (0x7F+60) << 23) { | |
| 35 | z = 0x1.0p90; | |
| 36 | xx *= 0x1.0p-90; | |
| 37 | yy *= 0x1.0p-90; | |
| 38 | } else if (uy < (0x7F-60) << 23) { | |
| 39 | z = 0x1.0p-90; | |
| 40 | xx *= 0x1.0p-90; | |
| 41 | yy *= 0x1.0p-90; | |
| 42 | } | |
| 43 | ||
| 44 | z * math.sqrt(f32(f64(x) * x + f64(y) * y)) | |
| 45 | } | |
| 46 | ||
| 47 | fn sq(hi: &f64, lo: &f64, x: f64) { | |
| 48 | const split: f64 = 0x1.0p27 + 1.0; | |
| 49 | const xc = x * split; | |
| 50 | const xh = x - xc + xc; | |
| 51 | const xl = x - xh; | |
| 52 | *hi = x * x; | |
| 53 | *lo = xh * xh - *hi + 2 * xh * xl + xl * xl; | |
| 54 | } | |
| 55 | ||
| 56 | fn hypot64(x: f64, y: f64) -> f64 { | |
| 57 | var ux = @bitCast(u64, x); | |
| 58 | var uy = @bitCast(u64, y); | |
| 59 | ||
| 60 | ux &= @maxValue(u64) >> 1; | |
| 61 | uy &= @maxValue(u64) >> 1; | |
| 62 | if (ux < uy) { | |
| 63 | const tmp = ux; | |
| 64 | ux = uy; | |
| 65 | uy = tmp; | |
| 66 | } | |
| 67 | ||
| 68 | const ex = ux >> 52; | |
| 69 | const ey = uy >> 52; | |
| 70 | var xx = @bitCast(f64, ux); | |
| 71 | var yy = @bitCast(f64, uy); | |
| 72 | ||
| 73 | // hypot(inf, nan) == inf | |
| 74 | if (ey == 0x7FF) { | |
| 75 | return yy; | |
| 76 | } | |
| 77 | if (ex == 0x7FF or uy == 0) { | |
| 78 | return xx; | |
| 79 | } | |
| 80 | ||
| 81 | // hypot(x, y) ~= x + y * y / x / 2 with inexact for small y/x | |
| 82 | if (ex - ey > 64) { | |
| 83 | return xx + yy; | |
| 84 | } | |
| 85 | ||
| 86 | var z: f64 = 1; | |
| 87 | if (ex > 0x3FF + 510) { | |
| 88 | z = 0x1.0p700; | |
| 89 | xx *= 0x1.0p-700; | |
| 90 | yy *= 0x1.0p-700; | |
| 91 | } else if (ey < 0x3FF - 450) { | |
| 92 | z = 0x1.0p-700; | |
| 93 | xx *= 0x1.0p700; | |
| 94 | yy *= 0x1.0p700; | |
| 95 | } | |
| 96 | ||
| 97 | var hx: f64 = undefined; | |
| 98 | var lx: f64 = undefined; | |
| 99 | var hy: f64 = undefined; | |
| 100 | var ly: f64 = undefined; | |
| 101 | ||
| 102 | sq(&hx, &lx, x); | |
| 103 | sq(&hy, &ly, y); | |
| 104 | ||
| 105 | z * math.sqrt(ly + lx + hy + hx) | |
| 106 | } | |
| 107 | ||
| 108 | test "hypot" { | |
| 109 | assert(hypot(f32, 0.0, -1.2) == hypot32(0.0, -1.2)); | |
| 110 | assert(hypot(f64, 0.0, -1.2) == hypot64(0.0, -1.2)); | |
| 111 | } | |
| 112 | ||
| 113 | test "hypot32" { | |
| 114 | const epsilon = 0.000001; | |
| 115 | ||
| 116 | assert(math.approxEq(f32, hypot32(0.0, -1.2), 1.2, epsilon)); | |
| 117 | assert(math.approxEq(f32, hypot32(0.2, -0.34), 0.394462, epsilon)); | |
| 118 | assert(math.approxEq(f32, hypot32(0.8923, 2.636890), 2.783772, epsilon)); | |
| 119 | assert(math.approxEq(f32, hypot32(1.5, 5.25), 5.460083, epsilon)); | |
| 120 | assert(math.approxEq(f32, hypot32(37.45, 159.835), 164.163742, epsilon)); | |
| 121 | assert(math.approxEq(f32, hypot32(89.123, 382.028905), 392.286865, epsilon)); | |
| 122 | assert(math.approxEq(f32, hypot32(123123.234375, 529428.707813), 543556.875, epsilon)); | |
| 123 | } | |
| 124 | ||
| 125 | test "hypot64" { | |
| 126 | const epsilon = 0.000001; | |
| 127 | ||
| 128 | assert(math.approxEq(f64, hypot64(0.0, -1.2), 1.2, epsilon)); | |
| 129 | assert(math.approxEq(f64, hypot64(0.2, -0.34), 0.394462, epsilon)); | |
| 130 | assert(math.approxEq(f64, hypot64(0.8923, 2.636890), 2.783772, epsilon)); | |
| 131 | assert(math.approxEq(f64, hypot64(1.5, 5.25), 5.460082, epsilon)); | |
| 132 | assert(math.approxEq(f64, hypot64(37.45, 159.835), 164.163728, epsilon)); | |
| 133 | assert(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon)); | |
| 134 | assert(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon)); | |
| 135 | } |
std/math/ilogb.zig created+100| ... | ... | @@ -0,0 +1,100 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn ilogb(x: var) -> i32 { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(ilogb32, x), | |
| 8 | f64 => @inlineCall(ilogb64, x), | |
| 9 | else => @compileError("ilogb not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | // NOTE: Should these be exposed publically? | |
| 14 | const fp_ilogbnan = -1 - i32(@maxValue(u32) >> 1); | |
| 15 | const fp_ilogb0 = fp_ilogbnan; | |
| 16 | ||
| 17 | fn ilogb32(x: f32) -> i32 { | |
| 18 | var u = @bitCast(u32, x); | |
| 19 | var e = i32((u >> 23) & 0xFF); | |
| 20 | ||
| 21 | if (e == 0) { | |
| 22 | u <<= 9; | |
| 23 | if (u == 0) { | |
| 24 | math.raiseInvalid(); | |
| 25 | return fp_ilogb0; | |
| 26 | } | |
| 27 | ||
| 28 | // subnormal | |
| 29 | e = -0x7F; | |
| 30 | while (u >> 31 == 0) : (u <<= 1) { | |
| 31 | e -= 1; | |
| 32 | } | |
| 33 | return e; | |
| 34 | } | |
| 35 | ||
| 36 | if (e == 0xFF) { | |
| 37 | math.raiseInvalid(); | |
| 38 | if (u << 9 != 0) { | |
| 39 | return fp_ilogbnan; | |
| 40 | } else { | |
| 41 | return @maxValue(i32); | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | e - 0x7F | |
| 46 | } | |
| 47 | ||
| 48 | fn ilogb64(x: f64) -> i32 { | |
| 49 | var u = @bitCast(u64, x); | |
| 50 | var e = i32((u >> 52) & 0x7FF); | |
| 51 | ||
| 52 | if (e == 0) { | |
| 53 | u <<= 12; | |
| 54 | if (u == 0) { | |
| 55 | math.raiseInvalid(); | |
| 56 | return fp_ilogb0; | |
| 57 | } | |
| 58 | ||
| 59 | // subnormal | |
| 60 | e = -0x3FF; | |
| 61 | while (u >> 63 == 0) : (u <<= 1) { | |
| 62 | e -= 1; | |
| 63 | } | |
| 64 | return e; | |
| 65 | } | |
| 66 | ||
| 67 | if (e == 0x7FF) { | |
| 68 | math.raiseInvalid(); | |
| 69 | if (u << 12 != 0) { | |
| 70 | return fp_ilogbnan; | |
| 71 | } else { | |
| 72 | return @maxValue(i32); | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | e - 0x3FF | |
| 77 | } | |
| 78 | ||
| 79 | test "ilogb" { | |
| 80 | assert(ilogb(f32(0.2)) == ilogb32(0.2)); | |
| 81 | assert(ilogb(f64(0.2)) == ilogb64(0.2)); | |
| 82 | } | |
| 83 | ||
| 84 | test "ilogb32" { | |
| 85 | assert(ilogb32(0.0) == fp_ilogb0); | |
| 86 | assert(ilogb32(0.5) == -1); | |
| 87 | assert(ilogb32(0.8923) == -1); | |
| 88 | assert(ilogb32(10.0) == 3); | |
| 89 | assert(ilogb32(-123984) == 16); | |
| 90 | assert(ilogb32(2398.23) == 11); | |
| 91 | } | |
| 92 | ||
| 93 | test "ilogb64" { | |
| 94 | assert(ilogb64(0.0) == fp_ilogb0); | |
| 95 | assert(ilogb64(0.5) == -1); | |
| 96 | assert(ilogb64(0.8923) == -1); | |
| 97 | assert(ilogb64(10.0) == 3); | |
| 98 | assert(ilogb64(-123984) == 16); | |
| 99 | assert(ilogb64(2398.23) == 11); | |
| 100 | } |
std/math/index.zig+183-116| ... | ... | @@ -1,7 +1,189 @@ |
| 1 | const assert = @import("../debug.zig").assert; | |
| 2 | 1 | const builtin = @import("builtin"); |
| 2 | const TypeId = builtin.TypeId; | |
| 3 | const assert = @import("../debug.zig").assert; | |
| 4 | ||
| 5 | pub const e = 2.7182818284590452354; // e | |
| 6 | pub const log2_e = 1.4426950408889634074; // log_2(e) | |
| 7 | pub const log10_e = 0.43429448190325182765; // log_10(e) | |
| 8 | pub const ln_2 = 0.69314718055994530942; // log_e(2) | |
| 9 | pub const ln_10 = 2.30258509299404568402; // log_e(10) | |
| 10 | pub const pi = 3.14159265358979323846; // pi | |
| 11 | pub const pi_2 = 1.57079632679489661923; // pi/2 | |
| 12 | pub const pi_4 = 0.78539816339744830962; // pi/4 | |
| 13 | pub const r1_pi = 0.31830988618379067154; // 1/pi | |
| 14 | pub const r2_pi = 0.63661977236758134308; // 2/pi | |
| 15 | pub const r2_sqrtpi = 1.12837916709551257390; // 2/sqrt(pi) | |
| 16 | pub const sqrt2 = 1.41421356237309504880; // sqrt(2) | |
| 17 | pub const r1_sqrt2 = 0.70710678118654752440; // 1/sqrt(2) | |
| 18 | ||
| 19 | // float.h details | |
| 20 | pub const f64_true_min = 4.94065645841246544177e-324; | |
| 21 | pub const f64_min = 2.22507385850720138309e-308; | |
| 22 | pub const f64_max = 1.79769313486231570815e+308; | |
| 23 | pub const f64_epsilon = 2.22044604925031308085e-16; | |
| 24 | pub const f64_toint = 1.0 / f64_epsilon; | |
| 25 | ||
| 26 | pub const f32_true_min = 1.40129846432481707092e-45; | |
| 27 | pub const f32_min = 1.17549435082228750797e-38; | |
| 28 | pub const f32_max = 3.40282346638528859812e+38; | |
| 29 | pub const f32_epsilon = 1.1920928955078125e-07; | |
| 30 | pub const f32_toint = 1.0 / f32_epsilon; | |
| 31 | ||
| 32 | pub const nan_u32 = u32(0x7F800001); | |
| 33 | pub const nan_f32 = @bitCast(f32, nan_u32); | |
| 34 | ||
| 35 | pub const inf_u32 = u32(0x7F800000); | |
| 36 | pub const inf_f32 = @bitCast(f32, inf_u32); | |
| 37 | ||
| 38 | pub const nan_u64 = u64(0x7FF << 52) | 1; | |
| 39 | pub const nan_f64 = @bitCast(f64, nan_u64); | |
| 40 | ||
| 41 | pub const inf_u64 = u64(0x7FF << 52); | |
| 42 | pub const inf_f64 = @bitCast(f64, inf_u64); | |
| 43 | ||
| 44 | pub const nan = @import("nan.zig").nan; | |
| 45 | pub const inf = @import("inf.zig").inf; | |
| 46 | ||
| 47 | pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool { | |
| 48 | assert(@typeId(T) == TypeId.Float); | |
| 49 | fabs(x - y) < epsilon | |
| 50 | } | |
| 3 | 51 | |
| 52 | // TODO: Hide the following in an internal module. | |
| 53 | pub fn forceEval(value: var) { | |
| 54 | const T = @typeOf(value); | |
| 55 | switch (T) { | |
| 56 | f32 => { | |
| 57 | var x: f32 = undefined; | |
| 58 | const p = @ptrCast(&volatile f32, &x); | |
| 59 | *p = x; | |
| 60 | }, | |
| 61 | f64 => { | |
| 62 | var x: f64 = undefined; | |
| 63 | const p = @ptrCast(&volatile f64, &x); | |
| 64 | *p = x; | |
| 65 | }, | |
| 66 | else => { | |
| 67 | @compileError("forceEval not implemented for " ++ @typeName(T)); | |
| 68 | }, | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | pub fn raiseInvalid() { | |
| 73 | // Raise INVALID fpu exception | |
| 74 | } | |
| 75 | ||
| 76 | pub fn raiseUnderflow() { | |
| 77 | // Raise UNDERFLOW fpu exception | |
| 78 | } | |
| 79 | ||
| 80 | pub fn raiseOverflow() { | |
| 81 | // Raise OVERFLOW fpu exception | |
| 82 | } | |
| 83 | ||
| 84 | pub fn raiseInexact() { | |
| 85 | // Raise INEXACT fpu exception | |
| 86 | } | |
| 87 | ||
| 88 | pub fn raiseDivByZero() { | |
| 89 | // Raise INEXACT fpu exception | |
| 90 | } | |
| 91 | ||
| 92 | pub const isNan = @import("isnan.zig").isNan; | |
| 93 | pub const fabs = @import("fabs.zig").fabs; | |
| 94 | pub const ceil = @import("ceil.zig").ceil; | |
| 95 | pub const floor = @import("floor.zig").floor; | |
| 96 | pub const trunc = @import("floor.zig").trunc; | |
| 97 | pub const round = @import("round.zig").round; | |
| 4 | 98 | pub const frexp = @import("frexp.zig").frexp; |
| 99 | pub const frexp32_result = @import("frexp.zig").frexp32_result; | |
| 100 | pub const frexp64_result = @import("frexp.zig").frexp64_result; | |
| 101 | pub const fmod = @import("fmod.zig").fmod; | |
| 102 | pub const modf = @import("modf.zig").modf; | |
| 103 | pub const modf32_result = @import("modf.zig").modf32_result; | |
| 104 | pub const modf64_result = @import("modf.zig").modf64_result; | |
| 105 | pub const copysign = @import("copysign.zig").copysign; | |
| 106 | pub const isFinite = @import("isfinite.zig").isFinite; | |
| 107 | pub const isInf = @import("isinf.zig").isInf; | |
| 108 | pub const isPositiveInf = @import("isinf.zig").isPositiveInf; | |
| 109 | pub const isNegativeInf = @import("isinf.zig").isNegativeInf; | |
| 110 | pub const isNormal = @import("isnormal.zig").isNormal; | |
| 111 | pub const signbit = @import("signbit.zig").signbit; | |
| 112 | pub const scalbn = @import("scalbn.zig").scalbn; | |
| 113 | pub const pow = @import("pow.zig").pow; | |
| 114 | pub const sqrt = @import("sqrt.zig").sqrt; | |
| 115 | pub const cbrt = @import("cbrt.zig").cbrt; | |
| 116 | pub const acos = @import("acos.zig").acos; | |
| 117 | pub const asin = @import("asin.zig").asin; | |
| 118 | pub const atan = @import("atan.zig").atan; | |
| 119 | pub const atan2 = @import("atan2.zig").atan2; | |
| 120 | pub const hypot = @import("hypot.zig").hypot; | |
| 121 | pub const exp = @import("exp.zig").exp; | |
| 122 | pub const exp2 = @import("exp2.zig").exp2; | |
| 123 | pub const expm1 = @import("expm1.zig").expm1; | |
| 124 | pub const ilogb = @import("ilogb.zig").ilogb; | |
| 125 | pub const ln = @import("ln.zig").ln; | |
| 126 | pub const log = @import("log.zig").log; | |
| 127 | pub const log2 = @import("log2.zig").log2; | |
| 128 | pub const log10 = @import("log10.zig").log10; | |
| 129 | pub const log1p = @import("log1p.zig").log1p; | |
| 130 | pub const fma = @import("fma.zig").fma; | |
| 131 | pub const asinh = @import("asinh.zig").asinh; | |
| 132 | pub const acosh = @import("acosh.zig").acosh; | |
| 133 | pub const atanh = @import("atanh.zig").atanh; | |
| 134 | pub const sinh = @import("sinh.zig").sinh; | |
| 135 | pub const cosh = @import("cosh.zig").cosh; | |
| 136 | pub const tanh = @import("tanh.zig").tanh; | |
| 137 | pub const cos = @import("cos.zig").cos; | |
| 138 | pub const sin = @import("sin.zig").sin; | |
| 139 | pub const tan = @import("tan.zig").tan; | |
| 140 | ||
| 141 | test "math" { | |
| 142 | _ = @import("nan.zig"); | |
| 143 | _ = @import("isnan.zig"); | |
| 144 | _ = @import("fabs.zig"); | |
| 145 | _ = @import("ceil.zig"); | |
| 146 | _ = @import("floor.zig"); | |
| 147 | _ = @import("trunc.zig"); | |
| 148 | _ = @import("round.zig"); | |
| 149 | _ = @import("frexp.zig"); | |
| 150 | _ = @import("fmod.zig"); | |
| 151 | _ = @import("modf.zig"); | |
| 152 | _ = @import("copysign.zig"); | |
| 153 | _ = @import("isfinite.zig"); | |
| 154 | _ = @import("isinf.zig"); | |
| 155 | _ = @import("isnormal.zig"); | |
| 156 | _ = @import("signbit.zig"); | |
| 157 | _ = @import("scalbn.zig"); | |
| 158 | _ = @import("pow.zig"); | |
| 159 | _ = @import("sqrt.zig"); | |
| 160 | _ = @import("cbrt.zig"); | |
| 161 | _ = @import("acos.zig"); | |
| 162 | _ = @import("asin.zig"); | |
| 163 | _ = @import("atan.zig"); | |
| 164 | _ = @import("atan2.zig"); | |
| 165 | _ = @import("hypot.zig"); | |
| 166 | _ = @import("exp.zig"); | |
| 167 | _ = @import("exp2.zig"); | |
| 168 | _ = @import("expm1.zig"); | |
| 169 | _ = @import("ilogb.zig"); | |
| 170 | _ = @import("ln.zig"); | |
| 171 | _ = @import("log.zig"); | |
| 172 | _ = @import("log2.zig"); | |
| 173 | _ = @import("log10.zig"); | |
| 174 | _ = @import("log1p.zig"); | |
| 175 | _ = @import("fma.zig"); | |
| 176 | _ = @import("asinh.zig"); | |
| 177 | _ = @import("acosh.zig"); | |
| 178 | _ = @import("atanh.zig"); | |
| 179 | _ = @import("sinh.zig"); | |
| 180 | _ = @import("cosh.zig"); | |
| 181 | _ = @import("tanh.zig"); | |
| 182 | _ = @import("sin.zig"); | |
| 183 | _ = @import("cos.zig"); | |
| 184 | _ = @import("tan.zig"); | |
| 185 | } | |
| 186 | ||
| 5 | 187 | |
| 6 | 188 | pub const Cmp = enum { |
| 7 | 189 | Less, |
| ... | ... | @@ -66,25 +248,6 @@ fn testOverflow() { |
| 66 | 248 | } |
| 67 | 249 | |
| 68 | 250 | |
| 69 | pub fn log(comptime base: usize, value: var) -> @typeOf(value) { | |
| 70 | const T = @typeOf(value); | |
| 71 | switch (@typeId(T)) { | |
| 72 | builtin.TypeId.Int => { | |
| 73 | if (base == 2) { | |
| 74 | return T.bit_count - 1 - @clz(value); | |
| 75 | } else { | |
| 76 | @compileError("TODO implement log for non base 2 integers"); | |
| 77 | } | |
| 78 | }, | |
| 79 | builtin.TypeId.Float => { | |
| 80 | @compileError("TODO implement log for floats"); | |
| 81 | }, | |
| 82 | else => { | |
| 83 | @compileError("log expects integer or float, found '" ++ @typeName(T) ++ "'"); | |
| 84 | }, | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | 251 | error Overflow; |
| 89 | 252 | pub fn absInt(x: var) -> %@typeOf(x) { |
| 90 | 253 | const T = @typeOf(x); |
| ... | ... | @@ -244,92 +407,6 @@ fn testRem() { |
| 244 | 407 | if (rem(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); |
| 245 | 408 | } |
| 246 | 409 | |
| 247 | fn isNan(comptime T: type, x: T) -> bool { | |
| 248 | assert(@typeId(T) == builtin.TypeId.Float); | |
| 249 | if (T == f32) { | |
| 250 | const bits = @bitCast(u32, x); | |
| 251 | return (bits & 0x7fffffff) > 0x7f800000; | |
| 252 | } else if (T == f64) { | |
| 253 | const bits = @bitCast(u64, x); | |
| 254 | return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52); | |
| 255 | } else if (T == c_longdouble) { | |
| 256 | @compileError("TODO support isNan for c_longdouble"); | |
| 257 | } else { | |
| 258 | unreachable; | |
| 259 | } | |
| 260 | } | |
| 261 | ||
| 262 | pub fn floor(x: var) -> @typeOf(x) { | |
| 263 | switch (@typeOf(x)) { | |
| 264 | f32 => floor_f32(x), | |
| 265 | f64 => floor_f64(x), | |
| 266 | c_longdouble => @compileError("TODO support floor for c_longdouble"), | |
| 267 | else => @compileError("Invalid type for floor: " ++ @typeName(@typeOf(x))), | |
| 268 | } | |
| 269 | } | |
| 270 | ||
| 271 | fn floor_f32(x: f32) -> f32 { | |
| 272 | var i = @bitCast(u32, x); | |
| 273 | const e = i32((i >> 23) & 0xff) -% 0x7f; | |
| 274 | if (e >= 23) | |
| 275 | return x; | |
| 276 | if (e >= 0) { | |
| 277 | const m = @bitCast(u32, 0x007fffff >> e); | |
| 278 | if ((i & m) == 0) | |
| 279 | return x; | |
| 280 | if (i >> 31 != 0) | |
| 281 | i +%= m; | |
| 282 | i &= ~m; | |
| 283 | } else { | |
| 284 | if (i >> 31 == 0) | |
| 285 | return 0; | |
| 286 | if (i <<% 1 != 0) | |
| 287 | return -1.0; | |
| 288 | } | |
| 289 | return @bitCast(f32, i); | |
| 290 | } | |
| 291 | ||
| 292 | fn floor_f64(x: f64) -> f64 { | |
| 293 | const DBL_EPSILON = 2.22044604925031308085e-16; | |
| 294 | const toint = 1.0 / DBL_EPSILON; | |
| 295 | ||
| 296 | var i = @bitCast(u64, x); | |
| 297 | const e = (i >> 52) & 0x7ff; | |
| 298 | ||
| 299 | if (e >= 0x3ff +% 52 or x == 0) | |
| 300 | return x; | |
| 301 | // y = int(x) - x, where int(x) is an integer neighbor of x | |
| 302 | const y = { | |
| 303 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 304 | if (i >> 63 != 0) { | |
| 305 | x - toint + toint - x | |
| 306 | } else { | |
| 307 | x + toint - toint - x | |
| 308 | } | |
| 309 | }; | |
| 310 | // special case because of non-nearest rounding modes | |
| 311 | if (e <= 0x3ff - 1) { | |
| 312 | if (i >> 63 != 0) | |
| 313 | return -1.0; | |
| 314 | return 0.0; | |
| 315 | } | |
| 316 | if (y > 0) | |
| 317 | return x + y - 1; | |
| 318 | return x + y; | |
| 319 | } | |
| 320 | ||
| 321 | test "math.floor" { | |
| 322 | assert(floor(f32(1.234)) == 1.0); | |
| 323 | assert(floor(f32(-1.234)) == -2.0); | |
| 324 | assert(floor(f32(999.0)) == 999.0); | |
| 325 | assert(floor(f32(-999.0)) == -999.0); | |
| 326 | ||
| 327 | assert(floor(f64(1.234)) == 1.0); | |
| 328 | assert(floor(f64(-1.234)) == -2.0); | |
| 329 | assert(floor(f64(999.0)) == 999.0); | |
| 330 | assert(floor(f64(-999.0)) == -999.0); | |
| 331 | } | |
| 332 | ||
| 333 | 410 | /// Returns the absolute value of the integer parameter. |
| 334 | 411 | /// Result is an unsigned integer. |
| 335 | 412 | pub fn absCast(x: var) -> @IntType(false, @typeOf(x).bit_count) { |
| ... | ... | @@ -377,13 +454,3 @@ test "math.negateCast" { |
| 377 | 454 | |
| 378 | 455 | if (negateCast(u32(@maxValue(i32) + 10))) |_| unreachable else |err| assert(err == error.Overflow); |
| 379 | 456 | } |
| 380 | ||
| 381 | test "math" { | |
| 382 | _ = @import("frexp.zig"); | |
| 383 | } | |
| 384 | ||
| 385 | ||
| 386 | pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool { | |
| 387 | comptime assert(@typeId(T) == builtin.TypeId.Float); | |
| 388 | absFloat(x - y) < epsilon | |
| 389 | } |
std/math/inf.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn inf(comptime T: type) -> T { | |
| 5 | switch (T) { | |
| 6 | f32 => @bitCast(f32, math.inf_u32), | |
| 7 | f64 => @bitCast(f64, math.inf_u64), | |
| 8 | else => @compileError("inf not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } |
std/math/isfinite.zig created+30| ... | ... | @@ -0,0 +1,30 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn isFinite(x: var) -> bool { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => { | |
| 8 | const bits = @bitCast(u32, x); | |
| 9 | bits & 0x7FFFFFFF < 0x7F800000 | |
| 10 | }, | |
| 11 | f64 => { | |
| 12 | const bits = @bitCast(u64, x); | |
| 13 | bits & (@maxValue(u64) >> 1) < (0x7FF << 52) | |
| 14 | }, | |
| 15 | else => { | |
| 16 | @compileError("isFinite not implemented for " ++ @typeName(T)); | |
| 17 | }, | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | test "isFinite" { | |
| 22 | assert(isFinite(f32(0.0))); | |
| 23 | assert(isFinite(f32(-0.0))); | |
| 24 | assert(isFinite(f64(0.0))); | |
| 25 | assert(isFinite(f64(-0.0))); | |
| 26 | assert(!isFinite(math.inf(f32))); | |
| 27 | assert(!isFinite(-math.inf(f32))); | |
| 28 | assert(!isFinite(math.inf(f64))); | |
| 29 | assert(!isFinite(-math.inf(f64))); | |
| 30 | } |
std/math/isinf.zig created+82| ... | ... | @@ -0,0 +1,82 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn isInf(x: var) -> bool { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => { | |
| 8 | const bits = @bitCast(u32, x); | |
| 9 | bits & 0x7FFFFFFF == 0x7F800000 | |
| 10 | }, | |
| 11 | f64 => { | |
| 12 | const bits = @bitCast(u64, x); | |
| 13 | bits & (@maxValue(u64) >> 1) == (0x7FF << 52) | |
| 14 | }, | |
| 15 | else => { | |
| 16 | @compileError("isInf not implemented for " ++ @typeName(T)); | |
| 17 | }, | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | pub fn isPositiveInf(x: var) -> bool { | |
| 22 | const T = @typeOf(x); | |
| 23 | switch (T) { | |
| 24 | f32 => { | |
| 25 | @bitCast(u32, x) == 0x7F800000 | |
| 26 | }, | |
| 27 | f64 => { | |
| 28 | @bitCast(u64, x) == 0x7FF << 52 | |
| 29 | }, | |
| 30 | else => { | |
| 31 | @compileError("isPositiveInf not implemented for " ++ @typeName(T)); | |
| 32 | }, | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | pub fn isNegativeInf(x: var) -> bool { | |
| 37 | const T = @typeOf(x); | |
| 38 | switch (T) { | |
| 39 | f32 => { | |
| 40 | @bitCast(u32, x) == 0xFF800000 | |
| 41 | }, | |
| 42 | f64 => { | |
| 43 | @bitCast(u64, x) == 0xFFF << 52 | |
| 44 | }, | |
| 45 | else => { | |
| 46 | @compileError("isNegativeInf not implemented for " ++ @typeName(T)); | |
| 47 | }, | |
| 48 | } | |
| 49 | } | |
| 50 | ||
| 51 | test "isInf" { | |
| 52 | assert(!isInf(f32(0.0))); | |
| 53 | assert(!isInf(f32(-0.0))); | |
| 54 | assert(!isInf(f64(0.0))); | |
| 55 | assert(!isInf(f64(-0.0))); | |
| 56 | assert(isInf(math.inf(f32))); | |
| 57 | assert(isInf(-math.inf(f32))); | |
| 58 | assert(isInf(math.inf(f64))); | |
| 59 | assert(isInf(-math.inf(f64))); | |
| 60 | } | |
| 61 | ||
| 62 | test "isPositiveInf" { | |
| 63 | assert(!isPositiveInf(f32(0.0))); | |
| 64 | assert(!isPositiveInf(f32(-0.0))); | |
| 65 | assert(!isPositiveInf(f64(0.0))); | |
| 66 | assert(!isPositiveInf(f64(-0.0))); | |
| 67 | assert(isPositiveInf(math.inf(f32))); | |
| 68 | assert(!isPositiveInf(-math.inf(f32))); | |
| 69 | assert(isPositiveInf(math.inf(f64))); | |
| 70 | assert(!isPositiveInf(-math.inf(f64))); | |
| 71 | } | |
| 72 | ||
| 73 | test "isNegativeInf" { | |
| 74 | assert(!isNegativeInf(f32(0.0))); | |
| 75 | assert(!isNegativeInf(f32(-0.0))); | |
| 76 | assert(!isNegativeInf(f64(0.0))); | |
| 77 | assert(!isNegativeInf(f64(-0.0))); | |
| 78 | assert(!isNegativeInf(math.inf(f32))); | |
| 79 | assert(isNegativeInf(-math.inf(f32))); | |
| 80 | assert(!isNegativeInf(math.inf(f64))); | |
| 81 | assert(isNegativeInf(-math.inf(f64))); | |
| 82 | } |
std/math/isnan.zig created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn isNan(x: var) -> bool { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => { | |
| 8 | const bits = @bitCast(u32, x); | |
| 9 | bits & 0x7FFFFFFF > 0x7F800000 | |
| 10 | }, | |
| 11 | f64 => { | |
| 12 | const bits = @bitCast(u64, x); | |
| 13 | (bits & (@maxValue(u64) >> 1)) > (u64(0x7FF) << 52) | |
| 14 | }, | |
| 15 | else => { | |
| 16 | @compileError("isNan not implemented for " ++ @typeName(T)); | |
| 17 | }, | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | test "isNan" { | |
| 22 | assert(isNan(math.nan(f32))); | |
| 23 | assert(isNan(math.nan(f64))); | |
| 24 | assert(!isNan(f32(1.0))); | |
| 25 | assert(!isNan(f64(1.0))); | |
| 26 | } |
std/math/isnormal.zig created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn isNormal(x: var) -> bool { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => { | |
| 8 | const bits = @bitCast(u32, x); | |
| 9 | (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000 | |
| 10 | }, | |
| 11 | f64 => { | |
| 12 | const bits = @bitCast(u64, x); | |
| 13 | (bits + (1 << 52)) & (@maxValue(u64) >> 1) >= (1 << 53) | |
| 14 | }, | |
| 15 | else => { | |
| 16 | @compileError("isNormal not implemented for " ++ @typeName(T)); | |
| 17 | }, | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | test "isNormal" { | |
| 22 | assert(!isNormal(math.nan(f32))); | |
| 23 | assert(!isNormal(math.nan(f64))); | |
| 24 | assert(isNormal(f32(1.0))); | |
| 25 | assert(isNormal(f64(1.0))); | |
| 26 | } |
std/math/ln.zig created+148| ... | ... | @@ -0,0 +1,148 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn ln(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(lnf, x), | |
| 8 | f64 => @inlineCall(lnd, x), | |
| 9 | else => @compileError("ln not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn lnf(x_: f32) -> f32 { | |
| 14 | const ln2_hi: f32 = 6.9313812256e-01; | |
| 15 | const ln2_lo: f32 = 9.0580006145e-06; | |
| 16 | const Lg1: f32 = 0xaaaaaa.0p-24; | |
| 17 | const Lg2: f32 = 0xccce13.0p-25; | |
| 18 | const Lg3: f32 = 0x91e9ee.0p-25; | |
| 19 | const Lg4: f32 = 0xf89e26.0p-26; | |
| 20 | ||
| 21 | var x = x_; | |
| 22 | var ix = @bitCast(u32, x); | |
| 23 | var k: i32 = 0; | |
| 24 | ||
| 25 | // x < 2^(-126) | |
| 26 | if (ix < 0x00800000 or ix >> 31 != 0) { | |
| 27 | // log(+-0) = -inf | |
| 28 | if (ix << 1 == 0) { | |
| 29 | return -1 / (x * x); | |
| 30 | } | |
| 31 | // log(-#) = nan | |
| 32 | if (ix >> 31 != 0) { | |
| 33 | return (x - x) / 0.0 | |
| 34 | } | |
| 35 | ||
| 36 | // subnormal, scale x | |
| 37 | k -= 25; | |
| 38 | x *= 0x1.0p25; | |
| 39 | ix = @bitCast(u32, x); | |
| 40 | } else if (ix >= 0x7F800000) { | |
| 41 | return x; | |
| 42 | } else if (ix == 0x3F800000) { | |
| 43 | return 0; | |
| 44 | } | |
| 45 | ||
| 46 | // x into [sqrt(2) / 2, sqrt(2)] | |
| 47 | ix += 0x3F800000 - 0x3F3504F3; | |
| 48 | k += i32(ix >> 23) - 0x7F; | |
| 49 | ix = (ix & 0x007FFFFF) + 0x3F3504F3; | |
| 50 | x = @bitCast(f32, ix); | |
| 51 | ||
| 52 | const f = x - 1.0; | |
| 53 | const s = f / (2.0 + f); | |
| 54 | const z = s * s; | |
| 55 | const w = z * z; | |
| 56 | const t1 = w * (Lg2 + w * Lg4); | |
| 57 | const t2 = z * (Lg1 + w * Lg3); | |
| 58 | const R = t2 + t1; | |
| 59 | const hfsq = 0.5 * f * f; | |
| 60 | const dk = f32(k); | |
| 61 | ||
| 62 | s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi | |
| 63 | } | |
| 64 | ||
| 65 | fn lnd(x_: f64) -> f64 { | |
| 66 | const ln2_hi: f64 = 6.93147180369123816490e-01; | |
| 67 | const ln2_lo: f64 = 1.90821492927058770002e-10; | |
| 68 | const Lg1: f64 = 6.666666666666735130e-01; | |
| 69 | const Lg2: f64 = 3.999999999940941908e-01; | |
| 70 | const Lg3: f64 = 2.857142874366239149e-01; | |
| 71 | const Lg4: f64 = 2.222219843214978396e-01; | |
| 72 | const Lg5: f64 = 1.818357216161805012e-01; | |
| 73 | const Lg6: f64 = 1.531383769920937332e-01; | |
| 74 | const Lg7: f64 = 1.479819860511658591e-01; | |
| 75 | ||
| 76 | var x = x_; | |
| 77 | var ix = @bitCast(u64, x); | |
| 78 | var hx = u32(ix >> 32); | |
| 79 | var k: i32 = 0; | |
| 80 | ||
| 81 | if (hx < 0x00100000 or hx >> 31 != 0) { | |
| 82 | // log(+-0) = -inf | |
| 83 | if (ix << 1 == 0) { | |
| 84 | return -1 / (x * x); | |
| 85 | } | |
| 86 | // log(-#) = nan | |
| 87 | if (hx >> 31 != 0) { | |
| 88 | return (x - x) / 0.0; | |
| 89 | } | |
| 90 | ||
| 91 | // subnormal, scale x | |
| 92 | k -= 54; | |
| 93 | x *= 0x1.0p54; | |
| 94 | hx = u32(@bitCast(u64, ix) >> 32) | |
| 95 | } | |
| 96 | else if (hx >= 0x7FF00000) { | |
| 97 | return x; | |
| 98 | } | |
| 99 | else if (hx == 0x3FF00000 and ix << 32 == 0) { | |
| 100 | return 0; | |
| 101 | } | |
| 102 | ||
| 103 | // x into [sqrt(2) / 2, sqrt(2)] | |
| 104 | hx += 0x3FF00000 - 0x3FE6A09E; | |
| 105 | k += i32(hx >> 20) - 0x3FF; | |
| 106 | hx = (hx & 0x000FFFFF) + 0x3FE6A09E; | |
| 107 | ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); | |
| 108 | x = @bitCast(f64, ix); | |
| 109 | ||
| 110 | const f = x - 1.0; | |
| 111 | const hfsq = 0.5 * f * f; | |
| 112 | const s = f / (2.0 + f); | |
| 113 | const z = s * s; | |
| 114 | const w = z * z; | |
| 115 | const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); | |
| 116 | const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); | |
| 117 | const R = t2 + t1; | |
| 118 | const dk = f64(k); | |
| 119 | ||
| 120 | s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi | |
| 121 | } | |
| 122 | ||
| 123 | test "log" { | |
| 124 | assert(ln(f32(0.2)) == lnf(0.2)); | |
| 125 | assert(ln(f64(0.2)) == lnd(0.2)); | |
| 126 | } | |
| 127 | ||
| 128 | test "logf" { | |
| 129 | const epsilon = 0.000001; | |
| 130 | ||
| 131 | assert(math.approxEq(f32, lnf(0.2), -1.609438, epsilon)); | |
| 132 | assert(math.approxEq(f32, lnf(0.8923), -0.113953, epsilon)); | |
| 133 | assert(math.approxEq(f32, lnf(1.5), 0.405465, epsilon)); | |
| 134 | assert(math.approxEq(f32, lnf(37.45), 3.623007, epsilon)); | |
| 135 | assert(math.approxEq(f32, lnf(89.123), 4.490017, epsilon)); | |
| 136 | assert(math.approxEq(f32, lnf(123123.234375), 11.720941, epsilon)); | |
| 137 | } | |
| 138 | ||
| 139 | test "logd" { | |
| 140 | const epsilon = 0.000001; | |
| 141 | ||
| 142 | assert(math.approxEq(f64, lnd(0.2), -1.609438, epsilon)); | |
| 143 | assert(math.approxEq(f64, lnd(0.8923), -0.113953, epsilon)); | |
| 144 | assert(math.approxEq(f64, lnd(1.5), 0.405465, epsilon)); | |
| 145 | assert(math.approxEq(f64, lnd(37.45), 3.623007, epsilon)); | |
| 146 | assert(math.approxEq(f64, lnd(89.123), 4.490017, epsilon)); | |
| 147 | assert(math.approxEq(f64, lnd(123123.234375), 11.720941, epsilon)); | |
| 148 | } |
std/math/log.zig created+72| ... | ... | @@ -0,0 +1,72 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const assert = @import("../debug.zig").assert; | |
| 4 | ||
| 5 | pub fn log(comptime base: usize, x: var) -> @typeOf(x) { | |
| 6 | const T = @typeOf(x); | |
| 7 | switch (@typeId(T)) { | |
| 8 | builtin.TypeId.Int => { | |
| 9 | if (base == 2) { | |
| 10 | return T.bit_count - 1 - @clz(x); | |
| 11 | } else { | |
| 12 | @compileError("TODO implement log for non base 2 integers"); | |
| 13 | } | |
| 14 | }, | |
| 15 | ||
| 16 | builtin.TypeId.Float => { | |
| 17 | return logf(base, x); | |
| 18 | }, | |
| 19 | ||
| 20 | else => { | |
| 21 | @compileError("log expects integer or float, found '" ++ @typeName(T) ++ "'"); | |
| 22 | }, | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | fn logf(comptime base: usize, x: var) -> @typeOf(x) { | |
| 27 | const T = @typeOf(x); | |
| 28 | switch (T) { | |
| 29 | f32 => { | |
| 30 | switch (base) { | |
| 31 | 2 => return math.log2(x), | |
| 32 | 10 => return math.log10(x), | |
| 33 | else => return f32(math.ln(f64(x)) / math.ln(f64(base))), | |
| 34 | } | |
| 35 | }, | |
| 36 | ||
| 37 | f64 => { | |
| 38 | switch (base) { | |
| 39 | 2 => return math.log2(x), | |
| 40 | 10 => return math.log10(x), | |
| 41 | // NOTE: This likely is computed with reduced accuracy. | |
| 42 | else => return math.ln(x) / math.ln(f64(base)), | |
| 43 | } | |
| 44 | }, | |
| 45 | ||
| 46 | else => @compileError("log not implemented for " ++ @typeName(T)), | |
| 47 | } | |
| 48 | } | |
| 49 | ||
| 50 | test "log_integer" { | |
| 51 | assert(log(2, u8(0x1)) == 0); | |
| 52 | assert(log(2, u8(0x2)) == 1); | |
| 53 | assert(log(2, i16(0x72)) == 6); | |
| 54 | assert(log(2, u32(0xFFFFFF)) == 23); | |
| 55 | assert(log(2, u64(0x7FF0123456789ABC)) == 62); | |
| 56 | } | |
| 57 | ||
| 58 | test "log_float" { | |
| 59 | const epsilon = 0.000001; | |
| 60 | ||
| 61 | assert(math.approxEq(f32, log(6, f32(0.23947)), -0.797723, epsilon)); | |
| 62 | assert(math.approxEq(f32, log(89, f32(0.23947)), -0.318432, epsilon)); | |
| 63 | assert(math.approxEq(f64, log(123897, f64(12389216414)), 1.981724596, epsilon)); | |
| 64 | } | |
| 65 | ||
| 66 | test "log_float_special" { | |
| 67 | assert(log(2, f32(0.2301974)) == math.log2(f32(0.2301974))); | |
| 68 | assert(log(10, f32(0.2301974)) == math.log10(f32(0.2301974))); | |
| 69 | ||
| 70 | assert(log(2, f64(213.23019799993)) == math.log2(f64(213.23019799993))); | |
| 71 | assert(log(10, f64(213.23019799993)) == math.log10(f64(213.23019799993))); | |
| 72 | } |
std/math/log10.zig created+175| ... | ... | @@ -0,0 +1,175 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn log10(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(log10f, x), | |
| 8 | f64 => @inlineCall(log10d, x), | |
| 9 | else => @compileError("log10 not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn log10f(x_: f32) -> f32 { | |
| 14 | const ivln10hi: f32 = 4.3432617188e-01; | |
| 15 | const ivln10lo: f32 = -3.1689971365e-05; | |
| 16 | const log10_2hi: f32 = 3.0102920532e-01; | |
| 17 | const log10_2lo: f32 = 7.9034151668e-07; | |
| 18 | const Lg1: f32 = 0xaaaaaa.0p-24; | |
| 19 | const Lg2: f32 = 0xccce13.0p-25; | |
| 20 | const Lg3: f32 = 0x91e9ee.0p-25; | |
| 21 | const Lg4: f32 = 0xf89e26.0p-26; | |
| 22 | ||
| 23 | var x = x_; | |
| 24 | var u = @bitCast(u32, x); | |
| 25 | var ix = u; | |
| 26 | var k: i32 = 0; | |
| 27 | ||
| 28 | // x < 2^(-126) | |
| 29 | if (ix < 0x00800000 or ix >> 31 != 0) { | |
| 30 | // log(+-0) = -inf | |
| 31 | if (ix << 1 == 0) { | |
| 32 | return -1 / (x * x); | |
| 33 | } | |
| 34 | // log(-#) = nan | |
| 35 | if (ix >> 31 != 0) { | |
| 36 | return (x - x) / 0.0 | |
| 37 | } | |
| 38 | ||
| 39 | k -= 25; | |
| 40 | x *= 0x1.0p25; | |
| 41 | ix = @bitCast(u32, x); | |
| 42 | } else if (ix >= 0x7F800000) { | |
| 43 | return x; | |
| 44 | } else if (ix == 0x3F800000) { | |
| 45 | return 0; | |
| 46 | } | |
| 47 | ||
| 48 | // x into [sqrt(2) / 2, sqrt(2)] | |
| 49 | ix += 0x3F800000 - 0x3F3504F3; | |
| 50 | k += i32(ix >> 23) - 0x7F; | |
| 51 | ix = (ix & 0x007FFFFF) + 0x3F3504F3; | |
| 52 | x = @bitCast(f32, ix); | |
| 53 | ||
| 54 | const f = x - 1.0; | |
| 55 | const s = f / (2.0 + f); | |
| 56 | const z = s * s; | |
| 57 | const w = z * z; | |
| 58 | const t1 = w * (Lg2 + w * Lg4); | |
| 59 | const t2 = z * (Lg1 + w * Lg3); | |
| 60 | const R = t2 + t1; | |
| 61 | const hfsq = 0.5 * f * f; | |
| 62 | ||
| 63 | var hi = f - hfsq; | |
| 64 | u = @bitCast(u32, hi); | |
| 65 | u &= 0xFFFFF000; | |
| 66 | hi = @bitCast(f32, u); | |
| 67 | const lo = f - hi - hfsq + s * (hfsq + R); | |
| 68 | const dk = f32(k); | |
| 69 | ||
| 70 | dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi | |
| 71 | } | |
| 72 | ||
| 73 | fn log10d(x_: f64) -> f64 { | |
| 74 | const ivln10hi: f64 = 4.34294481878168880939e-01; | |
| 75 | const ivln10lo: f64 = 2.50829467116452752298e-11; | |
| 76 | const log10_2hi: f64 = 3.01029995663611771306e-01; | |
| 77 | const log10_2lo: f64 = 3.69423907715893078616e-13; | |
| 78 | const Lg1: f64 = 6.666666666666735130e-01; | |
| 79 | const Lg2: f64 = 3.999999999940941908e-01; | |
| 80 | const Lg3: f64 = 2.857142874366239149e-01; | |
| 81 | const Lg4: f64 = 2.222219843214978396e-01; | |
| 82 | const Lg5: f64 = 1.818357216161805012e-01; | |
| 83 | const Lg6: f64 = 1.531383769920937332e-01; | |
| 84 | const Lg7: f64 = 1.479819860511658591e-01; | |
| 85 | ||
| 86 | var x = x_; | |
| 87 | var ix = @bitCast(u64, x); | |
| 88 | var hx = u32(ix >> 32); | |
| 89 | var k: i32 = 0; | |
| 90 | ||
| 91 | if (hx < 0x00100000 or hx >> 31 != 0) { | |
| 92 | // log(+-0) = -inf | |
| 93 | if (ix << 1 == 0) { | |
| 94 | return -1 / (x * x); | |
| 95 | } | |
| 96 | // log(-#) = nan | |
| 97 | if (hx >> 31 != 0) { | |
| 98 | return (x - x) / 0.0; | |
| 99 | } | |
| 100 | ||
| 101 | // subnormal, scale x | |
| 102 | k -= 54; | |
| 103 | x *= 0x1.0p54; | |
| 104 | hx = u32(@bitCast(u64, x) >> 32) | |
| 105 | } | |
| 106 | else if (hx >= 0x7FF00000) { | |
| 107 | return x; | |
| 108 | } | |
| 109 | else if (hx == 0x3FF00000 and ix << 32 == 0) { | |
| 110 | return 0; | |
| 111 | } | |
| 112 | ||
| 113 | // x into [sqrt(2) / 2, sqrt(2)] | |
| 114 | hx += 0x3FF00000 - 0x3FE6A09E; | |
| 115 | k += i32(hx >> 20) - 0x3FF; | |
| 116 | hx = (hx & 0x000FFFFF) + 0x3FE6A09E; | |
| 117 | ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); | |
| 118 | x = @bitCast(f64, ix); | |
| 119 | ||
| 120 | const f = x - 1.0; | |
| 121 | const hfsq = 0.5 * f * f; | |
| 122 | const s = f / (2.0 + f); | |
| 123 | const z = s * s; | |
| 124 | const w = z * z; | |
| 125 | const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); | |
| 126 | const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); | |
| 127 | const R = t2 + t1; | |
| 128 | ||
| 129 | // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) | |
| 130 | var hi = f - hfsq; | |
| 131 | var hii = @bitCast(u64, hi); | |
| 132 | hii &= @maxValue(u64) << 32; | |
| 133 | hi = @bitCast(f64, hii); | |
| 134 | const lo = f - hi - hfsq + s * (hfsq + R); | |
| 135 | ||
| 136 | // val_hi + val_lo ~ log10(1 + f) + k * log10(2) | |
| 137 | var val_hi = hi * ivln10hi; | |
| 138 | const dk = f64(k); | |
| 139 | const y = dk * log10_2hi; | |
| 140 | var val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi; | |
| 141 | ||
| 142 | // Extra precision multiplication | |
| 143 | const ww = y + val_hi; | |
| 144 | val_lo += (y - ww) + val_hi; | |
| 145 | val_hi = ww; | |
| 146 | ||
| 147 | val_lo + val_hi | |
| 148 | } | |
| 149 | ||
| 150 | test "log10" { | |
| 151 | assert(log10(f32(0.2)) == log10f(0.2)); | |
| 152 | assert(log10(f64(0.2)) == log10d(0.2)); | |
| 153 | } | |
| 154 | ||
| 155 | test "log10f" { | |
| 156 | const epsilon = 0.000001; | |
| 157 | ||
| 158 | assert(math.approxEq(f32, log10f(0.2), -0.698970, epsilon)); | |
| 159 | assert(math.approxEq(f32, log10f(0.8923), -0.049489, epsilon)); | |
| 160 | assert(math.approxEq(f32, log10f(1.5), 0.176091, epsilon)); | |
| 161 | assert(math.approxEq(f32, log10f(37.45), 1.573452, epsilon)); | |
| 162 | assert(math.approxEq(f32, log10f(89.123), 1.94999, epsilon)); | |
| 163 | assert(math.approxEq(f32, log10f(123123.234375), 5.09034, epsilon)); | |
| 164 | } | |
| 165 | ||
| 166 | test "log10d" { | |
| 167 | const epsilon = 0.000001; | |
| 168 | ||
| 169 | assert(math.approxEq(f64, log10d(0.2), -0.698970, epsilon)); | |
| 170 | assert(math.approxEq(f64, log10d(0.8923), -0.049489, epsilon)); | |
| 171 | assert(math.approxEq(f64, log10d(1.5), 0.176091, epsilon)); | |
| 172 | assert(math.approxEq(f64, log10d(37.45), 1.573452, epsilon)); | |
| 173 | assert(math.approxEq(f64, log10d(89.123), 1.94999, epsilon)); | |
| 174 | assert(math.approxEq(f64, log10d(123123.234375), 5.09034, epsilon)); | |
| 175 | } |
std/math/log1p.zig created+197| ... | ... | @@ -0,0 +1,197 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn log1p(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(log1pf, x), | |
| 8 | f64 => @inlineCall(log1pd, x), | |
| 9 | else => @compileError("log1p not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn log1pf(x: f32) -> f32 { | |
| 14 | const ln2_hi = 6.9313812256e-01; | |
| 15 | const ln2_lo = 9.0580006145e-06; | |
| 16 | const Lg1: f32 = 0xaaaaaa.0p-24; | |
| 17 | const Lg2: f32 = 0xccce13.0p-25; | |
| 18 | const Lg3: f32 = 0x91e9ee.0p-25; | |
| 19 | const Lg4: f32 = 0xf89e26.0p-26; | |
| 20 | ||
| 21 | const u = @bitCast(u32, x); | |
| 22 | var ix = u; | |
| 23 | var k: i32 = 1; | |
| 24 | var f: f32 = undefined; | |
| 25 | var c: f32 = undefined; | |
| 26 | ||
| 27 | // 1 + x < sqrt(2)+ | |
| 28 | if (ix < 0x3ED413D0 or ix >> 31 != 0) { | |
| 29 | // x <= -1.0 | |
| 30 | if (ix >= 0xBF800000) { | |
| 31 | // log1p(-1) = +inf | |
| 32 | if (x == -1) { | |
| 33 | return x / 0.0; | |
| 34 | } | |
| 35 | // log1p(x < -1) = nan | |
| 36 | else { | |
| 37 | return (x - x) / 0.0; | |
| 38 | } | |
| 39 | } | |
| 40 | // |x| < 2^(-24) | |
| 41 | if ((ix << 1) < (0x33800000 << 1)) { | |
| 42 | // underflow if subnormal | |
| 43 | if (ix & 0x7F800000 == 0) { | |
| 44 | math.forceEval(x * x); | |
| 45 | } | |
| 46 | return x; | |
| 47 | } | |
| 48 | // sqrt(2) / 2- <= 1 + x < sqrt(2)+ | |
| 49 | if (ix <= 0xBE95F619) { | |
| 50 | k = 0; | |
| 51 | c = 0; | |
| 52 | f = x; | |
| 53 | } | |
| 54 | } else if (ix >= 0x7F800000) { | |
| 55 | return x; | |
| 56 | } | |
| 57 | ||
| 58 | if (k != 0) { | |
| 59 | const uf = 1 + x; | |
| 60 | var iu = @bitCast(u32, uf); | |
| 61 | iu += 0x3F800000 - 0x3F3504F3; | |
| 62 | k = i32(iu >> 23) - 0x7F; | |
| 63 | ||
| 64 | // correction to avoid underflow in c / u | |
| 65 | if (k < 25) { | |
| 66 | c = if (k >= 2) 1 - (uf - x) else x - (uf - 1); | |
| 67 | c /= uf; | |
| 68 | } else { | |
| 69 | c = 0; | |
| 70 | } | |
| 71 | ||
| 72 | // u into [sqrt(2)/2, sqrt(2)] | |
| 73 | iu = (iu & 0x007FFFFF) + 0x3F3504F3; | |
| 74 | f = @bitCast(f32, iu) - 1; | |
| 75 | } | |
| 76 | ||
| 77 | const s = f / (2.0 + f); | |
| 78 | const z = s * s; | |
| 79 | const w = z * z; | |
| 80 | const t1 = w * (Lg2 + w * Lg4); | |
| 81 | const t2 = z * (Lg1 + w * Lg3); | |
| 82 | const R = t2 + t1; | |
| 83 | const hfsq = 0.5 * f * f; | |
| 84 | const dk = f32(k); | |
| 85 | ||
| 86 | s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi | |
| 87 | } | |
| 88 | ||
| 89 | fn log1pd(x: f64) -> f64 { | |
| 90 | const ln2_hi: f64 = 6.93147180369123816490e-01; | |
| 91 | const ln2_lo: f64 = 1.90821492927058770002e-10; | |
| 92 | const Lg1: f64 = 6.666666666666735130e-01; | |
| 93 | const Lg2: f64 = 3.999999999940941908e-01; | |
| 94 | const Lg3: f64 = 2.857142874366239149e-01; | |
| 95 | const Lg4: f64 = 2.222219843214978396e-01; | |
| 96 | const Lg5: f64 = 1.818357216161805012e-01; | |
| 97 | const Lg6: f64 = 1.531383769920937332e-01; | |
| 98 | const Lg7: f64 = 1.479819860511658591e-01; | |
| 99 | ||
| 100 | var ix = @bitCast(u64, x); | |
| 101 | var hx = u32(ix >> 32); | |
| 102 | var k: i32 = 1; | |
| 103 | var c: f64 = undefined; | |
| 104 | var f: f64 = undefined; | |
| 105 | ||
| 106 | // 1 + x < sqrt(2) | |
| 107 | if (hx < 0x3FDA827A or hx >> 31 != 0) { | |
| 108 | // x <= -1.0 | |
| 109 | if (hx >= 0xBFF00000) { | |
| 110 | // log1p(-1) = -inf | |
| 111 | if (x == 1) { | |
| 112 | return x / 0.0; | |
| 113 | } | |
| 114 | // log1p(x < -1) = nan | |
| 115 | else { | |
| 116 | return (x - x) / 0.0; | |
| 117 | } | |
| 118 | } | |
| 119 | // |x| < 2^(-53) | |
| 120 | if ((hx << 1) < (0x3CA00000 << 1)) { | |
| 121 | if ((hx & 0x7FF00000) == 0) { | |
| 122 | math.raiseUnderflow(); | |
| 123 | } | |
| 124 | return x; | |
| 125 | } | |
| 126 | // sqrt(2) / 2- <= 1 + x < sqrt(2)+ | |
| 127 | if (hx <= 0xBFD2BEC4) { | |
| 128 | k = 0; | |
| 129 | c = 0; | |
| 130 | f = x; | |
| 131 | } | |
| 132 | } | |
| 133 | else if (hx >= 0x7FF00000) { | |
| 134 | return x; | |
| 135 | } | |
| 136 | ||
| 137 | if (k != 0) { | |
| 138 | const uf = 1 + x; | |
| 139 | const hu = @bitCast(u64, uf); | |
| 140 | var iu = u32(hu >> 32); | |
| 141 | iu += 0x3FF00000 - 0x3FE6A09E; | |
| 142 | k = i32(iu >> 20) - 0x3FF; | |
| 143 | ||
| 144 | // correction to avoid underflow in c / u | |
| 145 | if (k < 54) { | |
| 146 | c = if (k >= 2) 1 - (uf - x) else x - (uf - 1); | |
| 147 | c /= uf; | |
| 148 | } else { | |
| 149 | c = 0; | |
| 150 | } | |
| 151 | ||
| 152 | // u into [sqrt(2)/2, sqrt(2)] | |
| 153 | iu = (iu & 0x000FFFFF) + 0x3FE6A09E; | |
| 154 | const iq = (u64(iu) << 32) | (hu & 0xFFFFFFFF); | |
| 155 | f = @bitCast(f64, iq) - 1; | |
| 156 | } | |
| 157 | ||
| 158 | const hfsq = 0.5 * f * f; | |
| 159 | const s = f / (2.0 + f); | |
| 160 | const z = s * s; | |
| 161 | const w = z * z; | |
| 162 | const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); | |
| 163 | const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); | |
| 164 | const R = t2 + t1; | |
| 165 | const dk = f64(k); | |
| 166 | ||
| 167 | s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi | |
| 168 | } | |
| 169 | ||
| 170 | test "log1p" { | |
| 171 | assert(log1p(f32(0.0)) == log1pf(0.0)); | |
| 172 | assert(log1p(f64(0.0)) == log1pd(0.0)); | |
| 173 | } | |
| 174 | ||
| 175 | test "log1pf" { | |
| 176 | const epsilon = 0.000001; | |
| 177 | ||
| 178 | assert(math.approxEq(f32, log1pf(0.0), 0.0, epsilon)); | |
| 179 | assert(math.approxEq(f32, log1pf(0.2), 0.182322, epsilon)); | |
| 180 | assert(math.approxEq(f32, log1pf(0.8923), 0.637793, epsilon)); | |
| 181 | assert(math.approxEq(f32, log1pf(1.5), 0.916291, epsilon)); | |
| 182 | assert(math.approxEq(f32, log1pf(37.45), 3.649359, epsilon)); | |
| 183 | assert(math.approxEq(f32, log1pf(89.123), 4.501175, epsilon)); | |
| 184 | assert(math.approxEq(f32, log1pf(123123.234375), 11.720949, epsilon)); | |
| 185 | } | |
| 186 | ||
| 187 | test "log1pd" { | |
| 188 | const epsilon = 0.000001; | |
| 189 | ||
| 190 | assert(math.approxEq(f64, log1pd(0.0), 0.0, epsilon)); | |
| 191 | assert(math.approxEq(f64, log1pd(0.2), 0.182322, epsilon)); | |
| 192 | assert(math.approxEq(f64, log1pd(0.8923), 0.637793, epsilon)); | |
| 193 | assert(math.approxEq(f64, log1pd(1.5), 0.916291, epsilon)); | |
| 194 | assert(math.approxEq(f64, log1pd(37.45), 3.649359, epsilon)); | |
| 195 | assert(math.approxEq(f64, log1pd(89.123), 4.501175, epsilon)); | |
| 196 | assert(math.approxEq(f64, log1pd(123123.234375), 11.720949, epsilon)); | |
| 197 | } |
std/math/log2.zig created+165| ... | ... | @@ -0,0 +1,165 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn log2(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(log2f, x), | |
| 8 | f64 => @inlineCall(log2d, x), | |
| 9 | else => @compileError("log2 not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn log2f(x_: f32) -> f32 { | |
| 14 | const ivln2hi: f32 = 1.4428710938e+00; | |
| 15 | const ivln2lo: f32 = -1.7605285393e-04; | |
| 16 | const Lg1: f32 = 0xaaaaaa.0p-24; | |
| 17 | const Lg2: f32 = 0xccce13.0p-25; | |
| 18 | const Lg3: f32 = 0x91e9ee.0p-25; | |
| 19 | const Lg4: f32 = 0xf89e26.0p-26; | |
| 20 | ||
| 21 | var x = x_; | |
| 22 | var u = @bitCast(u32, x); | |
| 23 | var ix = u; | |
| 24 | var k: i32 = 0; | |
| 25 | ||
| 26 | // x < 2^(-126) | |
| 27 | if (ix < 0x00800000 or ix >> 31 != 0) { | |
| 28 | // log(+-0) = -inf | |
| 29 | if (ix << 1 == 0) { | |
| 30 | return -1 / (x * x); | |
| 31 | } | |
| 32 | // log(-#) = nan | |
| 33 | if (ix >> 31 != 0) { | |
| 34 | return (x - x) / 0.0 | |
| 35 | } | |
| 36 | ||
| 37 | k -= 25; | |
| 38 | x *= 0x1.0p25; | |
| 39 | ix = @bitCast(u32, x); | |
| 40 | } else if (ix >= 0x7F800000) { | |
| 41 | return x; | |
| 42 | } else if (ix == 0x3F800000) { | |
| 43 | return 0; | |
| 44 | } | |
| 45 | ||
| 46 | // x into [sqrt(2) / 2, sqrt(2)] | |
| 47 | ix += 0x3F800000 - 0x3F3504F3; | |
| 48 | k += i32(ix >> 23) - 0x7F; | |
| 49 | ix = (ix & 0x007FFFFF) + 0x3F3504F3; | |
| 50 | x = @bitCast(f32, ix); | |
| 51 | ||
| 52 | const f = x - 1.0; | |
| 53 | const s = f / (2.0 + f); | |
| 54 | const z = s * s; | |
| 55 | const w = z * z; | |
| 56 | const t1 = w * (Lg2 + w * Lg4); | |
| 57 | const t2 = z * (Lg1 + w * Lg3); | |
| 58 | const R = t2 + t1; | |
| 59 | const hfsq = 0.5 * f * f; | |
| 60 | ||
| 61 | var hi = f - hfsq; | |
| 62 | u = @bitCast(u32, hi); | |
| 63 | u &= 0xFFFFF000; | |
| 64 | hi = @bitCast(f32, u); | |
| 65 | const lo = f - hi - hfsq + s * (hfsq + R); | |
| 66 | (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + f32(k) | |
| 67 | } | |
| 68 | ||
| 69 | fn log2d(x_: f64) -> f64 { | |
| 70 | const ivln2hi: f64 = 1.44269504072144627571e+00; | |
| 71 | const ivln2lo: f64 = 1.67517131648865118353e-10; | |
| 72 | const Lg1: f64 = 6.666666666666735130e-01; | |
| 73 | const Lg2: f64 = 3.999999999940941908e-01; | |
| 74 | const Lg3: f64 = 2.857142874366239149e-01; | |
| 75 | const Lg4: f64 = 2.222219843214978396e-01; | |
| 76 | const Lg5: f64 = 1.818357216161805012e-01; | |
| 77 | const Lg6: f64 = 1.531383769920937332e-01; | |
| 78 | const Lg7: f64 = 1.479819860511658591e-01; | |
| 79 | ||
| 80 | var x = x_; | |
| 81 | var ix = @bitCast(u64, x); | |
| 82 | var hx = u32(ix >> 32); | |
| 83 | var k: i32 = 0; | |
| 84 | ||
| 85 | if (hx < 0x00100000 or hx >> 31 != 0) { | |
| 86 | // log(+-0) = -inf | |
| 87 | if (ix << 1 == 0) { | |
| 88 | return -1 / (x * x); | |
| 89 | } | |
| 90 | // log(-#) = nan | |
| 91 | if (hx >> 31 != 0) { | |
| 92 | return (x - x) / 0.0; | |
| 93 | } | |
| 94 | ||
| 95 | // subnormal, scale x | |
| 96 | k -= 54; | |
| 97 | x *= 0x1.0p54; | |
| 98 | hx = u32(@bitCast(u64, x) >> 32); | |
| 99 | } | |
| 100 | else if (hx >= 0x7FF00000) { | |
| 101 | return x; | |
| 102 | } | |
| 103 | else if (hx == 0x3FF00000 and ix << 32 == 0) { | |
| 104 | return 0; | |
| 105 | } | |
| 106 | ||
| 107 | // x into [sqrt(2) / 2, sqrt(2)] | |
| 108 | hx += 0x3FF00000 - 0x3FE6A09E; | |
| 109 | k += i32(hx >> 20) - 0x3FF; | |
| 110 | hx = (hx & 0x000FFFFF) + 0x3FE6A09E; | |
| 111 | ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); | |
| 112 | x = @bitCast(f64, ix); | |
| 113 | ||
| 114 | const f = x - 1.0; | |
| 115 | const hfsq = 0.5 * f * f; | |
| 116 | const s = f / (2.0 + f); | |
| 117 | const z = s * s; | |
| 118 | const w = z * z; | |
| 119 | const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); | |
| 120 | const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); | |
| 121 | const R = t2 + t1; | |
| 122 | ||
| 123 | // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) | |
| 124 | var hi = f - hfsq; | |
| 125 | var hii = @bitCast(u64, hi); | |
| 126 | hii &= @maxValue(u64) << 32; | |
| 127 | hi = @bitCast(f64, hii); | |
| 128 | const lo = f - hi - hfsq + s * (hfsq + R); | |
| 129 | ||
| 130 | var val_hi = hi * ivln2hi; | |
| 131 | var val_lo = (lo + hi) * ivln2lo + lo * ivln2hi; | |
| 132 | ||
| 133 | // spadd(val_hi, val_lo, y) | |
| 134 | const y = f64(k); | |
| 135 | const ww = y + val_hi; | |
| 136 | val_lo += (y - ww) + val_hi; | |
| 137 | val_hi = ww; | |
| 138 | ||
| 139 | val_lo + val_hi | |
| 140 | } | |
| 141 | ||
| 142 | test "log2" { | |
| 143 | assert(log2(f32(0.2)) == log2f(0.2)); | |
| 144 | assert(log2(f64(0.2)) == log2d(0.2)); | |
| 145 | } | |
| 146 | ||
| 147 | test "log2f" { | |
| 148 | const epsilon = 0.000001; | |
| 149 | ||
| 150 | assert(math.approxEq(f32, log2f(0.2), -2.321928, epsilon)); | |
| 151 | assert(math.approxEq(f32, log2f(0.8923), -0.164399, epsilon)); | |
| 152 | assert(math.approxEq(f32, log2f(1.5), 0.584962, epsilon)); | |
| 153 | assert(math.approxEq(f32, log2f(37.45), 5.226894, epsilon)); | |
| 154 | assert(math.approxEq(f32, log2f(123123.234375), 16.909744, epsilon)); | |
| 155 | } | |
| 156 | ||
| 157 | test "log2d" { | |
| 158 | const epsilon = 0.000001; | |
| 159 | ||
| 160 | assert(math.approxEq(f64, log2d(0.2), -2.321928, epsilon)); | |
| 161 | assert(math.approxEq(f64, log2d(0.8923), -0.164399, epsilon)); | |
| 162 | assert(math.approxEq(f64, log2d(1.5), 0.584962, epsilon)); | |
| 163 | assert(math.approxEq(f64, log2d(37.45), 5.226894, epsilon)); | |
| 164 | assert(math.approxEq(f64, log2d(123123.234375), 16.909744, epsilon)); | |
| 165 | } |
std/math/modf.zig created+157| ... | ... | @@ -0,0 +1,157 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | fn modf_result(comptime T: type) -> type { | |
| 5 | struct { | |
| 6 | fpart: T, | |
| 7 | ipart: T, | |
| 8 | } | |
| 9 | } | |
| 10 | pub const modf32_result = modf_result(f32); | |
| 11 | pub const modf64_result = modf_result(f64); | |
| 12 | ||
| 13 | pub fn modf(x: var) -> modf_result(@typeOf(x)) { | |
| 14 | const T = @typeOf(x); | |
| 15 | switch (T) { | |
| 16 | f32 => @inlineCall(modf32, x), | |
| 17 | f64 => @inlineCall(modf64, x), | |
| 18 | else => @compileError("modf not implemented for " ++ @typeName(T)), | |
| 19 | } | |
| 20 | } | |
| 21 | ||
| 22 | fn modf32(x: f32) -> modf32_result { | |
| 23 | var result: modf32_result = undefined; | |
| 24 | ||
| 25 | const u = @bitCast(u32, x); | |
| 26 | const e = i32((u >> 23) & 0xFF) - 0x7F; | |
| 27 | const us = u & 0x80000000; | |
| 28 | ||
| 29 | // no fractional part | |
| 30 | if (e >= 23) { | |
| 31 | result.ipart = x; | |
| 32 | if (e == 0x80 and u << 9 != 0) { // nan | |
| 33 | result.fpart = x; | |
| 34 | } else { | |
| 35 | result.fpart = @bitCast(f32, us); | |
| 36 | } | |
| 37 | return result; | |
| 38 | } | |
| 39 | ||
| 40 | // no integral part | |
| 41 | if (e < 0) { | |
| 42 | result.ipart = @bitCast(f32, us); | |
| 43 | result.fpart = x; | |
| 44 | return result; | |
| 45 | } | |
| 46 | ||
| 47 | const mask = 0x007FFFFF >> u32(e); | |
| 48 | if (u & mask == 0) { | |
| 49 | result.ipart = x; | |
| 50 | result.fpart = @bitCast(f32, us); | |
| 51 | return result; | |
| 52 | } | |
| 53 | ||
| 54 | const uf = @bitCast(f32, u & ~mask); | |
| 55 | result.ipart = uf; | |
| 56 | result.fpart = x - uf; | |
| 57 | result | |
| 58 | } | |
| 59 | ||
| 60 | fn modf64(x: f64) -> modf64_result { | |
| 61 | var result: modf64_result = undefined; | |
| 62 | ||
| 63 | const u = @bitCast(u64, x); | |
| 64 | const e = i32((u >> 52) & 0x7FF) - 0x3FF; | |
| 65 | const us = u & (1 << 63); | |
| 66 | ||
| 67 | // no fractional part | |
| 68 | if (e >= 52) { | |
| 69 | result.ipart = x; | |
| 70 | if (e == 0x400 and u << 12 != 0) { // nan | |
| 71 | result.fpart = x; | |
| 72 | } else { | |
| 73 | result.fpart = @bitCast(f64, us); | |
| 74 | } | |
| 75 | return result; | |
| 76 | } | |
| 77 | ||
| 78 | // no integral part | |
| 79 | if (e < 0) { | |
| 80 | result.ipart = @bitCast(f64, us); | |
| 81 | result.fpart = x; | |
| 82 | return result; | |
| 83 | } | |
| 84 | ||
| 85 | const mask = @maxValue(u64) >> 12 >> u64(e); | |
| 86 | if (u & mask == 0) { | |
| 87 | result.ipart = x; | |
| 88 | result.fpart = @bitCast(f64, us); | |
| 89 | return result; | |
| 90 | } | |
| 91 | ||
| 92 | const uf = @bitCast(f64, u & ~mask); | |
| 93 | result.ipart = uf; | |
| 94 | result.fpart = x - uf; | |
| 95 | result | |
| 96 | } | |
| 97 | ||
| 98 | test "modf" { | |
| 99 | const a = modf(f32(1.0)); | |
| 100 | const b = modf32(1.0); | |
| 101 | // NOTE: No struct comparison on generic return type function? non-named, makes sense, but still. | |
| 102 | assert(a.ipart == b.ipart and a.fpart == b.fpart); | |
| 103 | ||
| 104 | const c = modf(f64(1.0)); | |
| 105 | const d = modf64(1.0); | |
| 106 | assert(a.ipart == b.ipart and a.fpart == b.fpart); | |
| 107 | } | |
| 108 | ||
| 109 | test "modf32" { | |
| 110 | const epsilon = 0.000001; | |
| 111 | var r: modf32_result = undefined; | |
| 112 | ||
| 113 | r = modf32(1.0); | |
| 114 | assert(math.approxEq(f32, r.ipart, 1.0, epsilon)); | |
| 115 | assert(math.approxEq(f32, r.fpart, 0.0, epsilon)); | |
| 116 | ||
| 117 | r = modf32(2.545); | |
| 118 | assert(math.approxEq(f32, r.ipart, 2.0, epsilon)); | |
| 119 | assert(math.approxEq(f32, r.fpart, 0.545, epsilon)); | |
| 120 | ||
| 121 | r = modf32(3.978123); | |
| 122 | assert(math.approxEq(f32, r.ipart, 3.0, epsilon)); | |
| 123 | assert(math.approxEq(f32, r.fpart, 0.978123, epsilon)); | |
| 124 | ||
| 125 | r = modf32(43874.3); | |
| 126 | assert(math.approxEq(f32, r.ipart, 43874, epsilon)); | |
| 127 | assert(math.approxEq(f32, r.fpart, 0.300781, epsilon)); | |
| 128 | ||
| 129 | r = modf32(1234.340780); | |
| 130 | assert(math.approxEq(f32, r.ipart, 1234, epsilon)); | |
| 131 | assert(math.approxEq(f32, r.fpart, 0.340820, epsilon)); | |
| 132 | } | |
| 133 | ||
| 134 | test "modf64" { | |
| 135 | const epsilon = 0.000001; | |
| 136 | var r: modf64_result = undefined; | |
| 137 | ||
| 138 | r = modf64(1.0); | |
| 139 | assert(math.approxEq(f64, r.ipart, 1.0, epsilon)); | |
| 140 | assert(math.approxEq(f64, r.fpart, 0.0, epsilon)); | |
| 141 | ||
| 142 | r = modf64(2.545); | |
| 143 | assert(math.approxEq(f64, r.ipart, 2.0, epsilon)); | |
| 144 | assert(math.approxEq(f64, r.fpart, 0.545, epsilon)); | |
| 145 | ||
| 146 | r = modf64(3.978123); | |
| 147 | assert(math.approxEq(f64, r.ipart, 3.0, epsilon)); | |
| 148 | assert(math.approxEq(f64, r.fpart, 0.978123, epsilon)); | |
| 149 | ||
| 150 | r = modf64(43874.3); | |
| 151 | assert(math.approxEq(f64, r.ipart, 43874, epsilon)); | |
| 152 | assert(math.approxEq(f64, r.fpart, 0.3, epsilon)); | |
| 153 | ||
| 154 | r = modf64(1234.340780); | |
| 155 | assert(math.approxEq(f64, r.ipart, 1234, epsilon)); | |
| 156 | assert(math.approxEq(f64, r.fpart, 0.340780, epsilon)); | |
| 157 | } |
std/math/nan.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | ||
| 3 | pub fn nan(comptime T: type) -> T { | |
| 4 | switch (T) { | |
| 5 | f32 => @bitCast(f32, math.nan_u32), | |
| 6 | f64 => @bitCast(f64, math.nan_u64), | |
| 7 | else => @compileError("nan not implemented for " ++ @typeName(T)), | |
| 8 | } | |
| 9 | } |
std/math/oindex.zig created+274| ... | ... | @@ -0,0 +1,274 @@ |
| 1 | const assert = @import("../debug.zig").assert; | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | pub const frexp = @import("frexp.zig").frexp; | |
| 5 | ||
| 6 | pub const Cmp = enum { | |
| 7 | Less, | |
| 8 | Equal, | |
| 9 | Greater, | |
| 10 | }; | |
| 11 | ||
| 12 | pub fn min(x: var, y: var) -> @typeOf(x + y) { | |
| 13 | if (x < y) x else y | |
| 14 | } | |
| 15 | ||
| 16 | test "math.min" { | |
| 17 | assert(min(i32(-1), i32(2)) == -1); | |
| 18 | } | |
| 19 | ||
| 20 | pub fn max(x: var, y: var) -> @typeOf(x + y) { | |
| 21 | if (x > y) x else y | |
| 22 | } | |
| 23 | ||
| 24 | test "math.max" { | |
| 25 | assert(max(i32(-1), i32(2)) == 2); | |
| 26 | } | |
| 27 | ||
| 28 | error Overflow; | |
| 29 | pub fn mul(comptime T: type, a: T, b: T) -> %T { | |
| 30 | var answer: T = undefined; | |
| 31 | if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer | |
| 32 | } | |
| 33 | ||
| 34 | error Overflow; | |
| 35 | pub fn add(comptime T: type, a: T, b: T) -> %T { | |
| 36 | var answer: T = undefined; | |
| 37 | if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer | |
| 38 | } | |
| 39 | ||
| 40 | error Overflow; | |
| 41 | pub fn sub(comptime T: type, a: T, b: T) -> %T { | |
| 42 | var answer: T = undefined; | |
| 43 | if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer | |
| 44 | } | |
| 45 | ||
| 46 | pub fn negate(x: var) -> %@typeOf(x) { | |
| 47 | return sub(@typeOf(x), 0, x); | |
| 48 | } | |
| 49 | ||
| 50 | error Overflow; | |
| 51 | pub fn shl(comptime T: type, a: T, b: T) -> %T { | |
| 52 | var answer: T = undefined; | |
| 53 | if (@shlWithOverflow(T, a, b, &answer)) error.Overflow else answer | |
| 54 | } | |
| 55 | ||
| 56 | test "math overflow functions" { | |
| 57 | testOverflow(); | |
| 58 | comptime testOverflow(); | |
| 59 | } | |
| 60 | ||
| 61 | fn testOverflow() { | |
| 62 | assert(%%mul(i32, 3, 4) == 12); | |
| 63 | assert(%%add(i32, 3, 4) == 7); | |
| 64 | assert(%%sub(i32, 3, 4) == -1); | |
| 65 | assert(%%shl(i32, 0b11, 4) == 0b110000); | |
| 66 | } | |
| 67 | ||
| 68 | ||
| 69 | error Overflow; | |
| 70 | pub fn absInt(x: var) -> %@typeOf(x) { | |
| 71 | const T = @typeOf(x); | |
| 72 | comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt | |
| 73 | comptime assert(T.is_signed); // must pass a signed integer to absInt | |
| 74 | if (x == @minValue(@typeOf(x))) | |
| 75 | return error.Overflow; | |
| 76 | { | |
| 77 | @setDebugSafety(this, false); | |
| 78 | return if (x < 0) -x else x; | |
| 79 | } | |
| 80 | } | |
| 81 | ||
| 82 | test "math.absInt" { | |
| 83 | testAbsInt(); | |
| 84 | comptime testAbsInt(); | |
| 85 | } | |
| 86 | fn testAbsInt() { | |
| 87 | assert(%%absInt(i32(-10)) == 10); | |
| 88 | assert(%%absInt(i32(10)) == 10); | |
| 89 | } | |
| 90 | ||
| 91 | pub const absFloat = @import("fabs.zig").fabs; | |
| 92 | ||
| 93 | error DivisionByZero; | |
| 94 | error Overflow; | |
| 95 | pub fn divTrunc(comptime T: type, numerator: T, denominator: T) -> %T { | |
| 96 | @setDebugSafety(this, false); | |
| 97 | if (denominator == 0) | |
| 98 | return error.DivisionByZero; | |
| 99 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) | |
| 100 | return error.Overflow; | |
| 101 | return @divTrunc(numerator, denominator); | |
| 102 | } | |
| 103 | ||
| 104 | test "math.divTrunc" { | |
| 105 | testDivTrunc(); | |
| 106 | comptime testDivTrunc(); | |
| 107 | } | |
| 108 | fn testDivTrunc() { | |
| 109 | assert(%%divTrunc(i32, 5, 3) == 1); | |
| 110 | assert(%%divTrunc(i32, -5, 3) == -1); | |
| 111 | if (divTrunc(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); | |
| 112 | if (divTrunc(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow); | |
| 113 | ||
| 114 | assert(%%divTrunc(f32, 5.0, 3.0) == 1.0); | |
| 115 | assert(%%divTrunc(f32, -5.0, 3.0) == -1.0); | |
| 116 | } | |
| 117 | ||
| 118 | error DivisionByZero; | |
| 119 | error Overflow; | |
| 120 | pub fn divFloor(comptime T: type, numerator: T, denominator: T) -> %T { | |
| 121 | @setDebugSafety(this, false); | |
| 122 | if (denominator == 0) | |
| 123 | return error.DivisionByZero; | |
| 124 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) | |
| 125 | return error.Overflow; | |
| 126 | return @divFloor(numerator, denominator); | |
| 127 | } | |
| 128 | ||
| 129 | test "math.divFloor" { | |
| 130 | testDivFloor(); | |
| 131 | comptime testDivFloor(); | |
| 132 | } | |
| 133 | fn testDivFloor() { | |
| 134 | assert(%%divFloor(i32, 5, 3) == 1); | |
| 135 | assert(%%divFloor(i32, -5, 3) == -2); | |
| 136 | if (divFloor(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); | |
| 137 | if (divFloor(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow); | |
| 138 | ||
| 139 | assert(%%divFloor(f32, 5.0, 3.0) == 1.0); | |
| 140 | assert(%%divFloor(f32, -5.0, 3.0) == -2.0); | |
| 141 | } | |
| 142 | ||
| 143 | error DivisionByZero; | |
| 144 | error Overflow; | |
| 145 | error UnexpectedRemainder; | |
| 146 | pub fn divExact(comptime T: type, numerator: T, denominator: T) -> %T { | |
| 147 | @setDebugSafety(this, false); | |
| 148 | if (denominator == 0) | |
| 149 | return error.DivisionByZero; | |
| 150 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) | |
| 151 | return error.Overflow; | |
| 152 | const result = @divTrunc(numerator, denominator); | |
| 153 | if (result * denominator != numerator) | |
| 154 | return error.UnexpectedRemainder; | |
| 155 | return result; | |
| 156 | } | |
| 157 | ||
| 158 | test "math.divExact" { | |
| 159 | testDivExact(); | |
| 160 | comptime testDivExact(); | |
| 161 | } | |
| 162 | fn testDivExact() { | |
| 163 | assert(%%divExact(i32, 10, 5) == 2); | |
| 164 | assert(%%divExact(i32, -10, 5) == -2); | |
| 165 | if (divExact(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); | |
| 166 | if (divExact(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow); | |
| 167 | if (divExact(i32, 5, 2)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder); | |
| 168 | ||
| 169 | assert(%%divExact(f32, 10.0, 5.0) == 2.0); | |
| 170 | assert(%%divExact(f32, -10.0, 5.0) == -2.0); | |
| 171 | if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder); | |
| 172 | } | |
| 173 | ||
| 174 | error DivisionByZero; | |
| 175 | error NegativeDenominator; | |
| 176 | pub fn mod(comptime T: type, numerator: T, denominator: T) -> %T { | |
| 177 | @setDebugSafety(this, false); | |
| 178 | if (denominator == 0) | |
| 179 | return error.DivisionByZero; | |
| 180 | if (denominator < 0) | |
| 181 | return error.NegativeDenominator; | |
| 182 | return @mod(numerator, denominator); | |
| 183 | } | |
| 184 | ||
| 185 | test "math.mod" { | |
| 186 | testMod(); | |
| 187 | comptime testMod(); | |
| 188 | } | |
| 189 | fn testMod() { | |
| 190 | assert(%%mod(i32, -5, 3) == 1); | |
| 191 | assert(%%mod(i32, 5, 3) == 2); | |
| 192 | if (mod(i32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); | |
| 193 | if (mod(i32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); | |
| 194 | ||
| 195 | assert(%%mod(f32, -5, 3) == 1); | |
| 196 | assert(%%mod(f32, 5, 3) == 2); | |
| 197 | if (mod(f32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); | |
| 198 | if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); | |
| 199 | } | |
| 200 | ||
| 201 | error DivisionByZero; | |
| 202 | error NegativeDenominator; | |
| 203 | pub fn rem(comptime T: type, numerator: T, denominator: T) -> %T { | |
| 204 | @setDebugSafety(this, false); | |
| 205 | if (denominator == 0) | |
| 206 | return error.DivisionByZero; | |
| 207 | if (denominator < 0) | |
| 208 | return error.NegativeDenominator; | |
| 209 | return @rem(numerator, denominator); | |
| 210 | } | |
| 211 | ||
| 212 | test "math.rem" { | |
| 213 | testRem(); | |
| 214 | comptime testRem(); | |
| 215 | } | |
| 216 | fn testRem() { | |
| 217 | assert(%%rem(i32, -5, 3) == -2); | |
| 218 | assert(%%rem(i32, 5, 3) == 2); | |
| 219 | if (rem(i32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); | |
| 220 | if (rem(i32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); | |
| 221 | ||
| 222 | assert(%%rem(f32, -5, 3) == -2); | |
| 223 | assert(%%rem(f32, 5, 3) == 2); | |
| 224 | if (rem(f32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator); | |
| 225 | if (rem(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); | |
| 226 | } | |
| 227 | ||
| 228 | /// Returns the absolute value of the integer parameter. | |
| 229 | /// Result is an unsigned integer. | |
| 230 | pub fn absCast(x: var) -> @IntType(false, @typeOf(x).bit_count) { | |
| 231 | const uint = @IntType(false, @typeOf(x).bit_count); | |
| 232 | if (x >= 0) | |
| 233 | return uint(x); | |
| 234 | ||
| 235 | return uint(-(x + 1)) + 1; | |
| 236 | } | |
| 237 | ||
| 238 | test "math.absCast" { | |
| 239 | assert(absCast(i32(-999)) == 999); | |
| 240 | assert(@typeOf(absCast(i32(-999))) == u32); | |
| 241 | ||
| 242 | assert(absCast(i32(999)) == 999); | |
| 243 | assert(@typeOf(absCast(i32(999))) == u32); | |
| 244 | ||
| 245 | assert(absCast(i32(@minValue(i32))) == -@minValue(i32)); | |
| 246 | assert(@typeOf(absCast(i32(@minValue(i32)))) == u32); | |
| 247 | } | |
| 248 | ||
| 249 | /// Returns the negation of the integer parameter. | |
| 250 | /// Result is a signed integer. | |
| 251 | error Overflow; | |
| 252 | pub fn negateCast(x: var) -> %@IntType(true, @typeOf(x).bit_count) { | |
| 253 | if (@typeOf(x).is_signed) | |
| 254 | return negate(x); | |
| 255 | ||
| 256 | const int = @IntType(true, @typeOf(x).bit_count); | |
| 257 | if (x > -@minValue(int)) | |
| 258 | return error.Overflow; | |
| 259 | ||
| 260 | if (x == -@minValue(int)) | |
| 261 | return @minValue(int); | |
| 262 | ||
| 263 | return -int(x); | |
| 264 | } | |
| 265 | ||
| 266 | test "math.negateCast" { | |
| 267 | assert(%%negateCast(u32(999)) == -999); | |
| 268 | assert(@typeOf(%%negateCast(u32(999))) == i32); | |
| 269 | ||
| 270 | assert(%%negateCast(u32(-@minValue(i32))) == @minValue(i32)); | |
| 271 | assert(@typeOf(%%negateCast(u32(-@minValue(i32)))) == i32); | |
| 272 | ||
| 273 | if (negateCast(u32(@maxValue(i32) + 10))) |_| unreachable else |err| assert(err == error.Overflow); | |
| 274 | } |
std/math/pow.zig created+316| ... | ... | @@ -0,0 +1,316 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn pow(comptime T: type, x: T, y: T) -> T { | |
| 5 | switch (T) { | |
| 6 | f32 => @inlineCall(pow32, x, y), | |
| 7 | f64 => @inlineCall(pow64, x, y), | |
| 8 | else => @compileError("pow not implemented for " ++ @typeName(T)), | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | fn isOddInteger(x: f64) -> bool { | |
| 13 | const r = math.modf(x); | |
| 14 | r.fpart == 0.0 and i64(r.ipart) & 1 == 1 | |
| 15 | } | |
| 16 | ||
| 17 | // This implementation is taken from the go stlib, musl is a bit more complex. | |
| 18 | fn pow32(x: f32, y: f32) -> f32 { | |
| 19 | // pow(x, +-0) = 1 for all x | |
| 20 | // pow(1, y) = 1 for all y | |
| 21 | if (y == 0 or x == 1) { | |
| 22 | return 1; | |
| 23 | } | |
| 24 | ||
| 25 | // pow(nan, y) = nan for all y | |
| 26 | // pow(x, nan) = nan for all x | |
| 27 | if (math.isNan(x) or math.isNan(y)) { | |
| 28 | return math.nan(f32); | |
| 29 | } | |
| 30 | ||
| 31 | // pow(x, 1) = x for all x | |
| 32 | if (y == 1) { | |
| 33 | return x; | |
| 34 | } | |
| 35 | ||
| 36 | // special case sqrt | |
| 37 | if (y == 0.5) { | |
| 38 | return math.sqrt(x); | |
| 39 | } | |
| 40 | ||
| 41 | if (y == -0.5) { | |
| 42 | return 1 / math.sqrt(x); | |
| 43 | } | |
| 44 | ||
| 45 | if (x == 0) { | |
| 46 | if (y < 0) { | |
| 47 | // pow(+-0, y) = +- 0 for y an odd integer | |
| 48 | if (isOddInteger(y)) { | |
| 49 | return math.copysign(f32, math.inf(f32), x); | |
| 50 | } | |
| 51 | // pow(+-0, y) = +inf for y an even integer | |
| 52 | else { | |
| 53 | return math.inf(f32); | |
| 54 | } | |
| 55 | } else { | |
| 56 | if (isOddInteger(y)) { | |
| 57 | return x; | |
| 58 | } else { | |
| 59 | return 0; | |
| 60 | } | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | if (math.isInf(y)) { | |
| 65 | // pow(-1, inf) = -1 for all x | |
| 66 | if (x == -1) { | |
| 67 | return -1; | |
| 68 | } | |
| 69 | // pow(x, +inf) = +0 for |x| < 1 | |
| 70 | // pow(x, -inf) = +0 for |x| > 1 | |
| 71 | else if ((math.fabs(x) < 1) == math.isPositiveInf(y)) { | |
| 72 | return 0; | |
| 73 | } | |
| 74 | // pow(x, -inf) = +inf for |x| < 1 | |
| 75 | // pow(x, +inf) = +inf for |x| > 1 | |
| 76 | else { | |
| 77 | return math.inf(f32); | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 81 | if (math.isInf(x)) { | |
| 82 | if (math.isNegativeInf(x)) { | |
| 83 | return pow32(1 / x, -y); | |
| 84 | } | |
| 85 | // pow(+inf, y) = +0 for y < 0 | |
| 86 | else if (y < 0) { | |
| 87 | return 0; | |
| 88 | } | |
| 89 | // pow(+inf, y) = +0 for y > 0 | |
| 90 | else if (y > 0) { | |
| 91 | return math.inf(f32); | |
| 92 | } | |
| 93 | } | |
| 94 | ||
| 95 | var ay = y; | |
| 96 | var flip = false; | |
| 97 | if (ay < 0) { | |
| 98 | ay = -ay; | |
| 99 | flip = true; | |
| 100 | } | |
| 101 | ||
| 102 | const r1 = math.modf(ay); | |
| 103 | var yi = r1.ipart; | |
| 104 | var yf = r1.fpart; | |
| 105 | ||
| 106 | if (yf != 0 and x < 0) { | |
| 107 | return math.nan(f32); | |
| 108 | } | |
| 109 | if (yi >= 1 << 31) { | |
| 110 | return math.exp(y * math.ln(x)); | |
| 111 | } | |
| 112 | ||
| 113 | // a = a1 * 2^ae | |
| 114 | var a1: f32 = 1.0; | |
| 115 | var ae: i32 = 0; | |
| 116 | ||
| 117 | // a *= x^yf | |
| 118 | if (yf != 0) { | |
| 119 | if (yf > 0.5) { | |
| 120 | yf -= 1; | |
| 121 | yi += 1; | |
| 122 | } | |
| 123 | a1 = math.exp(yf * math.ln(x)); | |
| 124 | } | |
| 125 | ||
| 126 | // a *= x^yi | |
| 127 | const r2 = math.frexp(x); | |
| 128 | var xe = r2.exponent; | |
| 129 | var x1 = r2.significand; | |
| 130 | ||
| 131 | var i = i32(yi); | |
| 132 | while (i != 0) : (i >>= 1) { | |
| 133 | if (i & 1 == 1) { | |
| 134 | a1 *= x1; | |
| 135 | ae += xe; | |
| 136 | } | |
| 137 | x1 *= x1; | |
| 138 | xe <<= 1; | |
| 139 | if (x1 < 0.5) { | |
| 140 | x1 += x1; | |
| 141 | xe -= 1; | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 145 | // a *= a1 * 2^ae | |
| 146 | if (flip) { | |
| 147 | a1 = 1 / a1; | |
| 148 | ae = -ae; | |
| 149 | } | |
| 150 | ||
| 151 | math.scalbn(a1, ae) | |
| 152 | } | |
| 153 | ||
| 154 | // This implementation is taken from the go stlib, musl is a bit more complex. | |
| 155 | fn pow64(x: f64, y: f64) -> f64 { | |
| 156 | // pow(x, +-0) = 1 for all x | |
| 157 | // pow(1, y) = 1 for all y | |
| 158 | if (y == 0 or x == 1) { | |
| 159 | return 1; | |
| 160 | } | |
| 161 | ||
| 162 | // pow(nan, y) = nan for all y | |
| 163 | // pow(x, nan) = nan for all x | |
| 164 | if (math.isNan(x) or math.isNan(y)) { | |
| 165 | return math.nan(f64); | |
| 166 | } | |
| 167 | ||
| 168 | // pow(x, 1) = x for all x | |
| 169 | if (y == 1) { | |
| 170 | return x; | |
| 171 | } | |
| 172 | ||
| 173 | // special case sqrt | |
| 174 | if (y == 0.5) { | |
| 175 | return math.sqrt(x); | |
| 176 | } | |
| 177 | ||
| 178 | if (y == -0.5) { | |
| 179 | return 1 / math.sqrt(x); | |
| 180 | } | |
| 181 | ||
| 182 | if (x == 0) { | |
| 183 | if (y < 0) { | |
| 184 | // pow(+-0, y) = +- 0 for y an odd integer | |
| 185 | if (isOddInteger(y)) { | |
| 186 | return math.copysign(f64, math.inf(f64), x); | |
| 187 | } | |
| 188 | // pow(+-0, y) = +inf for y an even integer | |
| 189 | else { | |
| 190 | return math.inf(f64); | |
| 191 | } | |
| 192 | } else { | |
| 193 | if (isOddInteger(y)) { | |
| 194 | return x; | |
| 195 | } else { | |
| 196 | return 0; | |
| 197 | } | |
| 198 | } | |
| 199 | } | |
| 200 | ||
| 201 | if (math.isInf(y)) { | |
| 202 | // pow(-1, inf) = -1 for all x | |
| 203 | if (x == -1) { | |
| 204 | return -1; | |
| 205 | } | |
| 206 | // pow(x, +inf) = +0 for |x| < 1 | |
| 207 | // pow(x, -inf) = +0 for |x| > 1 | |
| 208 | else if ((math.fabs(x) < 1) == math.isInf(y)) { | |
| 209 | return 0; | |
| 210 | } | |
| 211 | // pow(x, -inf) = +inf for |x| < 1 | |
| 212 | // pow(x, +inf) = +inf for |x| > 1 | |
| 213 | else { | |
| 214 | return math.inf(f64); | |
| 215 | } | |
| 216 | } | |
| 217 | ||
| 218 | if (math.isInf(x)) { | |
| 219 | if (math.isInf(x)) { | |
| 220 | return pow64(1 / x, -y); | |
| 221 | } | |
| 222 | // pow(+inf, y) = +0 for y < 0 | |
| 223 | else if (y < 0) { | |
| 224 | return 0; | |
| 225 | } | |
| 226 | // pow(+inf, y) = +0 for y > 0 | |
| 227 | else if (y > 0) { | |
| 228 | return math.inf(f64); | |
| 229 | } | |
| 230 | } | |
| 231 | ||
| 232 | var ay = y; | |
| 233 | var flip = false; | |
| 234 | if (ay < 0) { | |
| 235 | ay = -ay; | |
| 236 | flip = true; | |
| 237 | } | |
| 238 | ||
| 239 | const r1 = math.modf(ay); | |
| 240 | var yi = r1.ipart; | |
| 241 | var yf = r1.fpart; | |
| 242 | ||
| 243 | if (yf != 0 and x < 0) { | |
| 244 | return math.nan(f64); | |
| 245 | } | |
| 246 | if (yi >= 1 << 63) { | |
| 247 | return math.exp(y * math.ln(x)); | |
| 248 | } | |
| 249 | ||
| 250 | // a = a1 * 2^ae | |
| 251 | var a1: f64 = 1.0; | |
| 252 | var ae: i32 = 0; | |
| 253 | ||
| 254 | // a *= x^yf | |
| 255 | if (yf != 0) { | |
| 256 | if (yf > 0.5) { | |
| 257 | yf -= 1; | |
| 258 | yi += 1; | |
| 259 | } | |
| 260 | a1 = math.exp(yf * math.ln(x)); | |
| 261 | } | |
| 262 | ||
| 263 | // a *= x^yi | |
| 264 | const r2 = math.frexp(x); | |
| 265 | var xe = r2.exponent; | |
| 266 | var x1 = r2.significand; | |
| 267 | ||
| 268 | var i = i64(yi); | |
| 269 | while (i != 0) : (i >>= 1) { | |
| 270 | if (i & 1 == 1) { | |
| 271 | a1 *= x1; | |
| 272 | ae += xe; | |
| 273 | } | |
| 274 | x1 *= x1; | |
| 275 | xe <<= 1; | |
| 276 | if (x1 < 0.5) { | |
| 277 | x1 += x1; | |
| 278 | xe -= 1; | |
| 279 | } | |
| 280 | } | |
| 281 | ||
| 282 | // a *= a1 * 2^ae | |
| 283 | if (flip) { | |
| 284 | a1 = 1 / a1; | |
| 285 | ae = -ae; | |
| 286 | } | |
| 287 | ||
| 288 | math.scalbn(a1, ae) | |
| 289 | } | |
| 290 | ||
| 291 | test "pow" { | |
| 292 | assert(pow(f32, 0.2, 3.3) == pow32(0.2, 3.3)); | |
| 293 | assert(pow(f64, 0.2, 3.3) == pow64(0.2, 3.3)); | |
| 294 | } | |
| 295 | ||
| 296 | test "pow32" { | |
| 297 | const epsilon = 0.000001; | |
| 298 | ||
| 299 | // assert(math.approxEq(f32, pow32(0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero | |
| 300 | assert(math.approxEq(f32, pow32(0.8923, 3.3), 0.686572, epsilon)); | |
| 301 | assert(math.approxEq(f32, pow32(0.2, 3.3), 0.004936, epsilon)); | |
| 302 | assert(math.approxEq(f32, pow32(1.5, 3.3), 3.811546, epsilon)); | |
| 303 | assert(math.approxEq(f32, pow32(37.45, 3.3), 155736.703125, epsilon)); | |
| 304 | assert(math.approxEq(f32, pow32(89.123, 3.3), 2722489.5, epsilon)); | |
| 305 | } | |
| 306 | ||
| 307 | test "pow64" { | |
| 308 | const epsilon = 0.000001; | |
| 309 | ||
| 310 | // assert(math.approxEq(f32, pow32(0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero | |
| 311 | assert(math.approxEq(f64, pow64(0.8923, 3.3), 0.686572, epsilon)); | |
| 312 | assert(math.approxEq(f64, pow64(0.2, 3.3), 0.004936, epsilon)); | |
| 313 | assert(math.approxEq(f64, pow64(1.5, 3.3), 3.811546, epsilon)); | |
| 314 | assert(math.approxEq(f64, pow64(37.45, 3.3), 155736.7160616, epsilon)); | |
| 315 | assert(math.approxEq(f64, pow64(89.123, 3.3), 2722490.231436, epsilon)); | |
| 316 | } |
std/math/round.zig created+105| ... | ... | @@ -0,0 +1,105 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | const math = @import("index.zig"); | |
| 4 | ||
| 5 | pub fn round(x: var) -> @typeOf(x) { | |
| 6 | const T = @typeOf(x); | |
| 7 | switch (T) { | |
| 8 | f32 => @inlineCall(round32, x), | |
| 9 | f64 => @inlineCall(round64, x), | |
| 10 | else => @compileError("round not implemented for " ++ @typeName(T)), | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | fn round32(x_: f32) -> f32 { | |
| 15 | var x = x_; | |
| 16 | const u = @bitCast(u32, x); | |
| 17 | const e = (u >> 23) & 0xFF; | |
| 18 | var y: f32 = undefined; | |
| 19 | ||
| 20 | if (e >= 0x7F+23) { | |
| 21 | return x; | |
| 22 | } | |
| 23 | if (u >> 31 != 0) { | |
| 24 | x = -x; | |
| 25 | } | |
| 26 | if (e < 0x7F-1) { | |
| 27 | math.forceEval(x + math.f32_toint); | |
| 28 | return 0 * @bitCast(f32, u); | |
| 29 | } | |
| 30 | ||
| 31 | { | |
| 32 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 33 | y = x + math.f32_toint - math.f32_toint - x; | |
| 34 | } | |
| 35 | ||
| 36 | if (y > 0.5) { | |
| 37 | y = y + x - 1; | |
| 38 | } else if (y <= -0.5) { | |
| 39 | y = y + x + 1; | |
| 40 | } else { | |
| 41 | y = y + x; | |
| 42 | } | |
| 43 | ||
| 44 | if (u >> 31 != 0) { | |
| 45 | -y | |
| 46 | } else { | |
| 47 | y | |
| 48 | } | |
| 49 | } | |
| 50 | ||
| 51 | fn round64(x_: f64) -> f64 { | |
| 52 | var x = x_; | |
| 53 | const u = @bitCast(u64, x); | |
| 54 | const e = (u >> 52) & 0x7FF; | |
| 55 | var y: f64 = undefined; | |
| 56 | ||
| 57 | if (e >= 0x3FF+52) { | |
| 58 | return x; | |
| 59 | } | |
| 60 | if (u >> 63 != 0) { | |
| 61 | x = -x; | |
| 62 | } | |
| 63 | if (e < 0x3ff-1) { | |
| 64 | math.forceEval(x + math.f64_toint); | |
| 65 | return 0 * @bitCast(f64, u); | |
| 66 | } | |
| 67 | ||
| 68 | { | |
| 69 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 70 | y = x + math.f64_toint - math.f64_toint - x; | |
| 71 | } | |
| 72 | ||
| 73 | if (y > 0.5) { | |
| 74 | y = y + x - 1; | |
| 75 | } else if (y <= -0.5) { | |
| 76 | y = y + x + 1; | |
| 77 | } else { | |
| 78 | y = y + x; | |
| 79 | } | |
| 80 | ||
| 81 | if (u >> 63 != 0) { | |
| 82 | -y | |
| 83 | } else { | |
| 84 | y | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | test "round" { | |
| 89 | assert(round(f32(1.3)) == round32(1.3)); | |
| 90 | assert(round(f64(1.3)) == round64(1.3)); | |
| 91 | } | |
| 92 | ||
| 93 | test "round32" { | |
| 94 | assert(round32(1.3) == 1.0); | |
| 95 | assert(round32(-1.3) == -1.0); | |
| 96 | assert(round32(0.2) == 0.0); | |
| 97 | assert(round32(1.8) == 2.0); | |
| 98 | } | |
| 99 | ||
| 100 | test "round64" { | |
| 101 | assert(round64(1.3) == 1.0); | |
| 102 | assert(round64(-1.3) == -1.0); | |
| 103 | assert(round64(0.2) == 0.0); | |
| 104 | assert(round64(1.8) == 2.0); | |
| 105 | } |
std/math/scalbn.zig created+85| ... | ... | @@ -0,0 +1,85 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn scalbn(x: var, n: i32) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(scalbn32, x, n), | |
| 8 | f64 => @inlineCall(scalbn64, x, n), | |
| 9 | else => @compileError("scalbn not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn scalbn32(x: f32, n_: i32) -> f32 { | |
| 14 | var y = x; | |
| 15 | var n = n_; | |
| 16 | ||
| 17 | if (n > 127) { | |
| 18 | y *= 0x1.0p127; | |
| 19 | n -= 127; | |
| 20 | if (n > 1023) { | |
| 21 | y *= 0x1.0p127; | |
| 22 | n -= 127; | |
| 23 | if (n > 127) { | |
| 24 | n = 127; | |
| 25 | } | |
| 26 | } | |
| 27 | } else if (n < -126) { | |
| 28 | y *= 0x1.0p-126 * 0x1.0p24; | |
| 29 | n += 126 - 24; | |
| 30 | if (n < -126) { | |
| 31 | y *= 0x1.0p-126 * 0x1.0p24; | |
| 32 | n += 126 - 24; | |
| 33 | if (n < -126) { | |
| 34 | n = -126; | |
| 35 | } | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | const u = u32(n +% 0x7F) << 23; | |
| 40 | y * @bitCast(f32, u) | |
| 41 | } | |
| 42 | ||
| 43 | fn scalbn64(x: f64, n_: i32) -> f64 { | |
| 44 | var y = x; | |
| 45 | var n = n_; | |
| 46 | ||
| 47 | if (n > 1023) { | |
| 48 | // TODO: Determine how to do the following. | |
| 49 | // y *= 0x1.0p1023; | |
| 50 | n -= 1023; | |
| 51 | if (n > 1023) { | |
| 52 | // y *= 0x1.0p1023; | |
| 53 | n -= 1023; | |
| 54 | if (n > 1023) { | |
| 55 | n = 1023; | |
| 56 | } | |
| 57 | } | |
| 58 | } else if (n < -1022) { | |
| 59 | y *= 0x1.0p-1022 * 0x1.0p53; | |
| 60 | n += 1022 - 53; | |
| 61 | if (n < -1022) { | |
| 62 | y *= 0x1.0p-1022 * 0x1.0p53; | |
| 63 | n += 1022 - 53; | |
| 64 | if (n < -1022) { | |
| 65 | n = -1022; | |
| 66 | } | |
| 67 | } | |
| 68 | } | |
| 69 | ||
| 70 | const u = u64(n +% 0x3FF) << 52; | |
| 71 | y * @bitCast(f64, u) | |
| 72 | } | |
| 73 | ||
| 74 | test "scalbn" { | |
| 75 | assert(scalbn(f32(1.5), 4) == scalbn32(1.5, 4)); | |
| 76 | assert(scalbn(f64(1.5), 4) == scalbn64(1.5, 4)); | |
| 77 | } | |
| 78 | ||
| 79 | test "scalbn32" { | |
| 80 | assert(scalbn32(1.5, 4) == 24.0); | |
| 81 | } | |
| 82 | ||
| 83 | test "scalbn64" { | |
| 84 | assert(scalbn64(1.5, 4) == 24.0); | |
| 85 | } |
std/math/signbit.zig created+36| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn signbit(x: var) -> bool { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(signbit32, x), | |
| 8 | f64 => @inlineCall(signbit64, x), | |
| 9 | else => @compileError("signbit not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn signbit32(x: f32) -> bool { | |
| 14 | const bits = @bitCast(u32, x); | |
| 15 | bits >> 31 != 0 | |
| 16 | } | |
| 17 | ||
| 18 | fn signbit64(x: f64) -> bool { | |
| 19 | const bits = @bitCast(u64, x); | |
| 20 | bits >> 63 != 0 | |
| 21 | } | |
| 22 | ||
| 23 | test "signbit" { | |
| 24 | assert(signbit(f32(4.0)) == signbit32(4.0)); | |
| 25 | assert(signbit(f64(4.0)) == signbit64(4.0)); | |
| 26 | } | |
| 27 | ||
| 28 | test "signbit32" { | |
| 29 | assert(!signbit32(4.0)); | |
| 30 | assert(signbit32(-3.0)); | |
| 31 | } | |
| 32 | ||
| 33 | test "signbit64" { | |
| 34 | assert(!signbit64(4.0)); | |
| 35 | assert(signbit64(-3.0)); | |
| 36 | } |
std/math/sin.zig created+161| ... | ... | @@ -0,0 +1,161 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn sin(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(sin32, x), | |
| 8 | f64 => @inlineCall(sin64, x), | |
| 9 | else => @compileError("sin not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | // sin polynomial coefficients | |
| 14 | const S0 = 1.58962301576546568060E-10; | |
| 15 | const S1 = -2.50507477628578072866E-8; | |
| 16 | const S2 = 2.75573136213857245213E-6; | |
| 17 | const S3 = -1.98412698295895385996E-4; | |
| 18 | const S4 = 8.33333333332211858878E-3; | |
| 19 | const S5 = -1.66666666666666307295E-1; | |
| 20 | ||
| 21 | // cos polynomial coeffiecients | |
| 22 | const C0 = -1.13585365213876817300E-11; | |
| 23 | const C1 = 2.08757008419747316778E-9; | |
| 24 | const C2 = -2.75573141792967388112E-7; | |
| 25 | const C3 = 2.48015872888517045348E-5; | |
| 26 | const C4 = -1.38888888888730564116E-3; | |
| 27 | const C5 = 4.16666666666665929218E-2; | |
| 28 | ||
| 29 | // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. | |
| 30 | // | |
| 31 | // This may have slight differences on some edge cases and may need to replaced if so. | |
| 32 | fn sin32(x_: f32) -> f32 { | |
| 33 | const pi4a = 7.85398125648498535156e-1; | |
| 34 | const pi4b = 3.77489470793079817668E-8; | |
| 35 | const pi4c = 2.69515142907905952645E-15; | |
| 36 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 37 | ||
| 38 | var x = x_; | |
| 39 | if (x == 0 or math.isNan(x)) { | |
| 40 | return x; | |
| 41 | } | |
| 42 | if (math.isInf(x)) { | |
| 43 | return math.nan(f32); | |
| 44 | } | |
| 45 | ||
| 46 | var sign = false; | |
| 47 | if (x < 0) { | |
| 48 | x = -x; | |
| 49 | sign = true; | |
| 50 | } | |
| 51 | ||
| 52 | var y = math.floor(x * m4pi); | |
| 53 | var j = i64(y); | |
| 54 | ||
| 55 | if (j & 1 == 1) { | |
| 56 | j += 1; | |
| 57 | y += 1; | |
| 58 | } | |
| 59 | ||
| 60 | j &= 7; | |
| 61 | if (j > 3) { | |
| 62 | j -= 4; | |
| 63 | sign = !sign; | |
| 64 | } | |
| 65 | ||
| 66 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 67 | const w = z * z; | |
| 68 | ||
| 69 | const r = { | |
| 70 | if (j == 1 or j == 2) { | |
| 71 | 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))) | |
| 72 | } else { | |
| 73 | z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))) | |
| 74 | } | |
| 75 | }; | |
| 76 | ||
| 77 | if (sign) { | |
| 78 | -r | |
| 79 | } else { | |
| 80 | r | |
| 81 | } | |
| 82 | } | |
| 83 | ||
| 84 | fn sin64(x_: f64) -> f64 { | |
| 85 | const pi4a = 7.85398125648498535156e-1; | |
| 86 | const pi4b = 3.77489470793079817668E-8; | |
| 87 | const pi4c = 2.69515142907905952645E-15; | |
| 88 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 89 | ||
| 90 | var x = x_; | |
| 91 | if (x == 0 or math.isNan(x)) { | |
| 92 | return x; | |
| 93 | } | |
| 94 | if (math.isInf(x)) { | |
| 95 | return math.nan(f64); | |
| 96 | } | |
| 97 | ||
| 98 | var sign = false; | |
| 99 | if (x < 0) { | |
| 100 | x = -x; | |
| 101 | sign = true; | |
| 102 | } | |
| 103 | ||
| 104 | var y = math.floor(x * m4pi); | |
| 105 | var j = i64(y); | |
| 106 | ||
| 107 | if (j & 1 == 1) { | |
| 108 | j += 1; | |
| 109 | y += 1; | |
| 110 | } | |
| 111 | ||
| 112 | j &= 7; | |
| 113 | if (j > 3) { | |
| 114 | j -= 4; | |
| 115 | sign = !sign; | |
| 116 | } | |
| 117 | ||
| 118 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 119 | const w = z * z; | |
| 120 | ||
| 121 | const r = { | |
| 122 | if (j == 1 or j == 2) { | |
| 123 | 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))) | |
| 124 | } else { | |
| 125 | z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))) | |
| 126 | } | |
| 127 | }; | |
| 128 | ||
| 129 | if (sign) { | |
| 130 | -r | |
| 131 | } else { | |
| 132 | r | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | test "sin" { | |
| 137 | assert(sin(f32(0.0)) == sin32(0.0)); | |
| 138 | assert(sin(f64(0.0)) == sin64(0.0)); | |
| 139 | } | |
| 140 | ||
| 141 | test "sin32" { | |
| 142 | const epsilon = 0.000001; | |
| 143 | ||
| 144 | assert(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); | |
| 145 | assert(math.approxEq(f32, sin32(0.2), 0.198669, epsilon)); | |
| 146 | assert(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon)); | |
| 147 | assert(math.approxEq(f32, sin32(1.5), 0.997495, epsilon)); | |
| 148 | assert(math.approxEq(f32, sin32(37.45), -0.246544, epsilon)); | |
| 149 | assert(math.approxEq(f32, sin32(89.123), 0.916166, epsilon)); | |
| 150 | } | |
| 151 | ||
| 152 | test "sin64" { | |
| 153 | const epsilon = 0.000001; | |
| 154 | ||
| 155 | assert(math.approxEq(f64, sin64(0.0), 0.0, epsilon)); | |
| 156 | assert(math.approxEq(f64, sin64(0.2), 0.198669, epsilon)); | |
| 157 | assert(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon)); | |
| 158 | assert(math.approxEq(f64, sin64(1.5), 0.997495, epsilon)); | |
| 159 | assert(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); | |
| 160 | assert(math.approxEq(f64, sin64(89.123), 0.916166, epsilon)); | |
| 161 | } |
std/math/sinh.zig created+93| ... | ... | @@ -0,0 +1,93 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | const expo2 = @import("_expo2.zig").expo2; | |
| 4 | ||
| 5 | pub fn sinh(x: var) -> @typeOf(x) { | |
| 6 | const T = @typeOf(x); | |
| 7 | switch (T) { | |
| 8 | f32 => @inlineCall(sinhf, x), | |
| 9 | f64 => @inlineCall(sinhd, x), | |
| 10 | else => @compileError("sinh not implemented for " ++ @typeName(T)), | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | // sinh(x) = (exp(x) - 1 / exp(x)) / 2 | |
| 15 | // = (exp(x) - 1 + (exp(x) - 1) / exp(x)) / 2 | |
| 16 | // = x + x^3 / 6 + o(x^5) | |
| 17 | fn sinhf(x: f32) -> f32 { | |
| 18 | const u = @bitCast(u32, x); | |
| 19 | const ux = u & 0x7FFFFFFF; | |
| 20 | const ax = @bitCast(f32, ux); | |
| 21 | ||
| 22 | var h: f32 = 0.5; | |
| 23 | if (u >> 31 != 0) { | |
| 24 | h = -h; | |
| 25 | } | |
| 26 | ||
| 27 | // |x| < log(FLT_MAX) | |
| 28 | if (ux < 0x42B17217) { | |
| 29 | const t = math.expm1(ax); | |
| 30 | if (ux < 0x3F800000) { | |
| 31 | if (ux < 0x3F800000 - (12 << 23)) { | |
| 32 | return x; | |
| 33 | } else { | |
| 34 | return h * (2 * t - t * t / (t + 1)); | |
| 35 | } | |
| 36 | } | |
| 37 | return h * (t + t / (t + 1)); | |
| 38 | } | |
| 39 | ||
| 40 | // |x| > log(FLT_MAX) or nan | |
| 41 | 2 * h * expo2(ax) | |
| 42 | } | |
| 43 | ||
| 44 | fn sinhd(x: f64) -> f64 { | |
| 45 | const u = @bitCast(u64, x); | |
| 46 | const w = u32(u >> 32); | |
| 47 | const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); | |
| 48 | ||
| 49 | var h: f32 = 0.5; | |
| 50 | if (u >> 63 != 0) { | |
| 51 | h = -h; | |
| 52 | } | |
| 53 | ||
| 54 | // |x| < log(FLT_MAX) | |
| 55 | if (w < 0x40862E42) { | |
| 56 | const t = math.expm1(ax); | |
| 57 | if (w < 0x3FF00000) { | |
| 58 | if (w < 0x3FF00000 - (26 << 20)) { | |
| 59 | return x; | |
| 60 | } else { | |
| 61 | return h * (2 * t - t * t / (t + 1)); | |
| 62 | } | |
| 63 | } | |
| 64 | // NOTE: |x| > log(0x1p26) + eps could be h * exp(x) | |
| 65 | return h * (t + t / (t + 1)); | |
| 66 | } | |
| 67 | ||
| 68 | // |x| > log(DBL_MAX) or nan | |
| 69 | 2 * h * expo2(ax) | |
| 70 | } | |
| 71 | ||
| 72 | test "sinh" { | |
| 73 | assert(sinh(f32(1.5)) == sinhf(1.5)); | |
| 74 | assert(sinh(f64(1.5)) == sinhd(1.5)); | |
| 75 | } | |
| 76 | ||
| 77 | test "sinhf" { | |
| 78 | const epsilon = 0.000001; | |
| 79 | ||
| 80 | assert(math.approxEq(f32, sinhf(0.0), 0.0, epsilon)); | |
| 81 | assert(math.approxEq(f32, sinhf(0.2), 0.201336, epsilon)); | |
| 82 | assert(math.approxEq(f32, sinhf(0.8923), 1.015512, epsilon)); | |
| 83 | assert(math.approxEq(f32, sinhf(1.5), 2.129279, epsilon)); | |
| 84 | } | |
| 85 | ||
| 86 | test "sinhd" { | |
| 87 | const epsilon = 0.000001; | |
| 88 | ||
| 89 | assert(math.approxEq(f64, sinhd(0.0), 0.0, epsilon)); | |
| 90 | assert(math.approxEq(f64, sinhd(0.2), 0.201336, epsilon)); | |
| 91 | assert(math.approxEq(f64, sinhd(0.8923), 1.015512, epsilon)); | |
| 92 | assert(math.approxEq(f64, sinhd(1.5), 2.129279, epsilon)); | |
| 93 | } |
std/math/sqrt.zig created+253| ... | ... | @@ -0,0 +1,253 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn sqrt(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(sqrt32, x), | |
| 8 | f64 => @inlineCall(sqrt64, x), | |
| 9 | else => @compileError("sqrt not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn sqrt32(x: f32) -> f32 { | |
| 14 | const tiny: f32 = 1.0e-30; | |
| 15 | const sign: i32 = @bitCast(i32, u32(0x80000000)); | |
| 16 | var ix: i32 = @bitCast(i32, x); | |
| 17 | ||
| 18 | if ((ix & 0x7F800000) == 0x7F800000) { | |
| 19 | return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan | |
| 20 | } | |
| 21 | ||
| 22 | // zero | |
| 23 | if (ix <= 0) { | |
| 24 | if (ix & ~sign == 0) { | |
| 25 | return x; // sqrt (+-0) = +-0 | |
| 26 | } | |
| 27 | if (ix < 0) { | |
| 28 | return (x - x) / (x - x); // sqrt(-ve) = snan | |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | // normalize | |
| 33 | var m = ix >> 23; | |
| 34 | if (m == 0) { | |
| 35 | // subnormal | |
| 36 | var i: i32 = 0; | |
| 37 | while (ix & 0x00800000 == 0) : (i += 1) { | |
| 38 | ix <<= 1 | |
| 39 | } | |
| 40 | m -= i - 1; | |
| 41 | } | |
| 42 | ||
| 43 | m -= 127; // unbias exponent | |
| 44 | ix = (ix & 0x007FFFFF) | 0x00800000; | |
| 45 | ||
| 46 | if (m & 1 != 0) { // odd m, double x to even | |
| 47 | ix += ix; | |
| 48 | } | |
| 49 | ||
| 50 | m >>= 1; // m = [m / 2] | |
| 51 | ||
| 52 | // sqrt(x) bit by bit | |
| 53 | ix += ix; | |
| 54 | var q: i32 = 0; // q = sqrt(x) | |
| 55 | var s: i32 = 0; | |
| 56 | var r: i32 = 0x01000000; // r = moving bit right -> left | |
| 57 | ||
| 58 | while (r != 0) { | |
| 59 | const t = s + r; | |
| 60 | if (t <= ix) { | |
| 61 | s = t + r; | |
| 62 | ix -= t; | |
| 63 | q += r; | |
| 64 | } | |
| 65 | ix += ix; | |
| 66 | r >>= 1; | |
| 67 | } | |
| 68 | ||
| 69 | // floating add to find rounding direction | |
| 70 | if (ix != 0) { | |
| 71 | var z = 1.0 - tiny; // inexact | |
| 72 | if (z >= 1.0) { | |
| 73 | z = 1.0 + tiny; | |
| 74 | if (z > 1.0) { | |
| 75 | q += 2; | |
| 76 | } else { | |
| 77 | if (q & 1 != 0) { | |
| 78 | q += 1; | |
| 79 | } | |
| 80 | } | |
| 81 | } | |
| 82 | } | |
| 83 | ||
| 84 | ix = (q >> 1) + 0x3f000000; | |
| 85 | ix += m << 23; | |
| 86 | @bitCast(f32, ix) | |
| 87 | } | |
| 88 | ||
| 89 | // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound | |
| 90 | // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are | |
| 91 | // potentially some edge cases remaining that are not handled in the same way. | |
| 92 | fn sqrt64(x: f64) -> f64 { | |
| 93 | const tiny: f64 = 1.0e-300; | |
| 94 | const sign: u32 = 0x80000000; | |
| 95 | const u = @bitCast(u64, x); | |
| 96 | ||
| 97 | var ix0 = u32(u >> 32); | |
| 98 | var ix1 = u32(u & 0xFFFFFFFF); | |
| 99 | ||
| 100 | // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan | |
| 101 | if (ix0 & 0x7FF00000 == 0x7FF00000) { | |
| 102 | return x * x + x; | |
| 103 | } | |
| 104 | ||
| 105 | // sqrt(+-0) = +-0 | |
| 106 | if ((ix0 & ~sign) | ix0 == 0) { | |
| 107 | return x; | |
| 108 | } | |
| 109 | // sqrt(-ve) = snan | |
| 110 | if (ix0 & sign != 0) { | |
| 111 | return (x - x) / (x - x); | |
| 112 | } | |
| 113 | ||
| 114 | // normalize x | |
| 115 | var m = i32(ix0 >> 20); | |
| 116 | if (m == 0) { | |
| 117 | // subnormal | |
| 118 | while (ix0 == 0) { | |
| 119 | m -= 21; | |
| 120 | ix0 |= ix1 >> 11; | |
| 121 | ix1 <<= 21; | |
| 122 | } | |
| 123 | ||
| 124 | // subnormal | |
| 125 | var i: u32 = 0; | |
| 126 | while (ix0 & 0x00100000 == 0) : (i += 1) { | |
| 127 | ix0 <<= 1 | |
| 128 | } | |
| 129 | m -= i32(i) - 1; | |
| 130 | ix0 |= ix1 >> (32 - i); | |
| 131 | ix1 <<= i; | |
| 132 | } | |
| 133 | ||
| 134 | // unbias exponent | |
| 135 | m -= 1023; | |
| 136 | ix0 = (ix0 & 0x000FFFFF) | 0x00100000; | |
| 137 | if (m & 1 != 0) { | |
| 138 | ix0 += ix0 + (ix1 >> 31); | |
| 139 | ix1 = ix1 +% ix1; | |
| 140 | } | |
| 141 | m >>= 1; | |
| 142 | ||
| 143 | // sqrt(x) bit by bit | |
| 144 | ix0 += ix0 + (ix1 >> 31); | |
| 145 | ix1 = ix1 +% ix1; | |
| 146 | ||
| 147 | var q: u32 = 0; | |
| 148 | var q1: u32 = 0; | |
| 149 | var s0: u32 = 0; | |
| 150 | var s1: u32 = 0; | |
| 151 | var r: u32 = 0x00200000; | |
| 152 | var t: u32 = undefined; | |
| 153 | var t1: u32 = undefined; | |
| 154 | ||
| 155 | while (r != 0) { | |
| 156 | t = s0 +% r; | |
| 157 | if (t <= ix0) { | |
| 158 | s0 = t + r; | |
| 159 | ix0 -= t; | |
| 160 | q += r; | |
| 161 | } | |
| 162 | ix0 = ix0 +% ix0 +% (ix1 >> 31); | |
| 163 | ix1 = ix1 +% ix1; | |
| 164 | r >>= 1; | |
| 165 | } | |
| 166 | ||
| 167 | r = sign; | |
| 168 | while (r != 0) { | |
| 169 | t = s1 +% r; | |
| 170 | t = s0; | |
| 171 | if (t < ix0 or (t == ix0 and t1 <= ix1)) { | |
| 172 | s1 = t1 +% r; | |
| 173 | if (t1 & sign == sign and s1 & sign == 0) { | |
| 174 | s0 += 1; | |
| 175 | } | |
| 176 | ix0 -= t; | |
| 177 | if (ix1 < t1) { | |
| 178 | ix0 -= 1; | |
| 179 | } | |
| 180 | ix1 = ix1 -% t1; | |
| 181 | q1 += r; | |
| 182 | } | |
| 183 | ix0 = ix0 +% ix0 +% (ix1 >> 31); | |
| 184 | ix1 = ix1 +% ix1; | |
| 185 | r >>= 1; | |
| 186 | } | |
| 187 | ||
| 188 | // rounding direction | |
| 189 | if (ix0 | ix1 != 0) { | |
| 190 | var z = 1.0 - tiny; // raise inexact | |
| 191 | if (z >= 1.0) { | |
| 192 | z = 1.0 + tiny; | |
| 193 | if (q1 == 0xFFFFFFFF) { | |
| 194 | q1 = 0; | |
| 195 | q += 1; | |
| 196 | } else if (z > 1.0) { | |
| 197 | if (q1 == 0xFFFFFFFE) { | |
| 198 | q += 1; | |
| 199 | } | |
| 200 | q1 += 2; | |
| 201 | } else { | |
| 202 | q1 += q1 & 1; | |
| 203 | } | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | ix0 = (q >> 1) + 0x3FE00000; | |
| 208 | ix1 = q1 >> 1; | |
| 209 | if (q & 1 != 0) { | |
| 210 | ix1 |= 0x80000000; | |
| 211 | } | |
| 212 | ||
| 213 | // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same | |
| 214 | // behaviour at least. | |
| 215 | var iix0 = i32(ix0); | |
| 216 | iix0 = iix0 +% (m << 20); | |
| 217 | ||
| 218 | const uz = (u64(iix0) << 32) | ix1; | |
| 219 | @bitCast(f64, uz) | |
| 220 | } | |
| 221 | ||
| 222 | test "sqrt" { | |
| 223 | assert(sqrt(f32(0.0)) == sqrt32(0.0)); | |
| 224 | assert(sqrt(f64(0.0)) == sqrt64(0.0)); | |
| 225 | } | |
| 226 | ||
| 227 | test "sqrt32" { | |
| 228 | const epsilon = 0.000001; | |
| 229 | ||
| 230 | assert(sqrt32(0.0) == 0.0); | |
| 231 | assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon)); | |
| 232 | assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon)); | |
| 233 | assert(sqrt32(4.0) == 2.0); | |
| 234 | assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon)); | |
| 235 | assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon)); | |
| 236 | assert(sqrt32(64.0) == 8.0); | |
| 237 | assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon)); | |
| 238 | assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon)); | |
| 239 | } | |
| 240 | ||
| 241 | test "sqrt64" { | |
| 242 | const epsilon = 0.000001; | |
| 243 | ||
| 244 | assert(sqrt64(0.0) == 0.0); | |
| 245 | assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon)); | |
| 246 | assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon)); | |
| 247 | assert(sqrt64(4.0) == 2.0); | |
| 248 | assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon)); | |
| 249 | assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon)); | |
| 250 | assert(sqrt64(64.0) == 8.0); | |
| 251 | assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon)); | |
| 252 | assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon)); | |
| 253 | } |
std/math/tan.zig created+148| ... | ... | @@ -0,0 +1,148 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn tan(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(tan32, x), | |
| 8 | f64 => @inlineCall(tan64, x), | |
| 9 | else => @compileError("tan not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | const Tp0 = -1.30936939181383777646E4; | |
| 14 | const Tp1 = 1.15351664838587416140E6; | |
| 15 | const Tp2 = -1.79565251976484877988E7; | |
| 16 | ||
| 17 | const Tq1 = 1.36812963470692954678E4; | |
| 18 | const Tq2 = -1.32089234440210967447E6; | |
| 19 | const Tq3 = 2.50083801823357915839E7; | |
| 20 | const Tq4 = -5.38695755929454629881E7; | |
| 21 | ||
| 22 | // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. | |
| 23 | // | |
| 24 | // This may have slight differences on some edge cases and may need to replaced if so. | |
| 25 | fn tan32(x_: f32) -> f32 { | |
| 26 | const pi4a = 7.85398125648498535156e-1; | |
| 27 | const pi4b = 3.77489470793079817668E-8; | |
| 28 | const pi4c = 2.69515142907905952645E-15; | |
| 29 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 30 | ||
| 31 | var x = x_; | |
| 32 | if (x == 0 or math.isNan(x)) { | |
| 33 | return x; | |
| 34 | } | |
| 35 | if (math.isInf(x)) { | |
| 36 | return math.nan(f32); | |
| 37 | } | |
| 38 | ||
| 39 | var sign = false; | |
| 40 | if (x < 0) { | |
| 41 | x = -x; | |
| 42 | sign = true; | |
| 43 | } | |
| 44 | ||
| 45 | var y = math.floor(x * m4pi); | |
| 46 | var j = i64(y); | |
| 47 | ||
| 48 | if (j & 1 == 1) { | |
| 49 | j += 1; | |
| 50 | y += 1; | |
| 51 | } | |
| 52 | ||
| 53 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 54 | const w = z * z; | |
| 55 | ||
| 56 | var r = { | |
| 57 | if (w > 1e-14) { | |
| 58 | z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)) | |
| 59 | } else { | |
| 60 | z | |
| 61 | } | |
| 62 | }; | |
| 63 | ||
| 64 | if (j & 2 == 2) { | |
| 65 | r = -1 / r; | |
| 66 | } | |
| 67 | if (sign) { | |
| 68 | r = -r; | |
| 69 | } | |
| 70 | ||
| 71 | r | |
| 72 | } | |
| 73 | ||
| 74 | fn tan64(x_: f64) -> f64 { | |
| 75 | const pi4a = 7.85398125648498535156e-1; | |
| 76 | const pi4b = 3.77489470793079817668E-8; | |
| 77 | const pi4c = 2.69515142907905952645E-15; | |
| 78 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 79 | ||
| 80 | var x = x_; | |
| 81 | if (x == 0 or math.isNan(x)) { | |
| 82 | return x; | |
| 83 | } | |
| 84 | if (math.isInf(x)) { | |
| 85 | return math.nan(f64); | |
| 86 | } | |
| 87 | ||
| 88 | var sign = false; | |
| 89 | if (x < 0) { | |
| 90 | x = -x; | |
| 91 | sign = true; | |
| 92 | } | |
| 93 | ||
| 94 | var y = math.floor(x * m4pi); | |
| 95 | var j = i64(y); | |
| 96 | ||
| 97 | if (j & 1 == 1) { | |
| 98 | j += 1; | |
| 99 | y += 1; | |
| 100 | } | |
| 101 | ||
| 102 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 103 | const w = z * z; | |
| 104 | ||
| 105 | var r = { | |
| 106 | if (w > 1e-14) { | |
| 107 | z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)) | |
| 108 | } else { | |
| 109 | z | |
| 110 | } | |
| 111 | }; | |
| 112 | ||
| 113 | if (j & 2 == 2) { | |
| 114 | r = -1 / r; | |
| 115 | } | |
| 116 | if (sign) { | |
| 117 | r = -r; | |
| 118 | } | |
| 119 | ||
| 120 | r | |
| 121 | } | |
| 122 | ||
| 123 | test "tan" { | |
| 124 | assert(tan(f32(0.0)) == tan32(0.0)); | |
| 125 | assert(tan(f64(0.0)) == tan64(0.0)); | |
| 126 | } | |
| 127 | ||
| 128 | test "tan32" { | |
| 129 | const epsilon = 0.000001; | |
| 130 | ||
| 131 | assert(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); | |
| 132 | assert(math.approxEq(f32, tan32(0.2), 0.202710, epsilon)); | |
| 133 | assert(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon)); | |
| 134 | assert(math.approxEq(f32, tan32(1.5), 14.101420, epsilon)); | |
| 135 | assert(math.approxEq(f32, tan32(37.45), -0.254397, epsilon)); | |
| 136 | assert(math.approxEq(f32, tan32(89.123), 2.285852, epsilon)); | |
| 137 | } | |
| 138 | ||
| 139 | test "tan64" { | |
| 140 | const epsilon = 0.000001; | |
| 141 | ||
| 142 | assert(math.approxEq(f64, tan64(0.0), 0.0, epsilon)); | |
| 143 | assert(math.approxEq(f64, tan64(0.2), 0.202710, epsilon)); | |
| 144 | assert(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon)); | |
| 145 | assert(math.approxEq(f64, tan64(1.5), 14.101420, epsilon)); | |
| 146 | assert(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); | |
| 147 | assert(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); | |
| 148 | } |
std/math/tanh.zig created+120| ... | ... | @@ -0,0 +1,120 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | const expo2 = @import("_expo2.zig").expo2; | |
| 4 | ||
| 5 | pub fn tanh(x: var) -> @typeOf(x) { | |
| 6 | const T = @typeOf(x); | |
| 7 | switch (T) { | |
| 8 | f32 => @inlineCall(tanhf, x), | |
| 9 | f64 => @inlineCall(tanhd, x), | |
| 10 | else => @compileError("tanh not implemented for " ++ @typeName(T)), | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | // tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x)) | |
| 15 | // = (exp(2x) - 1) / (exp(2x) - 1 + 2) | |
| 16 | // = (1 - exp(-2x)) / (exp(-2x) - 1 + 2) | |
| 17 | fn tanhf(x: f32) -> f32 { | |
| 18 | const u = @bitCast(u32, x); | |
| 19 | const ux = u & 0x7FFFFFFF; | |
| 20 | const ax = @bitCast(f32, ux); | |
| 21 | ||
| 22 | var t: f32 = undefined; | |
| 23 | ||
| 24 | // |x| < log(3) / 2 ~= 0.5493 or nan | |
| 25 | if (ux > 0x3F0C9F54) { | |
| 26 | // |x| > 10 | |
| 27 | if (ux > 0x41200000) { | |
| 28 | t = 1.0 + 0 / x; | |
| 29 | } else { | |
| 30 | t = math.expm1(2 * x); | |
| 31 | t = 1 - 2 / (t + 2); | |
| 32 | } | |
| 33 | } | |
| 34 | // |x| > log(5 / 3) / 2 ~= 0.2554 | |
| 35 | else if (ux > 0x3E82C578) { | |
| 36 | t = math.expm1(2 * x); | |
| 37 | t = t / (t + 2); | |
| 38 | } | |
| 39 | // |x| >= 0x1.0p-126 | |
| 40 | else if (ux >= 0x00800000) { | |
| 41 | t = math.expm1(-2 * x); | |
| 42 | t = -t / (t + 2); | |
| 43 | } | |
| 44 | // |x| is subnormal | |
| 45 | else { | |
| 46 | math.forceEval(x * x); | |
| 47 | t = x; | |
| 48 | } | |
| 49 | ||
| 50 | if (u >> 31 != 0) { | |
| 51 | -t | |
| 52 | } else { | |
| 53 | t | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | fn tanhd(x: f64) -> f64 { | |
| 58 | const u = @bitCast(u64, x); | |
| 59 | const w = u32(u >> 32); | |
| 60 | const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); | |
| 61 | ||
| 62 | var t: f64 = undefined; | |
| 63 | ||
| 64 | // |x| < log(3) / 2 ~= 0.5493 or nan | |
| 65 | if (w > 0x3Fe193EA) { | |
| 66 | // |x| > 20 or nan | |
| 67 | if (w > 0x40340000) { | |
| 68 | t = 1.0 + 0 / x; | |
| 69 | } else { | |
| 70 | t = math.expm1(2 * x); | |
| 71 | t = 1 - 2 / (t + 2); | |
| 72 | } | |
| 73 | } | |
| 74 | // |x| > log(5 / 3) / 2 ~= 0.2554 | |
| 75 | else if (w > 0x3FD058AE) { | |
| 76 | t = math.expm1(2 * x); | |
| 77 | t = t / (t + 2); | |
| 78 | } | |
| 79 | // |x| >= 0x1.0p-1022 | |
| 80 | else if (w >= 0x00100000) { | |
| 81 | t = math.expm1(-2 * x); | |
| 82 | t = -t / (t + 2); | |
| 83 | } | |
| 84 | // |x| is subnormal | |
| 85 | else { | |
| 86 | math.forceEval(f32(x)); | |
| 87 | t = x; | |
| 88 | } | |
| 89 | ||
| 90 | if (u >> 63 != 0) { | |
| 91 | -t | |
| 92 | } else { | |
| 93 | t | |
| 94 | } | |
| 95 | } | |
| 96 | ||
| 97 | test "tanh" { | |
| 98 | assert(tanh(f32(1.5)) == tanhf(1.5)); | |
| 99 | assert(tanh(f64(1.5)) == tanhd(1.5)); | |
| 100 | } | |
| 101 | ||
| 102 | test "tanhf" { | |
| 103 | const epsilon = 0.000001; | |
| 104 | ||
| 105 | assert(math.approxEq(f32, tanhf(0.0), 0.0, epsilon)); | |
| 106 | assert(math.approxEq(f32, tanhf(0.2), 0.197375, epsilon)); | |
| 107 | assert(math.approxEq(f32, tanhf(0.8923), 0.712528, epsilon)); | |
| 108 | assert(math.approxEq(f32, tanhf(1.5), 0.905148, epsilon)); | |
| 109 | assert(math.approxEq(f32, tanhf(37.45), 1.0, epsilon)); | |
| 110 | } | |
| 111 | ||
| 112 | test "tanhd" { | |
| 113 | const epsilon = 0.000001; | |
| 114 | ||
| 115 | assert(math.approxEq(f64, tanhd(0.0), 0.0, epsilon)); | |
| 116 | assert(math.approxEq(f64, tanhd(0.2), 0.197375, epsilon)); | |
| 117 | assert(math.approxEq(f64, tanhd(0.8923), 0.712528, epsilon)); | |
| 118 | assert(math.approxEq(f64, tanhd(1.5), 0.905148, epsilon)); | |
| 119 | assert(math.approxEq(f64, tanhd(37.45), 1.0, epsilon)); | |
| 120 | } |
std/math/trunc.zig created+70| ... | ... | @@ -0,0 +1,70 @@ |
| 1 | const math = @import("index.zig"); | |
| 2 | const assert = @import("../debug.zig").assert; | |
| 3 | ||
| 4 | pub fn trunc(x: var) -> @typeOf(x) { | |
| 5 | const T = @typeOf(x); | |
| 6 | switch (T) { | |
| 7 | f32 => @inlineCall(trunc32, x), | |
| 8 | f64 => @inlineCall(trunc64, x), | |
| 9 | else => @compileError("trunc not implemented for " ++ @typeName(T)), | |
| 10 | } | |
| 11 | } | |
| 12 | ||
| 13 | fn trunc32(x: f32) -> f32 { | |
| 14 | const u = @bitCast(u32, x); | |
| 15 | var e = i32(((u >> 23) & 0xFF)) - 0x7F + 9; | |
| 16 | var m: u32 = undefined; | |
| 17 | ||
| 18 | if (e >= 23 + 9) { | |
| 19 | return x; | |
| 20 | } | |
| 21 | if (e < 9) { | |
| 22 | e = 1; | |
| 23 | } | |
| 24 | ||
| 25 | m = @maxValue(u32) >> u32(e); | |
| 26 | if (u & m == 0) { | |
| 27 | x | |
| 28 | } else { | |
| 29 | math.forceEval(x + 0x1p120); | |
| 30 | @bitCast(f32, u & ~m) | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | fn trunc64(x: f64) -> f64 { | |
| 35 | const u = @bitCast(u64, x); | |
| 36 | var e = i32(((u >> 52) & 0x7FF)) - 0x3FF + 12; | |
| 37 | var m: u64 = undefined; | |
| 38 | ||
| 39 | if (e >= 52 + 12) { | |
| 40 | return x; | |
| 41 | } | |
| 42 | if (e < 12) { | |
| 43 | e = 1; | |
| 44 | } | |
| 45 | ||
| 46 | m = @maxValue(u64) >> u64(e); | |
| 47 | if (u & m == 0) { | |
| 48 | x | |
| 49 | } else { | |
| 50 | math.forceEval(x + 0x1p120); | |
| 51 | @bitCast(f64, u & ~m) | |
| 52 | } | |
| 53 | } | |
| 54 | ||
| 55 | test "trunc" { | |
| 56 | assert(trunc(f32(1.3)) == trunc32(1.3)); | |
| 57 | assert(trunc(f64(1.3)) == trunc64(1.3)); | |
| 58 | } | |
| 59 | ||
| 60 | test "trunc32" { | |
| 61 | assert(trunc32(1.3) == 1.0); | |
| 62 | assert(trunc32(-1.3) == -1.0); | |
| 63 | assert(trunc32(0.2) == 0.0); | |
| 64 | } | |
| 65 | ||
| 66 | test "trunc64" { | |
| 67 | assert(trunc64(1.3) == 1.0); | |
| 68 | assert(trunc64(-1.3) == -1.0); | |
| 69 | assert(trunc64(0.2) == 0.0); | |
| 70 | } |