| author | |
| committer | |
| log | f94964cd057ace39caa05c10d9626ac6a4beb23b |
| tree | 14d6ac67d878caccbb2ce5217de420f57f9c094a |
| parent | 7bbc8eb16c44d0aa9fee72fbe5607b44468a2872 |
This also starts the documentation effort for the math/ subdirectory.
The intent is to use this as a somewhat representative test-case for any
work on the documentation generator.5 files changed, 214 insertions(+), 352 deletions(-)
std/math/cos.zig+46-100| ... | ... | @@ -1,18 +1,23 @@ |
| 1 | // Special Cases: | |
| 1 | // Ported from go, which is licensed under a BSD-3 license. | |
| 2 | // https://golang.org/LICENSE | |
| 2 | 3 | // |
| 3 | // - cos(+-inf) = nan | |
| 4 | // - cos(nan) = nan | |
| 4 | // https://golang.org/src/math/sin.go | |
| 5 | 5 | |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | 7 | const std = @import("../std.zig"); |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 10 | |
| 11 | /// Returns the cosine of the radian value x. | |
| 12 | /// | |
| 13 | /// Special Cases: | |
| 14 | /// - cos(+-inf) = nan | |
| 15 | /// - cos(nan) = nan | |
| 11 | 16 | pub fn cos(x: var) @typeOf(x) { |
| 12 | 17 | const T = @typeOf(x); |
| 13 | 18 | return switch (T) { |
| 14 | f32 => cos32(x), | |
| 15 | f64 => cos64(x), | |
| 19 | f32 => cos_(f32, x), | |
| 20 | f64 => cos_(f64, x), | |
| 16 | 21 | else => @compileError("cos not implemented for " ++ @typeName(T)), |
| 17 | 22 | }; |
| 18 | 23 | } |
| ... | ... | @@ -33,78 +38,24 @@ const C3 = 2.48015872888517045348E-5; |
| 33 | 38 | const C4 = -1.38888888888730564116E-3; |
| 34 | 39 | const C5 = 4.16666666666665929218E-2; |
| 35 | 40 | |
| 36 | // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. | |
| 37 | // | |
| 38 | // This may have slight differences on some edge cases and may need to replaced if so. | |
| 39 | fn cos32(x_: f32) f32 { | |
| 40 | const pi4a = 7.85398125648498535156e-1; | |
| 41 | const pi4b = 3.77489470793079817668E-8; | |
| 42 | const pi4c = 2.69515142907905952645E-15; | |
| 43 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 44 | ||
| 45 | var x = x_; | |
| 46 | if (math.isNan(x) or math.isInf(x)) { | |
| 47 | return math.nan(f32); | |
| 48 | } | |
| 41 | const pi4a = 7.85398125648498535156e-1; | |
| 42 | const pi4b = 3.77489470793079817668E-8; | |
| 43 | const pi4c = 2.69515142907905952645E-15; | |
| 44 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 49 | 45 | |
| 50 | var sign = false; | |
| 51 | if (x < 0) { | |
| 52 | x = -x; | |
| 53 | } | |
| 54 | ||
| 55 | var y = math.floor(x * m4pi); | |
| 56 | var j = @floatToInt(i64, y); | |
| 57 | ||
| 58 | if (j & 1 == 1) { | |
| 59 | j += 1; | |
| 60 | y += 1; | |
| 61 | } | |
| 62 | ||
| 63 | j &= 7; | |
| 64 | if (j > 3) { | |
| 65 | j -= 4; | |
| 66 | sign = !sign; | |
| 67 | } | |
| 68 | if (j > 1) { | |
| 69 | sign = !sign; | |
| 70 | } | |
| 71 | ||
| 72 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 73 | const w = z * z; | |
| 74 | ||
| 75 | const r = r: { | |
| 76 | if (j == 1 or j == 2) { | |
| 77 | break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); | |
| 78 | } else { | |
| 79 | break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); | |
| 80 | } | |
| 81 | }; | |
| 82 | ||
| 83 | if (sign) { | |
| 84 | return -r; | |
| 85 | } else { | |
| 86 | return r; | |
| 87 | } | |
| 88 | } | |
| 89 | ||
| 90 | fn cos64(x_: f64) f64 { | |
| 91 | const pi4a = 7.85398125648498535156e-1; | |
| 92 | const pi4b = 3.77489470793079817668E-8; | |
| 93 | const pi4c = 2.69515142907905952645E-15; | |
| 94 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 46 | fn cos_(comptime T: type, x_: T) T { | |
| 47 | const I = @IntType(true, T.bit_count); | |
| 95 | 48 | |
| 96 | 49 | var x = x_; |
| 97 | 50 | if (math.isNan(x) or math.isInf(x)) { |
| 98 | return math.nan(f64); | |
| 51 | return math.nan(f32); | |
| 99 | 52 | } |
| 100 | 53 | |
| 101 | 54 | var sign = false; |
| 102 | if (x < 0) { | |
| 103 | x = -x; | |
| 104 | } | |
| 55 | x = math.fabs(x); | |
| 105 | 56 | |
| 106 | 57 | var y = math.floor(x * m4pi); |
| 107 | var j = @floatToInt(i64, y); | |
| 58 | var j = @floatToInt(I, y); | |
| 108 | 59 | |
| 109 | 60 | if (j & 1 == 1) { |
| 110 | 61 | j += 1; |
| ... | ... | @@ -123,56 +74,51 @@ fn cos64(x_: f64) f64 { |
| 123 | 74 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; |
| 124 | 75 | const w = z * z; |
| 125 | 76 | |
| 126 | const r = r: { | |
| 127 | if (j == 1 or j == 2) { | |
| 128 | break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); | |
| 129 | } else { | |
| 130 | break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); | |
| 131 | } | |
| 132 | }; | |
| 77 | const r = if (j == 1 or j == 2) | |
| 78 | z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))) | |
| 79 | else | |
| 80 | 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); | |
| 133 | 81 | |
| 134 | if (sign) { | |
| 135 | return -r; | |
| 136 | } else { | |
| 137 | return r; | |
| 138 | } | |
| 82 | return if (sign) -r else r; | |
| 139 | 83 | } |
| 140 | 84 | |
| 141 | 85 | test "math.cos" { |
| 142 | expect(cos(f32(0.0)) == cos32(0.0)); | |
| 143 | expect(cos(f64(0.0)) == cos64(0.0)); | |
| 86 | expect(cos(f32(0.0)) == cos_(f32, 0.0)); | |
| 87 | expect(cos(f64(0.0)) == cos_(f64, 0.0)); | |
| 144 | 88 | } |
| 145 | 89 | |
| 146 | 90 | test "math.cos32" { |
| 147 | 91 | const epsilon = 0.000001; |
| 148 | 92 | |
| 149 | expect(math.approxEq(f32, cos32(0.0), 1.0, epsilon)); | |
| 150 | expect(math.approxEq(f32, cos32(0.2), 0.980067, epsilon)); | |
| 151 | expect(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon)); | |
| 152 | expect(math.approxEq(f32, cos32(1.5), 0.070737, epsilon)); | |
| 153 | expect(math.approxEq(f32, cos32(37.45), 0.969132, epsilon)); | |
| 154 | expect(math.approxEq(f32, cos32(89.123), 0.400798, epsilon)); | |
| 93 | expect(math.approxEq(f32, cos_(f32, 0.0), 1.0, epsilon)); | |
| 94 | expect(math.approxEq(f32, cos_(f32, 0.2), 0.980067, epsilon)); | |
| 95 | expect(math.approxEq(f32, cos_(f32, 0.8923), 0.627623, epsilon)); | |
| 96 | expect(math.approxEq(f32, cos_(f32, 1.5), 0.070737, epsilon)); | |
| 97 | expect(math.approxEq(f32, cos_(f32, -1.5), 0.070737, epsilon)); | |
| 98 | expect(math.approxEq(f32, cos_(f32, 37.45), 0.969132, epsilon)); | |
| 99 | expect(math.approxEq(f32, cos_(f32, 89.123), 0.400798, epsilon)); | |
| 155 | 100 | } |
| 156 | 101 | |
| 157 | 102 | test "math.cos64" { |
| 158 | 103 | const epsilon = 0.000001; |
| 159 | 104 | |
| 160 | expect(math.approxEq(f64, cos64(0.0), 1.0, epsilon)); | |
| 161 | expect(math.approxEq(f64, cos64(0.2), 0.980067, epsilon)); | |
| 162 | expect(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon)); | |
| 163 | expect(math.approxEq(f64, cos64(1.5), 0.070737, epsilon)); | |
| 164 | expect(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); | |
| 165 | expect(math.approxEq(f64, cos64(89.123), 0.40080, epsilon)); | |
| 105 | expect(math.approxEq(f64, cos_(f64, 0.0), 1.0, epsilon)); | |
| 106 | expect(math.approxEq(f64, cos_(f64, 0.2), 0.980067, epsilon)); | |
| 107 | expect(math.approxEq(f64, cos_(f64, 0.8923), 0.627623, epsilon)); | |
| 108 | expect(math.approxEq(f64, cos_(f64, 1.5), 0.070737, epsilon)); | |
| 109 | expect(math.approxEq(f64, cos_(f64, -1.5), 0.070737, epsilon)); | |
| 110 | expect(math.approxEq(f64, cos_(f64, 37.45), 0.969132, epsilon)); | |
| 111 | expect(math.approxEq(f64, cos_(f64, 89.123), 0.40080, epsilon)); | |
| 166 | 112 | } |
| 167 | 113 | |
| 168 | 114 | test "math.cos32.special" { |
| 169 | expect(math.isNan(cos32(math.inf(f32)))); | |
| 170 | expect(math.isNan(cos32(-math.inf(f32)))); | |
| 171 | expect(math.isNan(cos32(math.nan(f32)))); | |
| 115 | expect(math.isNan(cos_(f32, math.inf(f32)))); | |
| 116 | expect(math.isNan(cos_(f32, -math.inf(f32)))); | |
| 117 | expect(math.isNan(cos_(f32, math.nan(f32)))); | |
| 172 | 118 | } |
| 173 | 119 | |
| 174 | 120 | test "math.cos64.special" { |
| 175 | expect(math.isNan(cos64(math.inf(f64)))); | |
| 176 | expect(math.isNan(cos64(-math.inf(f64)))); | |
| 177 | expect(math.isNan(cos64(math.nan(f64)))); | |
| 121 | expect(math.isNan(cos_(f64, math.inf(f64)))); | |
| 122 | expect(math.isNan(cos_(f64, -math.inf(f64)))); | |
| 123 | expect(math.isNan(cos_(f64, math.nan(f64)))); | |
| 178 | 124 | } |
std/math/fma.zig+9-1| ... | ... | @@ -1,7 +1,14 @@ |
| 1 | // Ported from musl, which is licensed under the MIT license: | |
| 2 | // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT | |
| 3 | // | |
| 4 | // https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c | |
| 5 | // https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c | |
| 6 | ||
| 1 | 7 | const std = @import("../std.zig"); |
| 2 | 8 | const math = std.math; |
| 3 | 9 | const expect = std.testing.expect; |
| 4 | 10 | |
| 11 | /// Returns x * y + z with a single rounding error. | |
| 5 | 12 | pub fn fma(comptime T: type, x: T, y: T, z: T) T { |
| 6 | 13 | return switch (T) { |
| 7 | 14 | f32 => fma32(x, y, z), |
| ... | ... | @@ -16,7 +23,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 { |
| 16 | 23 | const u = @bitCast(u64, xy_z); |
| 17 | 24 | const e = (u >> 52) & 0x7FF; |
| 18 | 25 | |
| 19 | if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) { | |
| 26 | if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or (xy_z - xy == z and xy_z - z == xy)) { | |
| 20 | 27 | return @floatCast(f32, xy_z); |
| 21 | 28 | } else { |
| 22 | 29 | // TODO: Handle inexact case with double-rounding |
| ... | ... | @@ -24,6 +31,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 { |
| 24 | 31 | } |
| 25 | 32 | } |
| 26 | 33 | |
| 34 | // NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately. | |
| 27 | 35 | fn fma64(x: f64, y: f64, z: f64) f64 { |
| 28 | 36 | if (!math.isFinite(x) or !math.isFinite(y)) { |
| 29 | 37 | return x * y + z; |
std/math/pow.zig+57-39| ... | ... | @@ -1,32 +1,36 @@ |
| 1 | // Special Cases: | |
| 1 | // Ported from go, which is licensed under a BSD-3 license. | |
| 2 | // https://golang.org/LICENSE | |
| 2 | 3 | // |
| 3 | // pow(x, +-0) = 1 for any x | |
| 4 | // pow(1, y) = 1 for any y | |
| 5 | // pow(x, 1) = x for any x | |
| 6 | // pow(nan, y) = nan | |
| 7 | // pow(x, nan) = nan | |
| 8 | // pow(+-0, y) = +-inf for y an odd integer < 0 | |
| 9 | // pow(+-0, -inf) = +inf | |
| 10 | // pow(+-0, +inf) = +0 | |
| 11 | // pow(+-0, y) = +inf for finite y < 0 and not an odd integer | |
| 12 | // pow(+-0, y) = +-0 for y an odd integer > 0 | |
| 13 | // pow(+-0, y) = +0 for finite y > 0 and not an odd integer | |
| 14 | // pow(-1, +-inf) = 1 | |
| 15 | // pow(x, +inf) = +inf for |x| > 1 | |
| 16 | // pow(x, -inf) = +0 for |x| > 1 | |
| 17 | // pow(x, +inf) = +0 for |x| < 1 | |
| 18 | // pow(x, -inf) = +inf for |x| < 1 | |
| 19 | // pow(+inf, y) = +inf for y > 0 | |
| 20 | // pow(+inf, y) = +0 for y < 0 | |
| 21 | // pow(-inf, y) = pow(-0, -y) | |
| 22 | // pow(x, y) = nan for finite x < 0 and finite non-integer y | |
| 4 | // https://golang.org/src/math/pow.go | |
| 23 | 5 | |
| 24 | 6 | const builtin = @import("builtin"); |
| 25 | 7 | const std = @import("../std.zig"); |
| 26 | 8 | const math = std.math; |
| 27 | 9 | const expect = std.testing.expect; |
| 28 | 10 | |
| 29 | // This implementation is taken from the go stlib, musl is a bit more complex. | |
| 11 | /// Returns x raised to the power of y (x^y). | |
| 12 | /// | |
| 13 | /// Special Cases: | |
| 14 | /// - pow(x, +-0) = 1 for any x | |
| 15 | /// - pow(1, y) = 1 for any y | |
| 16 | /// - pow(x, 1) = x for any x | |
| 17 | /// - pow(nan, y) = nan | |
| 18 | /// - pow(x, nan) = nan | |
| 19 | /// - pow(+-0, y) = +-inf for y an odd integer < 0 | |
| 20 | /// - pow(+-0, -inf) = +inf | |
| 21 | /// - pow(+-0, +inf) = +0 | |
| 22 | /// - pow(+-0, y) = +inf for finite y < 0 and not an odd integer | |
| 23 | /// - pow(+-0, y) = +-0 for y an odd integer > 0 | |
| 24 | /// - pow(+-0, y) = +0 for finite y > 0 and not an odd integer | |
| 25 | /// - pow(-1, +-inf) = 1 | |
| 26 | /// - pow(x, +inf) = +inf for |x| > 1 | |
| 27 | /// - pow(x, -inf) = +0 for |x| > 1 | |
| 28 | /// - pow(x, +inf) = +0 for |x| < 1 | |
| 29 | /// - pow(x, -inf) = +inf for |x| < 1 | |
| 30 | /// - pow(+inf, y) = +inf for y > 0 | |
| 31 | /// - pow(+inf, y) = +0 for y < 0 | |
| 32 | /// - pow(-inf, y) = pow(-0, -y) | |
| 33 | /// - pow(x, y) = nan for finite x < 0 and finite non-integer y | |
| 30 | 34 | pub fn pow(comptime T: type, x: T, y: T) T { |
| 31 | 35 | if (@typeInfo(T) == builtin.TypeId.Int) { |
| 32 | 36 | return math.powi(T, x, y) catch unreachable; |
| ... | ... | @@ -53,15 +57,6 @@ pub fn pow(comptime T: type, x: T, y: T) T { |
| 53 | 57 | return x; |
| 54 | 58 | } |
| 55 | 59 | |
| 56 | // special case sqrt | |
| 57 | if (y == 0.5) { | |
| 58 | return math.sqrt(x); | |
| 59 | } | |
| 60 | ||
| 61 | if (y == -0.5) { | |
| 62 | return 1 / math.sqrt(x); | |
| 63 | } | |
| 64 | ||
| 65 | 60 | if (x == 0) { |
| 66 | 61 | if (y < 0) { |
| 67 | 62 | // pow(+-0, y) = +- 0 for y an odd integer |
| ... | ... | @@ -112,14 +107,16 @@ pub fn pow(comptime T: type, x: T, y: T) T { |
| 112 | 107 | } |
| 113 | 108 | } |
| 114 | 109 | |
| 115 | var ay = y; | |
| 116 | var flip = false; | |
| 117 | if (ay < 0) { | |
| 118 | ay = -ay; | |
| 119 | flip = true; | |
| 110 | // special case sqrt | |
| 111 | if (y == 0.5) { | |
| 112 | return math.sqrt(x); | |
| 113 | } | |
| 114 | ||
| 115 | if (y == -0.5) { | |
| 116 | return 1 / math.sqrt(x); | |
| 120 | 117 | } |
| 121 | 118 | |
| 122 | const r1 = math.modf(ay); | |
| 119 | const r1 = math.modf(math.fabs(y)); | |
| 123 | 120 | var yi = r1.ipart; |
| 124 | 121 | var yf = r1.fpart; |
| 125 | 122 | |
| ... | ... | @@ -148,8 +145,18 @@ pub fn pow(comptime T: type, x: T, y: T) T { |
| 148 | 145 | var xe = r2.exponent; |
| 149 | 146 | var x1 = r2.significand; |
| 150 | 147 | |
| 151 | var i = @floatToInt(i32, yi); | |
| 148 | var i = @floatToInt(@IntType(true, T.bit_count), yi); | |
| 152 | 149 | while (i != 0) : (i >>= 1) { |
| 150 | const overflow_shift = math.floatExponentBits(T) + 1; | |
| 151 | if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) { | |
| 152 | // catch xe before it overflows the left shift below | |
| 153 | // Since i != 0 it has at least one bit still set, so ae will accumulate xe | |
| 154 | // on at least one more iteration, ae += xe is a lower bound on ae | |
| 155 | // the lower bound on ae exceeds the size of a float exp | |
| 156 | // so the final call to Ldexp will produce under/overflow (0/Inf) | |
| 157 | ae += xe; | |
| 158 | break; | |
| 159 | } | |
| 153 | 160 | if (i & 1 == 1) { |
| 154 | 161 | a1 *= x1; |
| 155 | 162 | ae += xe; |
| ... | ... | @@ -163,7 +170,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { |
| 163 | 170 | } |
| 164 | 171 | |
| 165 | 172 | // a *= a1 * 2^ae |
| 166 | if (flip) { | |
| 173 | if (y < 0) { | |
| 167 | 174 | a1 = 1 / a1; |
| 168 | 175 | ae = -ae; |
| 169 | 176 | } |
| ... | ... | @@ -202,6 +209,9 @@ test "math.pow.special" { |
| 202 | 209 | expect(pow(f32, 45, 1.0) == 45); |
| 203 | 210 | expect(pow(f32, -45, 1.0) == -45); |
| 204 | 211 | expect(math.isNan(pow(f32, math.nan(f32), 5.0))); |
| 212 | expect(math.isPositiveInf(pow(f32, -math.inf(f32), 0.5))); | |
| 213 | expect(math.isPositiveInf(pow(f32, -0, -0.5))); | |
| 214 | expect(pow(f32, -0, 0.5) == 0); | |
| 205 | 215 | expect(math.isNan(pow(f32, 5.0, math.nan(f32)))); |
| 206 | 216 | expect(math.isPositiveInf(pow(f32, 0.0, -1.0))); |
| 207 | 217 | //expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required? |
| ... | ... | @@ -232,3 +242,11 @@ test "math.pow.special" { |
| 232 | 242 | expect(math.isNan(pow(f32, -1.0, 1.2))); |
| 233 | 243 | expect(math.isNan(pow(f32, -12.4, 78.5))); |
| 234 | 244 | } |
| 245 | ||
| 246 | test "math.pow.overflow" { | |
| 247 | expect(math.isPositiveInf(pow(f64, 2, 1 << 32))); | |
| 248 | expect(pow(f64, 2, -(1 << 32)) == 0); | |
| 249 | expect(math.isNegativeInf(pow(f64, -2, (1 << 32) + 1))); | |
| 250 | expect(pow(f64, 0.5, 1 << 45) == 0); | |
| 251 | expect(math.isPositiveInf(pow(f64, 0.5, -(1 << 45)))); | |
| 252 | } |
std/math/sin.zig+52-108| ... | ... | @@ -1,19 +1,24 @@ |
| 1 | // Special Cases: | |
| 1 | // Ported from go, which is licensed under a BSD-3 license. | |
| 2 | // https://golang.org/LICENSE | |
| 2 | 3 | // |
| 3 | // - sin(+-0) = +-0 | |
| 4 | // - sin(+-inf) = nan | |
| 5 | // - sin(nan) = nan | |
| 4 | // https://golang.org/src/math/sin.go | |
| 6 | 5 | |
| 7 | 6 | const builtin = @import("builtin"); |
| 8 | 7 | const std = @import("../std.zig"); |
| 9 | 8 | const math = std.math; |
| 10 | 9 | const expect = std.testing.expect; |
| 11 | 10 | |
| 11 | /// Returns the sine of the radian value x. | |
| 12 | /// | |
| 13 | /// Special Cases: | |
| 14 | /// - sin(+-0) = +-0 | |
| 15 | /// - sin(+-inf) = nan | |
| 16 | /// - sin(nan) = nan | |
| 12 | 17 | pub fn sin(x: var) @typeOf(x) { |
| 13 | 18 | const T = @typeOf(x); |
| 14 | 19 | return switch (T) { |
| 15 | f32 => sin32(x), | |
| 16 | f64 => sin64(x), | |
| 20 | f32 => sin_(T, x), | |
| 21 | f64 => sin_(T, x), | |
| 17 | 22 | else => @compileError("sin not implemented for " ++ @typeName(T)), |
| 18 | 23 | }; |
| 19 | 24 | } |
| ... | ... | @@ -34,83 +39,27 @@ const C3 = 2.48015872888517045348E-5; |
| 34 | 39 | const C4 = -1.38888888888730564116E-3; |
| 35 | 40 | const C5 = 4.16666666666665929218E-2; |
| 36 | 41 | |
| 37 | // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. | |
| 38 | // | |
| 39 | // This may have slight differences on some edge cases and may need to replaced if so. | |
| 40 | fn sin32(x_: f32) f32 { | |
| 41 | const pi4a = 7.85398125648498535156e-1; | |
| 42 | const pi4b = 3.77489470793079817668E-8; | |
| 43 | const pi4c = 2.69515142907905952645E-15; | |
| 44 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 45 | ||
| 46 | var x = x_; | |
| 47 | if (x == 0 or math.isNan(x)) { | |
| 48 | return x; | |
| 49 | } | |
| 50 | if (math.isInf(x)) { | |
| 51 | return math.nan(f32); | |
| 52 | } | |
| 53 | ||
| 54 | var sign = false; | |
| 55 | if (x < 0) { | |
| 56 | x = -x; | |
| 57 | sign = true; | |
| 58 | } | |
| 59 | ||
| 60 | var y = math.floor(x * m4pi); | |
| 61 | var j = @floatToInt(i64, y); | |
| 62 | ||
| 63 | if (j & 1 == 1) { | |
| 64 | j += 1; | |
| 65 | y += 1; | |
| 66 | } | |
| 42 | const pi4a = 7.85398125648498535156e-1; | |
| 43 | const pi4b = 3.77489470793079817668E-8; | |
| 44 | const pi4c = 2.69515142907905952645E-15; | |
| 45 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 67 | 46 | |
| 68 | j &= 7; | |
| 69 | if (j > 3) { | |
| 70 | j -= 4; | |
| 71 | sign = !sign; | |
| 72 | } | |
| 73 | ||
| 74 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 75 | const w = z * z; | |
| 76 | ||
| 77 | const r = r: { | |
| 78 | if (j == 1 or j == 2) { | |
| 79 | break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); | |
| 80 | } else { | |
| 81 | break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); | |
| 82 | } | |
| 83 | }; | |
| 84 | ||
| 85 | if (sign) { | |
| 86 | return -r; | |
| 87 | } else { | |
| 88 | return r; | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | fn sin64(x_: f64) f64 { | |
| 93 | const pi4a = 7.85398125648498535156e-1; | |
| 94 | const pi4b = 3.77489470793079817668E-8; | |
| 95 | const pi4c = 2.69515142907905952645E-15; | |
| 96 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 47 | fn sin_(comptime T: type, x_: T) T { | |
| 48 | const I = @IntType(true, T.bit_count); | |
| 97 | 49 | |
| 98 | 50 | var x = x_; |
| 99 | 51 | if (x == 0 or math.isNan(x)) { |
| 100 | 52 | return x; |
| 101 | 53 | } |
| 102 | 54 | if (math.isInf(x)) { |
| 103 | return math.nan(f64); | |
| 55 | return math.nan(T); | |
| 104 | 56 | } |
| 105 | 57 | |
| 106 | var sign = false; | |
| 107 | if (x < 0) { | |
| 108 | x = -x; | |
| 109 | sign = true; | |
| 110 | } | |
| 58 | var sign = x < 0; | |
| 59 | x = math.fabs(x); | |
| 111 | 60 | |
| 112 | 61 | var y = math.floor(x * m4pi); |
| 113 | var j = @floatToInt(i64, y); | |
| 62 | var j = @floatToInt(I, y); | |
| 114 | 63 | |
| 115 | 64 | if (j & 1 == 1) { |
| 116 | 65 | j += 1; |
| ... | ... | @@ -126,61 +75,56 @@ fn sin64(x_: f64) f64 { |
| 126 | 75 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; |
| 127 | 76 | const w = z * z; |
| 128 | 77 | |
| 129 | const r = r: { | |
| 130 | if (j == 1 or j == 2) { | |
| 131 | break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))); | |
| 132 | } else { | |
| 133 | break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); | |
| 134 | } | |
| 135 | }; | |
| 78 | const r = if (j == 1 or j == 2) | |
| 79 | 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0))))) | |
| 80 | else | |
| 81 | z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0))))); | |
| 136 | 82 | |
| 137 | if (sign) { | |
| 138 | return -r; | |
| 139 | } else { | |
| 140 | return r; | |
| 141 | } | |
| 83 | return if (sign) -r else r; | |
| 142 | 84 | } |
| 143 | 85 | |
| 144 | 86 | test "math.sin" { |
| 145 | expect(sin(f32(0.0)) == sin32(0.0)); | |
| 146 | expect(sin(f64(0.0)) == sin64(0.0)); | |
| 87 | expect(sin(f32(0.0)) == sin_(f32, 0.0)); | |
| 88 | expect(sin(f64(0.0)) == sin_(f64, 0.0)); | |
| 147 | 89 | expect(comptime (math.sin(f64(2))) == math.sin(f64(2))); |
| 148 | 90 | } |
| 149 | 91 | |
| 150 | 92 | test "math.sin32" { |
| 151 | 93 | const epsilon = 0.000001; |
| 152 | 94 | |
| 153 | expect(math.approxEq(f32, sin32(0.0), 0.0, epsilon)); | |
| 154 | expect(math.approxEq(f32, sin32(0.2), 0.198669, epsilon)); | |
| 155 | expect(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon)); | |
| 156 | expect(math.approxEq(f32, sin32(1.5), 0.997495, epsilon)); | |
| 157 | expect(math.approxEq(f32, sin32(37.45), -0.246544, epsilon)); | |
| 158 | expect(math.approxEq(f32, sin32(89.123), 0.916166, epsilon)); | |
| 95 | expect(math.approxEq(f32, sin_(f32, 0.0), 0.0, epsilon)); | |
| 96 | expect(math.approxEq(f32, sin_(f32, 0.2), 0.198669, epsilon)); | |
| 97 | expect(math.approxEq(f32, sin_(f32, 0.8923), 0.778517, epsilon)); | |
| 98 | expect(math.approxEq(f32, sin_(f32, 1.5), 0.997495, epsilon)); | |
| 99 | expect(math.approxEq(f32, sin_(f32, -1.5), -0.997495, epsilon)); | |
| 100 | expect(math.approxEq(f32, sin_(f32, 37.45), -0.246544, epsilon)); | |
| 101 | expect(math.approxEq(f32, sin_(f32, 89.123), 0.916166, epsilon)); | |
| 159 | 102 | } |
| 160 | 103 | |
| 161 | 104 | test "math.sin64" { |
| 162 | 105 | const epsilon = 0.000001; |
| 163 | 106 | |
| 164 | expect(math.approxEq(f64, sin64(0.0), 0.0, epsilon)); | |
| 165 | expect(math.approxEq(f64, sin64(0.2), 0.198669, epsilon)); | |
| 166 | expect(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon)); | |
| 167 | expect(math.approxEq(f64, sin64(1.5), 0.997495, epsilon)); | |
| 168 | expect(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); | |
| 169 | expect(math.approxEq(f64, sin64(89.123), 0.916166, epsilon)); | |
| 107 | expect(math.approxEq(f64, sin_(f64, 0.0), 0.0, epsilon)); | |
| 108 | expect(math.approxEq(f64, sin_(f64, 0.2), 0.198669, epsilon)); | |
| 109 | expect(math.approxEq(f64, sin_(f64, 0.8923), 0.778517, epsilon)); | |
| 110 | expect(math.approxEq(f64, sin_(f64, 1.5), 0.997495, epsilon)); | |
| 111 | expect(math.approxEq(f64, sin_(f64, -1.5), -0.997495, epsilon)); | |
| 112 | expect(math.approxEq(f64, sin_(f64, 37.45), -0.246543, epsilon)); | |
| 113 | expect(math.approxEq(f64, sin_(f64, 89.123), 0.916166, epsilon)); | |
| 170 | 114 | } |
| 171 | 115 | |
| 172 | 116 | test "math.sin32.special" { |
| 173 | expect(sin32(0.0) == 0.0); | |
| 174 | expect(sin32(-0.0) == -0.0); | |
| 175 | expect(math.isNan(sin32(math.inf(f32)))); | |
| 176 | expect(math.isNan(sin32(-math.inf(f32)))); | |
| 177 | expect(math.isNan(sin32(math.nan(f32)))); | |
| 117 | expect(sin_(f32, 0.0) == 0.0); | |
| 118 | expect(sin_(f32, -0.0) == -0.0); | |
| 119 | expect(math.isNan(sin_(f32, math.inf(f32)))); | |
| 120 | expect(math.isNan(sin_(f32, -math.inf(f32)))); | |
| 121 | expect(math.isNan(sin_(f32, math.nan(f32)))); | |
| 178 | 122 | } |
| 179 | 123 | |
| 180 | 124 | test "math.sin64.special" { |
| 181 | expect(sin64(0.0) == 0.0); | |
| 182 | expect(sin64(-0.0) == -0.0); | |
| 183 | expect(math.isNan(sin64(math.inf(f64)))); | |
| 184 | expect(math.isNan(sin64(-math.inf(f64)))); | |
| 185 | expect(math.isNan(sin64(math.nan(f64)))); | |
| 125 | expect(sin_(f64, 0.0) == 0.0); | |
| 126 | expect(sin_(f64, -0.0) == -0.0); | |
| 127 | expect(math.isNan(sin_(f64, math.inf(f64)))); | |
| 128 | expect(math.isNan(sin_(f64, -math.inf(f64)))); | |
| 129 | expect(math.isNan(sin_(f64, math.nan(f64)))); | |
| 186 | 130 | } |
std/math/tan.zig+50-104| ... | ... | @@ -1,19 +1,24 @@ |
| 1 | // Special Cases: | |
| 1 | // Ported from go, which is licensed under a BSD-3 license. | |
| 2 | // https://golang.org/LICENSE | |
| 2 | 3 | // |
| 3 | // - tan(+-0) = +-0 | |
| 4 | // - tan(+-inf) = nan | |
| 5 | // - tan(nan) = nan | |
| 4 | // https://golang.org/src/math/tan.go | |
| 6 | 5 | |
| 7 | 6 | const builtin = @import("builtin"); |
| 8 | 7 | const std = @import("../std.zig"); |
| 9 | 8 | const math = std.math; |
| 10 | 9 | const expect = std.testing.expect; |
| 11 | 10 | |
| 11 | /// Returns the tangent of the radian value x. | |
| 12 | /// | |
| 13 | /// Special Cases: | |
| 14 | /// - tan(+-0) = +-0 | |
| 15 | /// - tan(+-inf) = nan | |
| 16 | /// - tan(nan) = nan | |
| 12 | 17 | pub fn tan(x: var) @typeOf(x) { |
| 13 | 18 | const T = @typeOf(x); |
| 14 | 19 | return switch (T) { |
| 15 | f32 => tan32(x), | |
| 16 | f64 => tan64(x), | |
| 20 | f32 => tan_(f32, x), | |
| 21 | f64 => tan_(f64, x), | |
| 17 | 22 | else => @compileError("tan not implemented for " ++ @typeName(T)), |
| 18 | 23 | }; |
| 19 | 24 | } |
| ... | ... | @@ -27,80 +32,27 @@ const Tq2 = -1.32089234440210967447E6; |
| 27 | 32 | const Tq3 = 2.50083801823357915839E7; |
| 28 | 33 | const Tq4 = -5.38695755929454629881E7; |
| 29 | 34 | |
| 30 | // NOTE: This is taken from the go stdlib. The musl implementation is much more complex. | |
| 31 | // | |
| 32 | // This may have slight differences on some edge cases and may need to replaced if so. | |
| 33 | fn tan32(x_: f32) f32 { | |
| 34 | const pi4a = 7.85398125648498535156e-1; | |
| 35 | const pi4b = 3.77489470793079817668E-8; | |
| 36 | const pi4c = 2.69515142907905952645E-15; | |
| 37 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 38 | ||
| 39 | var x = x_; | |
| 40 | if (x == 0 or math.isNan(x)) { | |
| 41 | return x; | |
| 42 | } | |
| 43 | if (math.isInf(x)) { | |
| 44 | return math.nan(f32); | |
| 45 | } | |
| 46 | ||
| 47 | var sign = false; | |
| 48 | if (x < 0) { | |
| 49 | x = -x; | |
| 50 | sign = true; | |
| 51 | } | |
| 52 | ||
| 53 | var y = math.floor(x * m4pi); | |
| 54 | var j = @floatToInt(i64, y); | |
| 55 | ||
| 56 | if (j & 1 == 1) { | |
| 57 | j += 1; | |
| 58 | y += 1; | |
| 59 | } | |
| 60 | ||
| 61 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; | |
| 62 | const w = z * z; | |
| 63 | ||
| 64 | var r = r: { | |
| 65 | if (w > 1e-14) { | |
| 66 | break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)); | |
| 67 | } else { | |
| 68 | break :r z; | |
| 69 | } | |
| 70 | }; | |
| 71 | ||
| 72 | if (j & 2 == 2) { | |
| 73 | r = -1 / r; | |
| 74 | } | |
| 75 | if (sign) { | |
| 76 | r = -r; | |
| 77 | } | |
| 78 | ||
| 79 | return r; | |
| 80 | } | |
| 35 | const pi4a = 7.85398125648498535156e-1; | |
| 36 | const pi4b = 3.77489470793079817668E-8; | |
| 37 | const pi4c = 2.69515142907905952645E-15; | |
| 38 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 81 | 39 | |
| 82 | fn tan64(x_: f64) f64 { | |
| 83 | const pi4a = 7.85398125648498535156e-1; | |
| 84 | const pi4b = 3.77489470793079817668E-8; | |
| 85 | const pi4c = 2.69515142907905952645E-15; | |
| 86 | const m4pi = 1.273239544735162542821171882678754627704620361328125; | |
| 40 | fn tan_(comptime T: type, x_: T) T { | |
| 41 | const I = @IntType(true, T.bit_count); | |
| 87 | 42 | |
| 88 | 43 | var x = x_; |
| 89 | 44 | if (x == 0 or math.isNan(x)) { |
| 90 | 45 | return x; |
| 91 | 46 | } |
| 92 | 47 | if (math.isInf(x)) { |
| 93 | return math.nan(f64); | |
| 48 | return math.nan(T); | |
| 94 | 49 | } |
| 95 | 50 | |
| 96 | var sign = false; | |
| 97 | if (x < 0) { | |
| 98 | x = -x; | |
| 99 | sign = true; | |
| 100 | } | |
| 51 | var sign = x < 0; | |
| 52 | x = math.fabs(x); | |
| 101 | 53 | |
| 102 | 54 | var y = math.floor(x * m4pi); |
| 103 | var j = @floatToInt(i64, y); | |
| 55 | var j = @floatToInt(I, y); | |
| 104 | 56 | |
| 105 | 57 | if (j & 1 == 1) { |
| 106 | 58 | j += 1; |
| ... | ... | @@ -110,63 +62,57 @@ fn tan64(x_: f64) f64 { |
| 110 | 62 | const z = ((x - y * pi4a) - y * pi4b) - y * pi4c; |
| 111 | 63 | const w = z * z; |
| 112 | 64 | |
| 113 | var r = r: { | |
| 114 | if (w > 1e-14) { | |
| 115 | break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)); | |
| 116 | } else { | |
| 117 | break :r z; | |
| 118 | } | |
| 119 | }; | |
| 65 | var r = if (w > 1e-14) | |
| 66 | z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4)) | |
| 67 | else | |
| 68 | z; | |
| 120 | 69 | |
| 121 | 70 | if (j & 2 == 2) { |
| 122 | 71 | r = -1 / r; |
| 123 | 72 | } |
| 124 | if (sign) { | |
| 125 | r = -r; | |
| 126 | } | |
| 127 | 73 | |
| 128 | return r; | |
| 74 | return if (sign) -r else r; | |
| 129 | 75 | } |
| 130 | 76 | |
| 131 | 77 | test "math.tan" { |
| 132 | expect(tan(f32(0.0)) == tan32(0.0)); | |
| 133 | expect(tan(f64(0.0)) == tan64(0.0)); | |
| 78 | expect(tan(f32(0.0)) == tan_(f32, 0.0)); | |
| 79 | expect(tan(f64(0.0)) == tan_(f64, 0.0)); | |
| 134 | 80 | } |
| 135 | 81 | |
| 136 | 82 | test "math.tan32" { |
| 137 | 83 | const epsilon = 0.000001; |
| 138 | 84 | |
| 139 | expect(math.approxEq(f32, tan32(0.0), 0.0, epsilon)); | |
| 140 | expect(math.approxEq(f32, tan32(0.2), 0.202710, epsilon)); | |
| 141 | expect(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon)); | |
| 142 | expect(math.approxEq(f32, tan32(1.5), 14.101420, epsilon)); | |
| 143 | expect(math.approxEq(f32, tan32(37.45), -0.254397, epsilon)); | |
| 144 | expect(math.approxEq(f32, tan32(89.123), 2.285852, epsilon)); | |
| 85 | expect(math.approxEq(f32, tan_(f32, 0.0), 0.0, epsilon)); | |
| 86 | expect(math.approxEq(f32, tan_(f32, 0.2), 0.202710, epsilon)); | |
| 87 | expect(math.approxEq(f32, tan_(f32, 0.8923), 1.240422, epsilon)); | |
| 88 | expect(math.approxEq(f32, tan_(f32, 1.5), 14.101420, epsilon)); | |
| 89 | expect(math.approxEq(f32, tan_(f32, 37.45), -0.254397, epsilon)); | |
| 90 | expect(math.approxEq(f32, tan_(f32, 89.123), 2.285852, epsilon)); | |
| 145 | 91 | } |
| 146 | 92 | |
| 147 | 93 | test "math.tan64" { |
| 148 | 94 | const epsilon = 0.000001; |
| 149 | 95 | |
| 150 | expect(math.approxEq(f64, tan64(0.0), 0.0, epsilon)); | |
| 151 | expect(math.approxEq(f64, tan64(0.2), 0.202710, epsilon)); | |
| 152 | expect(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon)); | |
| 153 | expect(math.approxEq(f64, tan64(1.5), 14.101420, epsilon)); | |
| 154 | expect(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); | |
| 155 | expect(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); | |
| 96 | expect(math.approxEq(f64, tan_(f64, 0.0), 0.0, epsilon)); | |
| 97 | expect(math.approxEq(f64, tan_(f64, 0.2), 0.202710, epsilon)); | |
| 98 | expect(math.approxEq(f64, tan_(f64, 0.8923), 1.240422, epsilon)); | |
| 99 | expect(math.approxEq(f64, tan_(f64, 1.5), 14.101420, epsilon)); | |
| 100 | expect(math.approxEq(f64, tan_(f64, 37.45), -0.254397, epsilon)); | |
| 101 | expect(math.approxEq(f64, tan_(f64, 89.123), 2.2858376, epsilon)); | |
| 156 | 102 | } |
| 157 | 103 | |
| 158 | 104 | test "math.tan32.special" { |
| 159 | expect(tan32(0.0) == 0.0); | |
| 160 | expect(tan32(-0.0) == -0.0); | |
| 161 | expect(math.isNan(tan32(math.inf(f32)))); | |
| 162 | expect(math.isNan(tan32(-math.inf(f32)))); | |
| 163 | expect(math.isNan(tan32(math.nan(f32)))); | |
| 105 | expect(tan_(f32, 0.0) == 0.0); | |
| 106 | expect(tan_(f32, -0.0) == -0.0); | |
| 107 | expect(math.isNan(tan_(f32, math.inf(f32)))); | |
| 108 | expect(math.isNan(tan_(f32, -math.inf(f32)))); | |
| 109 | expect(math.isNan(tan_(f32, math.nan(f32)))); | |
| 164 | 110 | } |
| 165 | 111 | |
| 166 | 112 | test "math.tan64.special" { |
| 167 | expect(tan64(0.0) == 0.0); | |
| 168 | expect(tan64(-0.0) == -0.0); | |
| 169 | expect(math.isNan(tan64(math.inf(f64)))); | |
| 170 | expect(math.isNan(tan64(-math.inf(f64)))); | |
| 171 | expect(math.isNan(tan64(math.nan(f64)))); | |
| 113 | expect(tan_(f64, 0.0) == 0.0); | |
| 114 | expect(tan_(f64, -0.0) == -0.0); | |
| 115 | expect(math.isNan(tan_(f64, math.inf(f64)))); | |
| 116 | expect(math.isNan(tan_(f64, -math.inf(f64)))); | |
| 117 | expect(math.isNan(tan_(f64, math.nan(f64)))); | |
| 172 | 118 | } |