| author | |
| committer | |
| log | 5bbec42a4edb3f6758a9d67fcf763c660ac3f204 |
| tree | 3dc324d083f142935df53c5900edfc04c0f956f8 |
| parent | c9fc8bd802f5ed52c4cc78b93f18fc5dc9b6bb7f |
- Should cover special case inputs for most functions
- Fixed a number of runtime panicking behaviour reliant on shift
overflow/division by zero etc.37 files changed, 964 insertions(+), 82 deletions(-)
std/math/_expo2.zig deleted-28| ... | @@ -1,28 +0,0 @@ | ||
| 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+16-2| ... | @@ -1,3 +1,7 @@ | ... | @@ -1,3 +1,7 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - acos(x) = nan if x < -1 or x > 1 | ||
| 4 | |||
| 1 | const math = @import("index.zig"); | 5 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 6 | const assert = @import("../debug.zig").assert; |
| 3 | 7 | ||
| ... | @@ -40,7 +44,7 @@ fn acos32(x: f32) -> f32 { | ... | @@ -40,7 +44,7 @@ fn acos32(x: f32) -> f32 { |
| 40 | return 0; | 44 | return 0; |
| 41 | } | 45 | } |
| 42 | } else { | 46 | } else { |
| 43 | return 0 / (x - x); | 47 | return math.nan(f32); |
| 44 | } | 48 | } |
| 45 | } | 49 | } |
| 46 | 50 | ||
| ... | @@ -109,7 +113,7 @@ fn acos64(x: f64) -> f64 { | ... | @@ -109,7 +113,7 @@ fn acos64(x: f64) -> f64 { |
| 109 | } | 113 | } |
| 110 | } | 114 | } |
| 111 | 115 | ||
| 112 | return 0 / (x - x); | 116 | return math.nan(f32); |
| 113 | } | 117 | } |
| 114 | 118 | ||
| 115 | // |x| < 0.5 | 119 | // |x| < 0.5 |
| ... | @@ -166,3 +170,13 @@ test "math.acos64" { | ... | @@ -166,3 +170,13 @@ test "math.acos64" { |
| 166 | assert(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon)); | 170 | assert(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon)); |
| 167 | assert(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon)); | 171 | assert(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon)); |
| 168 | } | 172 | } |
| 173 | |||
| 174 | test "math.acos32.special" { | ||
| 175 | assert(math.isNan(acos32(-2))); | ||
| 176 | assert(math.isNan(acos32(1.5))); | ||
| 177 | } | ||
| 178 | |||
| 179 | test "math.acos64.special" { | ||
| 180 | assert(math.isNan(acos64(-2))); | ||
| 181 | assert(math.isNan(acos64(1.5))); | ||
| 182 | } |
std/math/acosh.zig+15| ... | @@ -1,3 +1,8 @@ | ... | @@ -1,3 +1,8 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - acosh(x) = snan if x < 1 | ||
| 4 | // - acosh(nan) = nan | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| ... | @@ -72,3 +77,13 @@ test "math.acosh64" { | ... | @@ -72,3 +77,13 @@ test "math.acosh64" { |
| 72 | assert(math.approxEq(f64, acosh64(89.123), 5.183133, epsilon)); | 77 | assert(math.approxEq(f64, acosh64(89.123), 5.183133, epsilon)); |
| 73 | assert(math.approxEq(f64, acosh64(123123.234375), 12.414088, epsilon)); | 78 | assert(math.approxEq(f64, acosh64(123123.234375), 12.414088, epsilon)); |
| 74 | } | 79 | } |
| 80 | |||
| 81 | test "math.acosh32.special" { | ||
| 82 | assert(math.isNan(acosh32(math.nan(f32)))); | ||
| 83 | assert(math.isSignalNan(acosh32(0.5))); | ||
| 84 | } | ||
| 85 | |||
| 86 | test "math.acosh64.special" { | ||
| 87 | assert(math.isNan(acosh64(math.nan(f64)))); | ||
| 88 | assert(math.isSignalNan(acosh64(0.5))); | ||
| 89 | } |
std/math/asin.zig+21-2| ... | @@ -1,3 +1,8 @@ | ... | @@ -1,3 +1,8 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - asin(+-0) = +-0 | ||
| 4 | // - asin(x) = nan if x < -1 or x > 1 | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| ... | @@ -36,7 +41,7 @@ fn asin32(x: f32) -> f32 { | ... | @@ -36,7 +41,7 @@ fn asin32(x: f32) -> f32 { |
| 36 | if (ix == 0x3F800000) { | 41 | if (ix == 0x3F800000) { |
| 37 | return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact | 42 | return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact |
| 38 | } else { | 43 | } else { |
| 39 | return 0 / (x - x); // asin(|x| > 1) is nan | 44 | return math.nan(f32); // asin(|x| > 1) is nan |
| 40 | } | 45 | } |
| 41 | } | 46 | } |
| 42 | 47 | ||
| ... | @@ -95,7 +100,7 @@ fn asin64(x: f64) -> f64 { | ... | @@ -95,7 +100,7 @@ fn asin64(x: f64) -> f64 { |
| 95 | if ((ix - 0x3FF00000) | lx == 0) { | 100 | if ((ix - 0x3FF00000) | lx == 0) { |
| 96 | return x * pio2_hi + 0x1.0p-120; | 101 | return x * pio2_hi + 0x1.0p-120; |
| 97 | } else { | 102 | } else { |
| 98 | return 0/ (x - x); | 103 | return math.nan(f64); |
| 99 | } | 104 | } |
| 100 | } | 105 | } |
| 101 | 106 | ||
| ... | @@ -158,3 +163,17 @@ test "math.asin64" { | ... | @@ -158,3 +163,17 @@ test "math.asin64" { |
| 158 | assert(math.approxEq(f64, asin64(0.5), 0.523599, epsilon)); | 163 | assert(math.approxEq(f64, asin64(0.5), 0.523599, epsilon)); |
| 159 | assert(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon)); | 164 | assert(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon)); |
| 160 | } | 165 | } |
| 166 | |||
| 167 | test "math.asin32.special" { | ||
| 168 | assert(asin32(0.0) == 0.0); | ||
| 169 | assert(asin32(-0.0) == -0.0); | ||
| 170 | assert(math.isNan(asin32(-2))); | ||
| 171 | assert(math.isNan(asin32(1.5))); | ||
| 172 | } | ||
| 173 | |||
| 174 | test "math.asin64.special" { | ||
| 175 | assert(asin64(0.0) == 0.0); | ||
| 176 | assert(asin64(-0.0) == -0.0); | ||
| 177 | assert(math.isNan(asin64(-2))); | ||
| 178 | assert(math.isNan(asin64(1.5))); | ||
| 179 | } |
std/math/asinh.zig+31| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - asinh(+-0) = +-0 | ||
| 4 | // - asinh(+-inf) = +-inf | ||
| 5 | // - asinh(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -21,6 +27,11 @@ fn asinh32(x: f32) -> f32 { | ... | @@ -21,6 +27,11 @@ fn asinh32(x: f32) -> f32 { |
| 21 | 27 | ||
| 22 | var rx = @bitCast(f32, i); // |x| | 28 | var rx = @bitCast(f32, i); // |x| |
| 23 | 29 | ||
| 30 | // TODO: Shouldn't need this explicit check. | ||
| 31 | if (math.isNegativeInf(x)) { | ||
| 32 | return x; | ||
| 33 | } | ||
| 34 | |||
| 24 | // |x| >= 0x1p12 or inf or nan | 35 | // |x| >= 0x1p12 or inf or nan |
| 25 | if (i >= 0x3F800000 + (12 << 23)) { | 36 | if (i >= 0x3F800000 + (12 << 23)) { |
| 26 | rx = math.ln(rx) + 0.69314718055994530941723212145817656; | 37 | rx = math.ln(rx) + 0.69314718055994530941723212145817656; |
| ... | @@ -48,6 +59,10 @@ fn asinh64(x: f64) -> f64 { | ... | @@ -48,6 +59,10 @@ fn asinh64(x: f64) -> f64 { |
| 48 | 59 | ||
| 49 | var rx = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x| | 60 | var rx = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x| |
| 50 | 61 | ||
| 62 | if (math.isNegativeInf(x)) { | ||
| 63 | return x; | ||
| 64 | } | ||
| 65 | |||
| 51 | // |x| >= 0x1p26 or inf or nan | 66 | // |x| >= 0x1p26 or inf or nan |
| 52 | if (e >= 0x3FF + 26) { | 67 | if (e >= 0x3FF + 26) { |
| 53 | rx = math.ln(rx) + 0.693147180559945309417232121458176568; | 68 | rx = math.ln(rx) + 0.693147180559945309417232121458176568; |
| ... | @@ -96,3 +111,19 @@ test "math.asinh64" { | ... | @@ -96,3 +111,19 @@ test "math.asinh64" { |
| 96 | assert(math.approxEq(f64, asinh64(89.123), 5.183196, epsilon)); | 111 | assert(math.approxEq(f64, asinh64(89.123), 5.183196, epsilon)); |
| 97 | assert(math.approxEq(f64, asinh64(123123.234375), 12.414088, epsilon)); | 112 | assert(math.approxEq(f64, asinh64(123123.234375), 12.414088, epsilon)); |
| 98 | } | 113 | } |
| 114 | |||
| 115 | test "math.asinh32.special" { | ||
| 116 | assert(asinh32(0.0) == 0.0); | ||
| 117 | assert(asinh32(-0.0) == -0.0); | ||
| 118 | assert(math.isPositiveInf(asinh32(math.inf(f32)))); | ||
| 119 | assert(math.isNegativeInf(asinh32(-math.inf(f32)))); | ||
| 120 | assert(math.isNan(asinh32(math.nan(f32)))); | ||
| 121 | } | ||
| 122 | |||
| 123 | test "math.asinh64.special" { | ||
| 124 | assert(asinh64(0.0) == 0.0); | ||
| 125 | assert(asinh64(-0.0) == -0.0); | ||
| 126 | assert(math.isPositiveInf(asinh64(math.inf(f64)))); | ||
| 127 | assert(math.isNegativeInf(asinh64(-math.inf(f64)))); | ||
| 128 | assert(math.isNan(asinh64(math.nan(f64)))); | ||
| 129 | } |
std/math/atan.zig+23| ... | @@ -1,3 +1,8 @@ | ... | @@ -1,3 +1,8 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - atan(+-0) = +-0 | ||
| 4 | // - atan(+-inf) = +-pi/2 | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| ... | @@ -228,3 +233,21 @@ test "math.atan64" { | ... | @@ -228,3 +233,21 @@ test "math.atan64" { |
| 228 | assert(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon)); | 233 | assert(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon)); |
| 229 | assert(math.approxEq(f64, atan64(1.5), 0.982794, epsilon)); | 234 | assert(math.approxEq(f64, atan64(1.5), 0.982794, epsilon)); |
| 230 | } | 235 | } |
| 236 | |||
| 237 | test "math.atan32.special" { | ||
| 238 | const epsilon = 0.000001; | ||
| 239 | |||
| 240 | assert(atan32(0.0) == 0.0); | ||
| 241 | assert(atan32(-0.0) == -0.0); | ||
| 242 | assert(math.approxEq(f32, atan32(math.inf(f32)), math.pi_2, epsilon)); | ||
| 243 | assert(math.approxEq(f32, atan32(-math.inf(f32)), -math.pi_2, epsilon)); | ||
| 244 | } | ||
| 245 | |||
| 246 | test "math.atan64.special" { | ||
| 247 | const epsilon = 0.000001; | ||
| 248 | |||
| 249 | assert(atan64(0.0) == 0.0); | ||
| 250 | assert(atan64(-0.0) == -0.0); | ||
| 251 | assert(math.approxEq(f64, atan64(math.inf(f64)), math.pi_2, epsilon)); | ||
| 252 | assert(math.approxEq(f64, atan64(-math.inf(f64)), -math.pi_2, epsilon)); | ||
| 253 | } |
std/math/atan2.zig+68| ... | @@ -1,3 +1,23 @@ | ... | @@ -1,3 +1,23 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // atan2(y, nan) = nan | ||
| 4 | // atan2(nan, x) = nan | ||
| 5 | // atan2(+0, x>=0) = +0 | ||
| 6 | // atan2(-0, x>=0) = -0 | ||
| 7 | // atan2(+0, x<=-0) = +pi | ||
| 8 | // atan2(-0, x<=-0) = -pi | ||
| 9 | // atan2(y>0, 0) = +pi/2 | ||
| 10 | // atan2(y<0, 0) = -pi/2 | ||
| 11 | // atan2(+inf, +inf) = +pi/4 | ||
| 12 | // atan2(-inf, +inf) = -pi/4 | ||
| 13 | // atan2(+inf, -inf) = 3pi/4 | ||
| 14 | // atan2(-inf, -inf) = -3pi/4 | ||
| 15 | // atan2(y, +inf) = 0 | ||
| 16 | // atan2(y>0, -inf) = +pi | ||
| 17 | // atan2(y<0, -inf) = -pi | ||
| 18 | // atan2(+inf, x) = +pi/2 | ||
| 19 | // atan2(-inf, x) = -pi/2 | ||
| 20 | |||
| 1 | const math = @import("index.zig"); | 21 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 22 | const assert = @import("../debug.zig").assert; |
| 3 | 23 | ||
| ... | @@ -215,3 +235,51 @@ test "math.atan2_64" { | ... | @@ -215,3 +235,51 @@ test "math.atan2_64" { |
| 215 | assert(math.approxEq(f64, atan2_64(0.34, -0.4), 2.437099, epsilon)); | 235 | assert(math.approxEq(f64, atan2_64(0.34, -0.4), 2.437099, epsilon)); |
| 216 | assert(math.approxEq(f64, atan2_64(0.34, 1.243), 0.267001, epsilon)); | 236 | assert(math.approxEq(f64, atan2_64(0.34, 1.243), 0.267001, epsilon)); |
| 217 | } | 237 | } |
| 238 | |||
| 239 | test "math.atan2_32.special" { | ||
| 240 | const epsilon = 0.000001; | ||
| 241 | |||
| 242 | assert(math.isNan(atan2_32(1.0, math.nan(f32)))); | ||
| 243 | assert(math.isNan(atan2_32(math.nan(f32), 1.0))); | ||
| 244 | assert(atan2_32(0.0, 5.0) == 0.0); | ||
| 245 | assert(atan2_32(-0.0, 5.0) == -0.0); | ||
| 246 | assert(math.approxEq(f32, atan2_32(0.0, -5.0), math.pi, epsilon)); | ||
| 247 | assert(math.approxEq(f32, atan2_32(-0.0, -5.0), -math.pi, epsilon)); | ||
| 248 | assert(math.approxEq(f32, atan2_32(1.0, 0.0), math.pi_2, epsilon)); | ||
| 249 | assert(math.approxEq(f32, atan2_32(1.0, -0.0), math.pi_2, epsilon)); | ||
| 250 | assert(math.approxEq(f32, atan2_32(-1.0, 0.0), -math.pi_2, epsilon)); | ||
| 251 | assert(math.approxEq(f32, atan2_32(-1.0, -0.0), -math.pi_2, epsilon)); | ||
| 252 | assert(math.approxEq(f32, atan2_32(math.inf(f32), math.inf(f32)), math.pi_4, epsilon)); | ||
| 253 | assert(math.approxEq(f32, atan2_32(-math.inf(f32), math.inf(f32)), -math.pi_4, epsilon)); | ||
| 254 | assert(math.approxEq(f32, atan2_32(math.inf(f32), -math.inf(f32)), 3.0 * math.pi_4, epsilon)); | ||
| 255 | assert(math.approxEq(f32, atan2_32(-math.inf(f32), -math.inf(f32)), -3.0 * math.pi_4, epsilon)); | ||
| 256 | assert(atan2_32(1.0, math.inf(f32)) == 0.0); | ||
| 257 | assert(math.approxEq(f32, atan2_32(1.0, -math.inf(f32)), math.pi, epsilon)); | ||
| 258 | assert(math.approxEq(f32, atan2_32(-1.0, -math.inf(f32)), -math.pi, epsilon)); | ||
| 259 | assert(math.approxEq(f32, atan2_32(math.inf(f32), 1.0), math.pi_2, epsilon)); | ||
| 260 | assert(math.approxEq(f32, atan2_32(-math.inf(f32), 1.0), -math.pi_2, epsilon)); | ||
| 261 | } | ||
| 262 | |||
| 263 | test "math.atan2_64.special" { | ||
| 264 | const epsilon = 0.000001; | ||
| 265 | |||
| 266 | assert(math.isNan(atan2_64(1.0, math.nan(f64)))); | ||
| 267 | assert(math.isNan(atan2_64(math.nan(f64), 1.0))); | ||
| 268 | assert(atan2_64(0.0, 5.0) == 0.0); | ||
| 269 | assert(atan2_64(-0.0, 5.0) == -0.0); | ||
| 270 | assert(math.approxEq(f64, atan2_64(0.0, -5.0), math.pi, epsilon)); | ||
| 271 | assert(math.approxEq(f64, atan2_64(-0.0, -5.0), -math.pi, epsilon)); | ||
| 272 | assert(math.approxEq(f64, atan2_64(1.0, 0.0), math.pi_2, epsilon)); | ||
| 273 | assert(math.approxEq(f64, atan2_64(1.0, -0.0), math.pi_2, epsilon)); | ||
| 274 | assert(math.approxEq(f64, atan2_64(-1.0, 0.0), -math.pi_2, epsilon)); | ||
| 275 | assert(math.approxEq(f64, atan2_64(-1.0, -0.0), -math.pi_2, epsilon)); | ||
| 276 | assert(math.approxEq(f64, atan2_64(math.inf(f64), math.inf(f64)), math.pi_4, epsilon)); | ||
| 277 | assert(math.approxEq(f64, atan2_64(-math.inf(f64), math.inf(f64)), -math.pi_4, epsilon)); | ||
| 278 | assert(math.approxEq(f64, atan2_64(math.inf(f64), -math.inf(f64)), 3.0 * math.pi_4, epsilon)); | ||
| 279 | assert(math.approxEq(f64, atan2_64(-math.inf(f64), -math.inf(f64)), -3.0 * math.pi_4, epsilon)); | ||
| 280 | assert(atan2_64(1.0, math.inf(f64)) == 0.0); | ||
| 281 | assert(math.approxEq(f64, atan2_64(1.0, -math.inf(f64)), math.pi, epsilon)); | ||
| 282 | assert(math.approxEq(f64, atan2_64(-1.0, -math.inf(f64)), -math.pi, epsilon)); | ||
| 283 | assert(math.approxEq(f64, atan2_64(math.inf(f64), 1.0), math.pi_2, epsilon)); | ||
| 284 | assert(math.approxEq(f64, atan2_64(-math.inf(f64), 1.0), -math.pi_2, epsilon)); | ||
| 285 | } |
std/math/atanh.zig+30-2| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - atanh(+-1) = +-inf with signal | ||
| 4 | // - atanh(x) = nan if |x| > 1 with signal | ||
| 5 | // - atanh(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -21,6 +27,10 @@ fn atanh_32(x: f32) -> f32 { | ... | @@ -21,6 +27,10 @@ fn atanh_32(x: f32) -> f32 { |
| 21 | 27 | ||
| 22 | var y = @bitCast(f32, i); // |x| | 28 | var y = @bitCast(f32, i); // |x| |
| 23 | 29 | ||
| 30 | if (y == 1.0) { | ||
| 31 | return math.copysign(f32, math.inf(f32), x); | ||
| 32 | } | ||
| 33 | |||
| 24 | if (u < 0x3F800000 - (1 << 23)) { | 34 | if (u < 0x3F800000 - (1 << 23)) { |
| 25 | if (u < 0x3F800000 - (32 << 23)) { | 35 | if (u < 0x3F800000 - (32 << 23)) { |
| 26 | // underflow | 36 | // underflow |
| ... | @@ -33,7 +43,6 @@ fn atanh_32(x: f32) -> f32 { | ... | @@ -33,7 +43,6 @@ fn atanh_32(x: f32) -> f32 { |
| 33 | y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y)); | 43 | y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y)); |
| 34 | } | 44 | } |
| 35 | } else { | 45 | } else { |
| 36 | // avoid overflow | ||
| 37 | y = 0.5 * math.log1p(2 * (y / (1 - y))); | 46 | y = 0.5 * math.log1p(2 * (y / (1 - y))); |
| 38 | } | 47 | } |
| 39 | 48 | ||
| ... | @@ -47,6 +56,10 @@ fn atanh_64(x: f64) -> f64 { | ... | @@ -47,6 +56,10 @@ fn atanh_64(x: f64) -> f64 { |
| 47 | 56 | ||
| 48 | var y = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x| | 57 | var y = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x| |
| 49 | 58 | ||
| 59 | if (y == 1.0) { | ||
| 60 | return math.copysign(f64, math.inf(f64), x); | ||
| 61 | } | ||
| 62 | |||
| 50 | if (e < 0x3FF - 1) { | 63 | if (e < 0x3FF - 1) { |
| 51 | if (e < 0x3FF - 32) { | 64 | if (e < 0x3FF - 32) { |
| 52 | // underflow | 65 | // underflow |
| ... | @@ -59,7 +72,6 @@ fn atanh_64(x: f64) -> f64 { | ... | @@ -59,7 +72,6 @@ fn atanh_64(x: f64) -> f64 { |
| 59 | y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y)); | 72 | y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y)); |
| 60 | } | 73 | } |
| 61 | } else { | 74 | } else { |
| 62 | // avoid overflow | ||
| 63 | y = 0.5 * math.log1p(2 * (y / (1 - y))); | 75 | y = 0.5 * math.log1p(2 * (y / (1 - y))); |
| 64 | } | 76 | } |
| 65 | 77 | ||
| ... | @@ -86,3 +98,19 @@ test "math.atanh_64" { | ... | @@ -86,3 +98,19 @@ test "math.atanh_64" { |
| 86 | assert(math.approxEq(f64, atanh_64(0.2), 0.202733, epsilon)); | 98 | assert(math.approxEq(f64, atanh_64(0.2), 0.202733, epsilon)); |
| 87 | assert(math.approxEq(f64, atanh_64(0.8923), 1.433099, epsilon)); | 99 | assert(math.approxEq(f64, atanh_64(0.8923), 1.433099, epsilon)); |
| 88 | } | 100 | } |
| 101 | |||
| 102 | test "math.atanh32.special" { | ||
| 103 | assert(math.isPositiveInf(atanh_32(1))); | ||
| 104 | assert(math.isNegativeInf(atanh_32(-1))); | ||
| 105 | assert(math.isSignalNan(atanh_32(1.5))); | ||
| 106 | assert(math.isSignalNan(atanh_32(-1.5))); | ||
| 107 | assert(math.isNan(atanh_32(math.nan(f32)))); | ||
| 108 | } | ||
| 109 | |||
| 110 | test "math.atanh64.special" { | ||
| 111 | assert(math.isPositiveInf(atanh_64(1))); | ||
| 112 | assert(math.isNegativeInf(atanh_64(-1))); | ||
| 113 | assert(math.isSignalNan(atanh_64(1.5))); | ||
| 114 | assert(math.isSignalNan(atanh_64(-1.5))); | ||
| 115 | assert(math.isNan(atanh_64(math.nan(f64)))); | ||
| 116 | } |
std/math/cbrt.zig+22| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - cbrt(+-0) = +-0 | ||
| 4 | // - cbrt(+-inf) = +-inf | ||
| 5 | // - cbrt(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -135,3 +141,19 @@ test "math.cbrt64" { | ... | @@ -135,3 +141,19 @@ test "math.cbrt64" { |
| 135 | assert(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon)); | 141 | assert(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon)); |
| 136 | assert(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon)); | 142 | assert(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon)); |
| 137 | } | 143 | } |
| 144 | |||
| 145 | test "math.cbrt.special" { | ||
| 146 | assert(cbrt32(0.0) == 0.0); | ||
| 147 | assert(cbrt32(-0.0) == -0.0); | ||
| 148 | assert(math.isPositiveInf(cbrt32(math.inf(f32)))); | ||
| 149 | assert(math.isNegativeInf(cbrt32(-math.inf(f32)))); | ||
| 150 | assert(math.isNan(cbrt32(math.nan(f32)))); | ||
| 151 | } | ||
| 152 | |||
| 153 | test "math.cbrt64.special" { | ||
| 154 | assert(cbrt64(0.0) == 0.0); | ||
| 155 | assert(cbrt64(-0.0) == -0.0); | ||
| 156 | assert(math.isPositiveInf(cbrt64(math.inf(f64)))); | ||
| 157 | assert(math.isNegativeInf(cbrt64(-math.inf(f64)))); | ||
| 158 | assert(math.isNan(cbrt64(math.nan(f64)))); | ||
| 159 | } |
std/math/ceil.zig+27| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - ceil(+-0) = +-0 | ||
| 4 | // - ceil(+-inf) = +-inf | ||
| 5 | // - ceil(nan) = nan | ||
| 6 | |||
| 1 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 2 | const math = @import("index.zig"); | 8 | const math = @import("index.zig"); |
| 3 | const assert = @import("../debug.zig").assert; | 9 | const assert = @import("../debug.zig").assert; |
| ... | @@ -19,6 +25,11 @@ fn ceil32(x: f32) -> f32 { | ... | @@ -19,6 +25,11 @@ fn ceil32(x: f32) -> f32 { |
| 19 | var e = i32((u >> 23) & 0xFF) - 0x7F; | 25 | var e = i32((u >> 23) & 0xFF) - 0x7F; |
| 20 | var m: u32 = undefined; | 26 | var m: u32 = undefined; |
| 21 | 27 | ||
| 28 | // TODO: Shouldn't need this explicit check. | ||
| 29 | if (x == 0.0) { | ||
| 30 | return x; | ||
| 31 | } | ||
| 32 | |||
| 22 | if (e >= 23) { | 33 | if (e >= 23) { |
| 23 | return x; | 34 | return x; |
| 24 | } | 35 | } |
| ... | @@ -90,3 +101,19 @@ test "math.ceil64" { | ... | @@ -90,3 +101,19 @@ test "math.ceil64" { |
| 90 | assert(ceil64(-1.3) == -1.0); | 101 | assert(ceil64(-1.3) == -1.0); |
| 91 | assert(ceil64(0.2) == 1.0); | 102 | assert(ceil64(0.2) == 1.0); |
| 92 | } | 103 | } |
| 104 | |||
| 105 | test "math.ceil32.special" { | ||
| 106 | assert(ceil32(0.0) == 0.0); | ||
| 107 | assert(ceil32(-0.0) == -0.0); | ||
| 108 | assert(math.isPositiveInf(ceil32(math.inf(f32)))); | ||
| 109 | assert(math.isNegativeInf(ceil32(-math.inf(f32)))); | ||
| 110 | assert(math.isNan(ceil32(math.nan(f32)))); | ||
| 111 | } | ||
| 112 | |||
| 113 | test "math.ceil64.special" { | ||
| 114 | assert(ceil64(0.0) == 0.0); | ||
| 115 | assert(ceil64(-0.0) == -0.0); | ||
| 116 | assert(math.isPositiveInf(ceil64(math.inf(f64)))); | ||
| 117 | assert(math.isNegativeInf(ceil64(-math.inf(f64)))); | ||
| 118 | assert(math.isNan(ceil64(math.nan(f64)))); | ||
| 119 | } |
std/math/cos.zig+17-1| ... | @@ -1,7 +1,11 @@ | ... | @@ -1,7 +1,11 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - cos(+-inf) = nan | ||
| 4 | // - cos(nan) = nan | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| 4 | |||
| 5 | // TODO issue #393 | 9 | // TODO issue #393 |
| 6 | pub const cos = cos_workaround; | 10 | pub const cos = cos_workaround; |
| 7 | 11 | ||
| ... | @@ -163,3 +167,15 @@ test "math.cos64" { | ... | @@ -163,3 +167,15 @@ test "math.cos64" { |
| 163 | assert(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); | 167 | assert(math.approxEq(f64, cos64(37.45), 0.969132, epsilon)); |
| 164 | assert(math.approxEq(f64, cos64(89.123), 0.40080, epsilon)); | 168 | assert(math.approxEq(f64, cos64(89.123), 0.40080, epsilon)); |
| 165 | } | 169 | } |
| 170 | |||
| 171 | test "math.cos32.special" { | ||
| 172 | assert(math.isNan(cos32(math.inf(f32)))); | ||
| 173 | assert(math.isNan(cos32(-math.inf(f32)))); | ||
| 174 | assert(math.isNan(cos32(math.nan(f32)))); | ||
| 175 | } | ||
| 176 | |||
| 177 | test "math.cos64.special" { | ||
| 178 | assert(math.isNan(cos64(math.inf(f64)))); | ||
| 179 | assert(math.isNan(cos64(-math.inf(f64)))); | ||
| 180 | assert(math.isNan(cos64(math.nan(f64)))); | ||
| 181 | } |
std/math/cosh.zig+28-1| ... | @@ -1,5 +1,11 @@ | ... | @@ -1,5 +1,11 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - cosh(+-0) = 1 | ||
| 4 | // - cosh(+-inf) = +inf | ||
| 5 | // - cosh(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const expo2 = @import("_expo2.zig").expo2; | 8 | const expo2 = @import("expo2.zig").expo2; |
| 3 | const assert = @import("../debug.zig").assert; | 9 | const assert = @import("../debug.zig").assert; |
| 4 | 10 | ||
| 5 | // TODO issue #393 | 11 | // TODO issue #393 |
| ... | @@ -47,6 +53,11 @@ fn cosh64(x: f64) -> f64 { | ... | @@ -47,6 +53,11 @@ fn cosh64(x: f64) -> f64 { |
| 47 | const w = u32(u >> 32); | 53 | const w = u32(u >> 32); |
| 48 | const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); | 54 | const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); |
| 49 | 55 | ||
| 56 | // TODO: Shouldn't need this explicit check. | ||
| 57 | if (x == 0.0) { | ||
| 58 | return 1.0; | ||
| 59 | } | ||
| 60 | |||
| 50 | // |x| < log(2) | 61 | // |x| < log(2) |
| 51 | if (w < 0x3FE62E42) { | 62 | if (w < 0x3FE62E42) { |
| 52 | if (w < 0x3FF00000 - (26 << 20)) { | 63 | if (w < 0x3FF00000 - (26 << 20)) { |
| ... | @@ -92,3 +103,19 @@ test "math.cosh64" { | ... | @@ -92,3 +103,19 @@ test "math.cosh64" { |
| 92 | assert(math.approxEq(f64, cosh64(0.8923), 1.425225, epsilon)); | 103 | assert(math.approxEq(f64, cosh64(0.8923), 1.425225, epsilon)); |
| 93 | assert(math.approxEq(f64, cosh64(1.5), 2.352410, epsilon)); | 104 | assert(math.approxEq(f64, cosh64(1.5), 2.352410, epsilon)); |
| 94 | } | 105 | } |
| 106 | |||
| 107 | test "math.cosh32.special" { | ||
| 108 | assert(cosh32(0.0) == 1.0); | ||
| 109 | assert(cosh32(-0.0) == 1.0); | ||
| 110 | assert(math.isPositiveInf(cosh32(math.inf(f32)))); | ||
| 111 | assert(math.isPositiveInf(cosh32(-math.inf(f32)))); | ||
| 112 | assert(math.isNan(cosh32(math.nan(f32)))); | ||
| 113 | } | ||
| 114 | |||
| 115 | test "math.cosh64.special" { | ||
| 116 | assert(cosh64(0.0) == 1.0); | ||
| 117 | assert(cosh64(-0.0) == 1.0); | ||
| 118 | assert(math.isPositiveInf(cosh64(math.inf(f64)))); | ||
| 119 | assert(math.isPositiveInf(cosh64(-math.inf(f64)))); | ||
| 120 | assert(math.isNan(cosh64(math.nan(f64)))); | ||
| 121 | } |
std/math/exp.zig+16| ... | @@ -1,3 +1,8 @@ | ... | @@ -1,3 +1,8 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - exp(+inf) = +inf | ||
| 4 | // - exp(nan) = nan | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| ... | @@ -192,3 +197,14 @@ test "math.exp64" { | ... | @@ -192,3 +197,14 @@ test "math.exp64" { |
| 192 | assert(math.approxEq(f64, exp64(0.8923), 2.440737, epsilon)); | 197 | assert(math.approxEq(f64, exp64(0.8923), 2.440737, epsilon)); |
| 193 | assert(math.approxEq(f64, exp64(1.5), 4.481689, epsilon)); | 198 | assert(math.approxEq(f64, exp64(1.5), 4.481689, epsilon)); |
| 194 | } | 199 | } |
| 200 | |||
| 201 | test "math.exp32.special" { | ||
| 202 | assert(math.isPositiveInf(exp32(math.inf(f32)))); | ||
| 203 | assert(math.isNan(exp32(math.nan(f32)))); | ||
| 204 | } | ||
| 205 | |||
| 206 | test "math.exp64.special" { | ||
| 207 | // TODO: Error on release (like pow) | ||
| 208 | assert(math.isPositiveInf(exp64(math.inf(f64)))); | ||
| 209 | assert(math.isNan(exp64(math.nan(f64)))); | ||
| 210 | } |
std/math/exp2.zig+20-1| ... | @@ -1,3 +1,8 @@ | ... | @@ -1,3 +1,8 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - exp2(+inf) = +inf | ||
| 4 | // - exp2(nan) = nan | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| ... | @@ -363,6 +368,11 @@ fn exp2_64(x: f64) -> f64 { | ... | @@ -363,6 +368,11 @@ fn exp2_64(x: f64) -> f64 { |
| 363 | const ux = @bitCast(u64, x); | 368 | const ux = @bitCast(u64, x); |
| 364 | const ix = u32(ux >> 32) & 0x7FFFFFFF; | 369 | const ix = u32(ux >> 32) & 0x7FFFFFFF; |
| 365 | 370 | ||
| 371 | // TODO: This should be handled beneath. | ||
| 372 | if (math.isNan(x)) { | ||
| 373 | return math.nan(f64); | ||
| 374 | } | ||
| 375 | |||
| 366 | // |x| >= 1022 or nan | 376 | // |x| >= 1022 or nan |
| 367 | if (ix >= 0x408FF000) { | 377 | if (ix >= 0x408FF000) { |
| 368 | // x >= 1024 or nan | 378 | // x >= 1024 or nan |
| ... | @@ -432,5 +442,14 @@ test "math.exp2_64" { | ... | @@ -432,5 +442,14 @@ test "math.exp2_64" { |
| 432 | assert(math.approxEq(f64, exp2_64(0.2), 1.148698, epsilon)); | 442 | assert(math.approxEq(f64, exp2_64(0.2), 1.148698, epsilon)); |
| 433 | assert(math.approxEq(f64, exp2_64(0.8923), 1.856133, epsilon)); | 443 | assert(math.approxEq(f64, exp2_64(0.8923), 1.856133, epsilon)); |
| 434 | assert(math.approxEq(f64, exp2_64(1.5), 2.828427, epsilon)); | 444 | assert(math.approxEq(f64, exp2_64(1.5), 2.828427, epsilon)); |
| 435 | // assert(math.approxEq(f64, exp2_64(37.45), 18379273786760560.000000, epsilon)); | 445 | } |
| 446 | |||
| 447 | test "math.exp2_32.special" { | ||
| 448 | assert(math.isPositiveInf(exp2_32(math.inf(f32)))); | ||
| 449 | assert(math.isNan(exp2_32(math.nan(f32)))); | ||
| 450 | } | ||
| 451 | |||
| 452 | test "math.exp2_64.special" { | ||
| 453 | assert(math.isPositiveInf(exp2_64(math.inf(f64)))); | ||
| 454 | assert(math.isNan(exp2_64(math.nan(f64)))); | ||
| 436 | } | 455 | } |
std/math/expm1.zig+34-3| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - expm1(+inf) = +inf | ||
| 4 | // - expm1(-inf) = -1 | ||
| 5 | // - expm1(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -26,6 +32,11 @@ fn expm1_32(x_: f32) -> f32 { | ... | @@ -26,6 +32,11 @@ fn expm1_32(x_: f32) -> f32 { |
| 26 | const hx = ux & 0x7FFFFFFF; | 32 | const hx = ux & 0x7FFFFFFF; |
| 27 | const sign = hx >> 31; | 33 | const sign = hx >> 31; |
| 28 | 34 | ||
| 35 | // TODO: Shouldn't need this check explicitly. | ||
| 36 | if (math.isNegativeInf(x)) { | ||
| 37 | return -1.0; | ||
| 38 | } | ||
| 39 | |||
| 29 | // |x| >= 27 * ln2 | 40 | // |x| >= 27 * ln2 |
| 30 | if (hx >= 0x4195B844) { | 41 | if (hx >= 0x4195B844) { |
| 31 | // nan | 42 | // nan |
| ... | @@ -113,7 +124,7 @@ fn expm1_32(x_: f32) -> f32 { | ... | @@ -113,7 +124,7 @@ fn expm1_32(x_: f32) -> f32 { |
| 113 | } | 124 | } |
| 114 | } | 125 | } |
| 115 | 126 | ||
| 116 | const twopk = @bitCast(f32, u32(0x7F + k) << 23); | 127 | const twopk = @bitCast(f32, u32((0x7F + k) <<% 23)); |
| 117 | 128 | ||
| 118 | if (k < 0 or k > 56) { | 129 | if (k < 0 or k > 56) { |
| 119 | var y = x - e + 1.0; | 130 | var y = x - e + 1.0; |
| ... | @@ -150,6 +161,10 @@ fn expm1_64(x_: f64) -> f64 { | ... | @@ -150,6 +161,10 @@ fn expm1_64(x_: f64) -> f64 { |
| 150 | const hx = u32(ux >> 32) & 0x7FFFFFFF; | 161 | const hx = u32(ux >> 32) & 0x7FFFFFFF; |
| 151 | const sign = hx >> 63; | 162 | const sign = hx >> 63; |
| 152 | 163 | ||
| 164 | if (math.isNegativeInf(x)) { | ||
| 165 | return -1.0; | ||
| 166 | } | ||
| 167 | |||
| 153 | // |x| >= 56 * ln2 | 168 | // |x| >= 56 * ln2 |
| 154 | if (hx >= 0x4043687A) { | 169 | if (hx >= 0x4043687A) { |
| 155 | // exp1md(nan) = nan | 170 | // exp1md(nan) = nan |
| ... | @@ -162,7 +177,7 @@ fn expm1_64(x_: f64) -> f64 { | ... | @@ -162,7 +177,7 @@ fn expm1_64(x_: f64) -> f64 { |
| 162 | } | 177 | } |
| 163 | if (x > o_threshold) { | 178 | if (x > o_threshold) { |
| 164 | math.raiseOverflow(); | 179 | math.raiseOverflow(); |
| 165 | return math.nan(f64); | 180 | return math.inf(f64); |
| 166 | } | 181 | } |
| 167 | } | 182 | } |
| 168 | 183 | ||
| ... | @@ -238,7 +253,7 @@ fn expm1_64(x_: f64) -> f64 { | ... | @@ -238,7 +253,7 @@ fn expm1_64(x_: f64) -> f64 { |
| 238 | } | 253 | } |
| 239 | } | 254 | } |
| 240 | 255 | ||
| 241 | const twopk = @bitCast(f64, u64(0x3FF + k) << 52); | 256 | const twopk = @bitCast(f64, u64(0x3FF + k) <<% 52); |
| 242 | 257 | ||
| 243 | if (k < 0 or k > 56) { | 258 | if (k < 0 or k > 56) { |
| 244 | var y = x - e + 1.0; | 259 | var y = x - e + 1.0; |
| ... | @@ -283,3 +298,19 @@ test "math.expm1_64" { | ... | @@ -283,3 +298,19 @@ test "math.expm1_64" { |
| 283 | assert(math.approxEq(f64, expm1_64(0.8923), 1.440737, epsilon)); | 298 | assert(math.approxEq(f64, expm1_64(0.8923), 1.440737, epsilon)); |
| 284 | assert(math.approxEq(f64, expm1_64(1.5), 3.481689, epsilon)); | 299 | assert(math.approxEq(f64, expm1_64(1.5), 3.481689, epsilon)); |
| 285 | } | 300 | } |
| 301 | |||
| 302 | test "math.expm1_32.special" { | ||
| 303 | const epsilon = 0.000001; | ||
| 304 | |||
| 305 | assert(math.isPositiveInf(expm1_32(math.inf(f32)))); | ||
| 306 | assert(expm1_32(-math.inf(f32)) == -1.0); | ||
| 307 | assert(math.isNan(expm1_32(math.nan(f32)))); | ||
| 308 | } | ||
| 309 | |||
| 310 | test "math.expm1_64.special" { | ||
| 311 | const epsilon = 0.000001; | ||
| 312 | |||
| 313 | assert(math.isPositiveInf(expm1_64(math.inf(f64)))); | ||
| 314 | assert(expm1_64(-math.inf(f64)) == -1.0); | ||
| 315 | assert(math.isNan(expm1_64(math.nan(f64)))); | ||
| 316 | } |
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/fabs.zig+17| ... | @@ -1,3 +1,8 @@ | ... | @@ -1,3 +1,8 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - fabs(+-inf) = +inf | ||
| 4 | // - fabs(nan) = nan | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| ... | @@ -39,3 +44,15 @@ test "math.fabs64" { | ... | @@ -39,3 +44,15 @@ test "math.fabs64" { |
| 39 | assert(fabs64(1.0) == 1.0); | 44 | assert(fabs64(1.0) == 1.0); |
| 40 | assert(fabs64(-1.0) == 1.0); | 45 | assert(fabs64(-1.0) == 1.0); |
| 41 | } | 46 | } |
| 47 | |||
| 48 | test "math.fabs32.special" { | ||
| 49 | assert(math.isPositiveInf(fabs(math.inf(f32)))); | ||
| 50 | assert(math.isPositiveInf(fabs(-math.inf(f32)))); | ||
| 51 | assert(math.isNan(fabs(math.nan(f32)))); | ||
| 52 | } | ||
| 53 | |||
| 54 | test "math.fabs64.special" { | ||
| 55 | assert(math.isPositiveInf(fabs(math.inf(f64)))); | ||
| 56 | assert(math.isPositiveInf(fabs(-math.inf(f64)))); | ||
| 57 | assert(math.isNan(fabs(math.nan(f64)))); | ||
| 58 | } |
std/math/floor.zig+27| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - floor(+-0) = +-0 | ||
| 4 | // - floor(+-inf) = +-inf | ||
| 5 | // - floor(nan) = nan | ||
| 6 | |||
| 1 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | const math = @import("index.zig"); | 9 | const math = @import("index.zig"); |
| ... | @@ -19,6 +25,11 @@ fn floor32(x: f32) -> f32 { | ... | @@ -19,6 +25,11 @@ fn floor32(x: f32) -> f32 { |
| 19 | const e = i32((u >> 23) & 0xFF) - 0x7F; | 25 | const e = i32((u >> 23) & 0xFF) - 0x7F; |
| 20 | var m: u32 = undefined; | 26 | var m: u32 = undefined; |
| 21 | 27 | ||
| 28 | // TODO: Shouldn't need this explicit check. | ||
| 29 | if (x == 0.0) { | ||
| 30 | return x; | ||
| 31 | } | ||
| 32 | |||
| 22 | if (e >= 23) { | 33 | if (e >= 23) { |
| 23 | return x; | 34 | return x; |
| 24 | } | 35 | } |
| ... | @@ -90,3 +101,19 @@ test "math.floor64" { | ... | @@ -90,3 +101,19 @@ test "math.floor64" { |
| 90 | assert(floor64(-1.3) == -2.0); | 101 | assert(floor64(-1.3) == -2.0); |
| 91 | assert(floor64(0.2) == 0.0); | 102 | assert(floor64(0.2) == 0.0); |
| 92 | } | 103 | } |
| 104 | |||
| 105 | test "math.floor32.special" { | ||
| 106 | assert(floor32(0.0) == 0.0); | ||
| 107 | assert(floor32(-0.0) == -0.0); | ||
| 108 | assert(math.isPositiveInf(floor32(math.inf(f32)))); | ||
| 109 | assert(math.isNegativeInf(floor32(-math.inf(f32)))); | ||
| 110 | assert(math.isNan(floor32(math.nan(f32)))); | ||
| 111 | } | ||
| 112 | |||
| 113 | test "math.floor64.special" { | ||
| 114 | assert(floor64(0.0) == 0.0); | ||
| 115 | assert(floor64(-0.0) == -0.0); | ||
| 116 | assert(math.isPositiveInf(floor64(math.inf(f64)))); | ||
| 117 | assert(math.isNegativeInf(floor64(-math.inf(f64)))); | ||
| 118 | assert(math.isNan(floor64(math.nan(f64)))); | ||
| 119 | } |
std/math/frexp.zig+45| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - frexp(+-0) = +-0, 0 | ||
| 4 | // - frexp(+-inf) = +-inf, 0 | ||
| 5 | // - frexp(nan) = nan, 0 | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -114,3 +120,42 @@ test "math.frexp64" { | ... | @@ -114,3 +120,42 @@ test "math.frexp64" { |
| 114 | r = frexp64(78.0234); | 120 | r = frexp64(78.0234); |
| 115 | assert(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7); | 121 | assert(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7); |
| 116 | } | 122 | } |
| 123 | |||
| 124 | test "math.frexp32.special" { | ||
| 125 | var r: frexp32_result = undefined; | ||
| 126 | |||
| 127 | r = frexp32(0.0); | ||
| 128 | assert(r.significand == 0.0 and r.exponent == 0); | ||
| 129 | |||
| 130 | r = frexp32(-0.0); | ||
| 131 | assert(r.significand == -0.0 and r.exponent == 0); | ||
| 132 | |||
| 133 | r = frexp32(math.inf(f32)); | ||
| 134 | assert(math.isPositiveInf(r.significand) and r.exponent == 0); | ||
| 135 | |||
| 136 | r = frexp32(-math.inf(f32)); | ||
| 137 | assert(math.isNegativeInf(r.significand) and r.exponent == 0); | ||
| 138 | |||
| 139 | r = frexp32(math.nan(f32)); | ||
| 140 | assert(math.isNan(r.significand) and r.exponent == 0); | ||
| 141 | } | ||
| 142 | |||
| 143 | test "math.frexp64.special" { | ||
| 144 | // TODO: Error on release mode (like pow) | ||
| 145 | var r: frexp64_result = undefined; | ||
| 146 | |||
| 147 | r = frexp64(0.0); | ||
| 148 | assert(r.significand == 0.0 and r.exponent == 0); | ||
| 149 | |||
| 150 | r = frexp64(-0.0); | ||
| 151 | assert(r.significand == -0.0 and r.exponent == 0); | ||
| 152 | |||
| 153 | r = frexp64(math.inf(f64)); | ||
| 154 | assert(math.isPositiveInf(r.significand) and r.exponent == 0); | ||
| 155 | |||
| 156 | r = frexp64(-math.inf(f64)); | ||
| 157 | assert(math.isNegativeInf(r.significand) and r.exponent == 0); | ||
| 158 | |||
| 159 | r = frexp64(math.nan(f64)); | ||
| 160 | assert(math.isNan(r.significand) and r.exponent == 0); | ||
| 161 | } |
std/math/hypot.zig+25| ... | @@ -1,3 +1,10 @@ | ... | @@ -1,3 +1,10 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - hypot(+-inf, y) = +inf | ||
| 4 | // - hypot(x, +-inf) = +inf | ||
| 5 | // - hypot(nan, y) = nan | ||
| 6 | // - hypot(x, nan) = nan | ||
| 7 | |||
| 1 | const math = @import("index.zig"); | 8 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 9 | const assert = @import("../debug.zig").assert; |
| 3 | 10 | ||
| ... | @@ -136,3 +143,21 @@ test "math.hypot64" { | ... | @@ -136,3 +143,21 @@ test "math.hypot64" { |
| 136 | assert(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon)); | 143 | assert(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon)); |
| 137 | assert(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon)); | 144 | assert(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon)); |
| 138 | } | 145 | } |
| 146 | |||
| 147 | test "math.hypot32.special" { | ||
| 148 | assert(math.isPositiveInf(hypot32(math.inf(f32), 0.0))); | ||
| 149 | assert(math.isPositiveInf(hypot32(-math.inf(f32), 0.0))); | ||
| 150 | assert(math.isPositiveInf(hypot32(0.0, math.inf(f32)))); | ||
| 151 | assert(math.isPositiveInf(hypot32(0.0, -math.inf(f32)))); | ||
| 152 | assert(math.isNan(hypot32(math.nan(f32), 0.0))); | ||
| 153 | assert(math.isNan(hypot32(0.0, math.nan(f32)))); | ||
| 154 | } | ||
| 155 | |||
| 156 | test "math.hypot64.special" { | ||
| 157 | assert(math.isPositiveInf(hypot64(math.inf(f64), 0.0))); | ||
| 158 | assert(math.isPositiveInf(hypot64(-math.inf(f64), 0.0))); | ||
| 159 | assert(math.isPositiveInf(hypot64(0.0, math.inf(f64)))); | ||
| 160 | assert(math.isPositiveInf(hypot64(0.0, -math.inf(f64)))); | ||
| 161 | assert(math.isNan(hypot64(math.nan(f64), 0.0))); | ||
| 162 | assert(math.isNan(hypot64(0.0, math.nan(f64)))); | ||
| 163 | } |
std/math/ilogb.zig+31-2| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - ilogb(+-inf) = @maxValue(i32) | ||
| 4 | // - ilogb(0) = @maxValue(i32) | ||
| 5 | // - ilogb(nan) = @maxValue(i32) | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -21,6 +27,11 @@ fn ilogb32(x: f32) -> i32 { | ... | @@ -21,6 +27,11 @@ fn ilogb32(x: f32) -> i32 { |
| 21 | var u = @bitCast(u32, x); | 27 | var u = @bitCast(u32, x); |
| 22 | var e = i32((u >> 23) & 0xFF); | 28 | var e = i32((u >> 23) & 0xFF); |
| 23 | 29 | ||
| 30 | // TODO: We should be able to merge this with the lower check. | ||
| 31 | if (math.isNan(x)) { | ||
| 32 | return @maxValue(i32); | ||
| 33 | } | ||
| 34 | |||
| 24 | if (e == 0) { | 35 | if (e == 0) { |
| 25 | u <<= 9; | 36 | u <<= 9; |
| 26 | if (u == 0) { | 37 | if (u == 0) { |
| ... | @@ -38,7 +49,7 @@ fn ilogb32(x: f32) -> i32 { | ... | @@ -38,7 +49,7 @@ fn ilogb32(x: f32) -> i32 { |
| 38 | 49 | ||
| 39 | if (e == 0xFF) { | 50 | if (e == 0xFF) { |
| 40 | math.raiseInvalid(); | 51 | math.raiseInvalid(); |
| 41 | if (u << 9 != 0) { | 52 | if (u <<% 9 != 0) { |
| 42 | return fp_ilogbnan; | 53 | return fp_ilogbnan; |
| 43 | } else { | 54 | } else { |
| 44 | return @maxValue(i32); | 55 | return @maxValue(i32); |
| ... | @@ -52,6 +63,10 @@ fn ilogb64(x: f64) -> i32 { | ... | @@ -52,6 +63,10 @@ fn ilogb64(x: f64) -> i32 { |
| 52 | var u = @bitCast(u64, x); | 63 | var u = @bitCast(u64, x); |
| 53 | var e = i32((u >> 52) & 0x7FF); | 64 | var e = i32((u >> 52) & 0x7FF); |
| 54 | 65 | ||
| 66 | if (math.isNan(x)) { | ||
| 67 | return @maxValue(i32); | ||
| 68 | } | ||
| 69 | |||
| 55 | if (e == 0) { | 70 | if (e == 0) { |
| 56 | u <<= 12; | 71 | u <<= 12; |
| 57 | if (u == 0) { | 72 | if (u == 0) { |
| ... | @@ -69,7 +84,7 @@ fn ilogb64(x: f64) -> i32 { | ... | @@ -69,7 +84,7 @@ fn ilogb64(x: f64) -> i32 { |
| 69 | 84 | ||
| 70 | if (e == 0x7FF) { | 85 | if (e == 0x7FF) { |
| 71 | math.raiseInvalid(); | 86 | math.raiseInvalid(); |
| 72 | if (u << 12 != 0) { | 87 | if (u <<% 12 != 0) { |
| 73 | return fp_ilogbnan; | 88 | return fp_ilogbnan; |
| 74 | } else { | 89 | } else { |
| 75 | return @maxValue(i32); | 90 | return @maxValue(i32); |
| ... | @@ -101,3 +116,17 @@ test "math.ilogb64" { | ... | @@ -101,3 +116,17 @@ test "math.ilogb64" { |
| 101 | assert(ilogb64(-123984) == 16); | 116 | assert(ilogb64(-123984) == 16); |
| 102 | assert(ilogb64(2398.23) == 11); | 117 | assert(ilogb64(2398.23) == 11); |
| 103 | } | 118 | } |
| 119 | |||
| 120 | test "math.ilogb32.special" { | ||
| 121 | assert(ilogb32(math.inf(f32)) == @maxValue(i32)); | ||
| 122 | assert(ilogb32(-math.inf(f32)) == @maxValue(i32)); | ||
| 123 | assert(ilogb32(0.0) == @minValue(i32)); | ||
| 124 | assert(ilogb32(math.nan(f32)) == @maxValue(i32)); | ||
| 125 | } | ||
| 126 | |||
| 127 | test "math.ilogb64.special" { | ||
| 128 | assert(ilogb64(math.inf(f64)) == @maxValue(i32)); | ||
| 129 | assert(ilogb64(-math.inf(f64)) == @maxValue(i32)); | ||
| 130 | assert(ilogb64(0.0) == @minValue(i32)); | ||
| 131 | assert(ilogb64(math.nan(f64)) == @maxValue(i32)); | ||
| 132 | } |
std/math/index.zig+2| ... | @@ -42,6 +42,7 @@ pub const inf_u64 = u64(0x7FF << 52); | ... | @@ -42,6 +42,7 @@ pub const inf_u64 = u64(0x7FF << 52); |
| 42 | pub const inf_f64 = @bitCast(f64, inf_u64); | 42 | pub const inf_f64 = @bitCast(f64, inf_u64); |
| 43 | 43 | ||
| 44 | pub const nan = @import("nan.zig").nan; | 44 | pub const nan = @import("nan.zig").nan; |
| 45 | pub const snan = @import("nan.zig").snan; | ||
| 45 | pub const inf = @import("inf.zig").inf; | 46 | pub const inf = @import("inf.zig").inf; |
| 46 | 47 | ||
| 47 | pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool { | 48 | pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool { |
| ... | @@ -90,6 +91,7 @@ pub fn raiseDivByZero() { | ... | @@ -90,6 +91,7 @@ pub fn raiseDivByZero() { |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 92 | pub const isNan = @import("isnan.zig").isNan; | 93 | pub const isNan = @import("isnan.zig").isNan; |
| 94 | pub const isSignalNan = @import("isnan.zig").isSignalNan; | ||
| 93 | pub const fabs = @import("fabs.zig").fabs; | 95 | pub const fabs = @import("fabs.zig").fabs; |
| 94 | pub const ceil = @import("ceil.zig").ceil; | 96 | pub const ceil = @import("ceil.zig").ceil; |
| 95 | pub const floor = @import("floor.zig").floor; | 97 | pub const floor = @import("floor.zig").floor; |
std/math/isnan.zig+6| ... | @@ -18,6 +18,12 @@ pub fn isNan(x: var) -> bool { | ... | @@ -18,6 +18,12 @@ pub fn isNan(x: var) -> bool { |
| 18 | } | 18 | } |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | // Note: A signalling nan is identical to a standard right now by may have a different bit | ||
| 22 | // representation in the future when required. | ||
| 23 | pub fn isSignalNan(x: var) -> bool { | ||
| 24 | isNan(x) | ||
| 25 | } | ||
| 26 | |||
| 21 | test "math.isNan" { | 27 | test "math.isNan" { |
| 22 | assert(isNan(math.nan(f32))); | 28 | assert(isNan(math.nan(f32))); |
| 23 | assert(isNan(math.nan(f64))); | 29 | assert(isNan(math.nan(f64))); |
std/math/ln.zig+27-6| ... | @@ -1,3 +1,10 @@ | ... | @@ -1,3 +1,10 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - ln(+inf) = +inf | ||
| 4 | // - ln(0) = -inf | ||
| 5 | // - ln(x) = nan if x < 0 | ||
| 6 | // - ln(nan) = nan | ||
| 7 | |||
| 1 | const math = @import("index.zig"); | 8 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 9 | const assert = @import("../debug.zig").assert; |
| 3 | 10 | ||
| ... | @@ -27,12 +34,12 @@ fn lnf(x_: f32) -> f32 { | ... | @@ -27,12 +34,12 @@ fn lnf(x_: f32) -> f32 { |
| 27 | // x < 2^(-126) | 34 | // x < 2^(-126) |
| 28 | if (ix < 0x00800000 or ix >> 31 != 0) { | 35 | if (ix < 0x00800000 or ix >> 31 != 0) { |
| 29 | // log(+-0) = -inf | 36 | // log(+-0) = -inf |
| 30 | if (ix << 1 == 0) { | 37 | if (ix <<% 1 == 0) { |
| 31 | return -1 / (x * x); | 38 | return -math.inf(f32); |
| 32 | } | 39 | } |
| 33 | // log(-#) = nan | 40 | // log(-#) = nan |
| 34 | if (ix >> 31 != 0) { | 41 | if (ix >> 31 != 0) { |
| 35 | return (x - x) / 0.0 | 42 | return math.nan(f32); |
| 36 | } | 43 | } |
| 37 | 44 | ||
| 38 | // subnormal, scale x | 45 | // subnormal, scale x |
| ... | @@ -82,12 +89,12 @@ fn lnd(x_: f64) -> f64 { | ... | @@ -82,12 +89,12 @@ fn lnd(x_: f64) -> f64 { |
| 82 | 89 | ||
| 83 | if (hx < 0x00100000 or hx >> 31 != 0) { | 90 | if (hx < 0x00100000 or hx >> 31 != 0) { |
| 84 | // log(+-0) = -inf | 91 | // log(+-0) = -inf |
| 85 | if (ix << 1 == 0) { | 92 | if (ix <<% 1 == 0) { |
| 86 | return -1 / (x * x); | 93 | return -math.inf(f64); |
| 87 | } | 94 | } |
| 88 | // log(-#) = nan | 95 | // log(-#) = nan |
| 89 | if (hx >> 31 != 0) { | 96 | if (hx >> 31 != 0) { |
| 90 | return (x - x) / 0.0; | 97 | return math.nan(f64); |
| 91 | } | 98 | } |
| 92 | 99 | ||
| 93 | // subnormal, scale x | 100 | // subnormal, scale x |
| ... | @@ -148,3 +155,17 @@ test "math.ln64" { | ... | @@ -148,3 +155,17 @@ test "math.ln64" { |
| 148 | assert(math.approxEq(f64, lnd(89.123), 4.490017, epsilon)); | 155 | assert(math.approxEq(f64, lnd(89.123), 4.490017, epsilon)); |
| 149 | assert(math.approxEq(f64, lnd(123123.234375), 11.720941, epsilon)); | 156 | assert(math.approxEq(f64, lnd(123123.234375), 11.720941, epsilon)); |
| 150 | } | 157 | } |
| 158 | |||
| 159 | test "math.ln32.special" { | ||
| 160 | assert(math.isPositiveInf(lnf(math.inf(f32)))); | ||
| 161 | assert(math.isNegativeInf(lnf(0.0))); | ||
| 162 | assert(math.isNan(lnf(-1.0))); | ||
| 163 | assert(math.isNan(lnf(math.nan(f32)))); | ||
| 164 | } | ||
| 165 | |||
| 166 | test "math.ln64.special" { | ||
| 167 | assert(math.isPositiveInf(lnd(math.inf(f64)))); | ||
| 168 | assert(math.isNegativeInf(lnd(0.0))); | ||
| 169 | assert(math.isNan(lnd(-1.0))); | ||
| 170 | assert(math.isNan(lnd(math.nan(f64)))); | ||
| 171 | } |
std/math/log10.zig+27-6| ... | @@ -1,3 +1,10 @@ | ... | @@ -1,3 +1,10 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - log10(+inf) = +inf | ||
| 4 | // - log10(0) = -inf | ||
| 5 | // - log10(x) = nan if x < 0 | ||
| 6 | // - log10(nan) = nan | ||
| 7 | |||
| 1 | const math = @import("index.zig"); | 8 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 9 | const assert = @import("../debug.zig").assert; |
| 3 | 10 | ||
| ... | @@ -31,12 +38,12 @@ fn log10_32(x_: f32) -> f32 { | ... | @@ -31,12 +38,12 @@ fn log10_32(x_: f32) -> f32 { |
| 31 | // x < 2^(-126) | 38 | // x < 2^(-126) |
| 32 | if (ix < 0x00800000 or ix >> 31 != 0) { | 39 | if (ix < 0x00800000 or ix >> 31 != 0) { |
| 33 | // log(+-0) = -inf | 40 | // log(+-0) = -inf |
| 34 | if (ix << 1 == 0) { | 41 | if (ix <<% 1 == 0) { |
| 35 | return -1 / (x * x); | 42 | return -math.inf(f32); |
| 36 | } | 43 | } |
| 37 | // log(-#) = nan | 44 | // log(-#) = nan |
| 38 | if (ix >> 31 != 0) { | 45 | if (ix >> 31 != 0) { |
| 39 | return (x - x) / 0.0 | 46 | return math.nan(f32); |
| 40 | } | 47 | } |
| 41 | 48 | ||
| 42 | k -= 25; | 49 | k -= 25; |
| ... | @@ -93,12 +100,12 @@ fn log10_64(x_: f64) -> f64 { | ... | @@ -93,12 +100,12 @@ fn log10_64(x_: f64) -> f64 { |
| 93 | 100 | ||
| 94 | if (hx < 0x00100000 or hx >> 31 != 0) { | 101 | if (hx < 0x00100000 or hx >> 31 != 0) { |
| 95 | // log(+-0) = -inf | 102 | // log(+-0) = -inf |
| 96 | if (ix << 1 == 0) { | 103 | if (ix <<% 1 == 0) { |
| 97 | return -1 / (x * x); | 104 | return -math.inf(f32); |
| 98 | } | 105 | } |
| 99 | // log(-#) = nan | 106 | // log(-#) = nan |
| 100 | if (hx >> 31 != 0) { | 107 | if (hx >> 31 != 0) { |
| 101 | return (x - x) / 0.0; | 108 | return math.nan(f32); |
| 102 | } | 109 | } |
| 103 | 110 | ||
| 104 | // subnormal, scale x | 111 | // subnormal, scale x |
| ... | @@ -176,3 +183,17 @@ test "math.log10_64" { | ... | @@ -176,3 +183,17 @@ test "math.log10_64" { |
| 176 | assert(math.approxEq(f64, log10_64(89.123), 1.94999, epsilon)); | 183 | assert(math.approxEq(f64, log10_64(89.123), 1.94999, epsilon)); |
| 177 | assert(math.approxEq(f64, log10_64(123123.234375), 5.09034, epsilon)); | 184 | assert(math.approxEq(f64, log10_64(123123.234375), 5.09034, epsilon)); |
| 178 | } | 185 | } |
| 186 | |||
| 187 | test "math.log10_32.special" { | ||
| 188 | assert(math.isPositiveInf(log10_32(math.inf(f32)))); | ||
| 189 | assert(math.isNegativeInf(log10_32(0.0))); | ||
| 190 | assert(math.isNan(log10_32(-1.0))); | ||
| 191 | assert(math.isNan(log10_32(math.nan(f32)))); | ||
| 192 | } | ||
| 193 | |||
| 194 | test "math.log10_64.special" { | ||
| 195 | assert(math.isPositiveInf(log10_64(math.inf(f64)))); | ||
| 196 | assert(math.isNegativeInf(log10_64(0.0))); | ||
| 197 | assert(math.isNan(log10_64(-1.0))); | ||
| 198 | assert(math.isNan(log10_64(math.nan(f64)))); | ||
| 199 | } |
std/math/log1p.zig+35-9| ... | @@ -1,3 +1,11 @@ | ... | @@ -1,3 +1,11 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - log1p(+inf) = +inf | ||
| 4 | // - log1p(+-0) = +-0 | ||
| 5 | // - log1p(-1) = -inf | ||
| 6 | // - log1p(x) = nan if x < -1 | ||
| 7 | // - log1p(nan) = nan | ||
| 8 | |||
| 1 | const math = @import("index.zig"); | 9 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 10 | const assert = @import("../debug.zig").assert; |
| 3 | 11 | ||
| ... | @@ -31,17 +39,17 @@ fn log1p_32(x: f32) -> f32 { | ... | @@ -31,17 +39,17 @@ fn log1p_32(x: f32) -> f32 { |
| 31 | if (ix < 0x3ED413D0 or ix >> 31 != 0) { | 39 | if (ix < 0x3ED413D0 or ix >> 31 != 0) { |
| 32 | // x <= -1.0 | 40 | // x <= -1.0 |
| 33 | if (ix >= 0xBF800000) { | 41 | if (ix >= 0xBF800000) { |
| 34 | // log1p(-1) = +inf | 42 | // log1p(-1) = -inf |
| 35 | if (x == -1) { | 43 | if (x == -1.0) { |
| 36 | return x / 0.0; | 44 | return -math.inf(f32); |
| 37 | } | 45 | } |
| 38 | // log1p(x < -1) = nan | 46 | // log1p(x < -1) = nan |
| 39 | else { | 47 | else { |
| 40 | return (x - x) / 0.0; | 48 | return math.nan(f32); |
| 41 | } | 49 | } |
| 42 | } | 50 | } |
| 43 | // |x| < 2^(-24) | 51 | // |x| < 2^(-24) |
| 44 | if ((ix << 1) < (0x33800000 << 1)) { | 52 | if ((ix <<% 1) < (0x33800000 << 1)) { |
| 45 | // underflow if subnormal | 53 | // underflow if subnormal |
| 46 | if (ix & 0x7F800000 == 0) { | 54 | if (ix & 0x7F800000 == 0) { |
| 47 | math.forceEval(x * x); | 55 | math.forceEval(x * x); |
| ... | @@ -111,16 +119,16 @@ fn log1p_64(x: f64) -> f64 { | ... | @@ -111,16 +119,16 @@ fn log1p_64(x: f64) -> f64 { |
| 111 | // x <= -1.0 | 119 | // x <= -1.0 |
| 112 | if (hx >= 0xBFF00000) { | 120 | if (hx >= 0xBFF00000) { |
| 113 | // log1p(-1) = -inf | 121 | // log1p(-1) = -inf |
| 114 | if (x == 1) { | 122 | if (x == -1.0) { |
| 115 | return x / 0.0; | 123 | return -math.inf(f64); |
| 116 | } | 124 | } |
| 117 | // log1p(x < -1) = nan | 125 | // log1p(x < -1) = nan |
| 118 | else { | 126 | else { |
| 119 | return (x - x) / 0.0; | 127 | return math.nan(f64); |
| 120 | } | 128 | } |
| 121 | } | 129 | } |
| 122 | // |x| < 2^(-53) | 130 | // |x| < 2^(-53) |
| 123 | if ((hx << 1) < (0x3CA00000 << 1)) { | 131 | if ((hx <<% 1) < (0x3CA00000 << 1)) { |
| 124 | if ((hx & 0x7FF00000) == 0) { | 132 | if ((hx & 0x7FF00000) == 0) { |
| 125 | math.raiseUnderflow(); | 133 | math.raiseUnderflow(); |
| 126 | } | 134 | } |
| ... | @@ -198,3 +206,21 @@ test "math.log1p_64" { | ... | @@ -198,3 +206,21 @@ test "math.log1p_64" { |
| 198 | assert(math.approxEq(f64, log1p_64(89.123), 4.501175, epsilon)); | 206 | assert(math.approxEq(f64, log1p_64(89.123), 4.501175, epsilon)); |
| 199 | assert(math.approxEq(f64, log1p_64(123123.234375), 11.720949, epsilon)); | 207 | assert(math.approxEq(f64, log1p_64(123123.234375), 11.720949, epsilon)); |
| 200 | } | 208 | } |
| 209 | |||
| 210 | test "math.log1p_32.special" { | ||
| 211 | assert(math.isPositiveInf(log1p_32(math.inf(f32)))); | ||
| 212 | assert(log1p_32(0.0) == 0.0); | ||
| 213 | assert(log1p_32(-0.0) == -0.0); | ||
| 214 | assert(math.isNegativeInf(log1p_32(-1.0))); | ||
| 215 | assert(math.isNan(log1p_32(-2.0))); | ||
| 216 | assert(math.isNan(log1p_32(math.nan(f32)))); | ||
| 217 | } | ||
| 218 | |||
| 219 | test "math.log1p_64.special" { | ||
| 220 | assert(math.isPositiveInf(log1p_64(math.inf(f64)))); | ||
| 221 | assert(log1p_64(0.0) == 0.0); | ||
| 222 | assert(log1p_64(-0.0) == -0.0); | ||
| 223 | assert(math.isNegativeInf(log1p_64(-1.0))); | ||
| 224 | assert(math.isNan(log1p_64(-2.0))); | ||
| 225 | assert(math.isNan(log1p_64(math.nan(f64)))); | ||
| 226 | } |
std/math/log2.zig+27-6| ... | @@ -1,3 +1,10 @@ | ... | @@ -1,3 +1,10 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - log2(+inf) = +inf | ||
| 4 | // - log2(0) = -inf | ||
| 5 | // - log2(x) = nan if x < 0 | ||
| 6 | // - log2(nan) = nan | ||
| 7 | |||
| 1 | const math = @import("index.zig"); | 8 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 9 | const assert = @import("../debug.zig").assert; |
| 3 | 10 | ||
| ... | @@ -29,12 +36,12 @@ fn log2_32(x_: f32) -> f32 { | ... | @@ -29,12 +36,12 @@ fn log2_32(x_: f32) -> f32 { |
| 29 | // x < 2^(-126) | 36 | // x < 2^(-126) |
| 30 | if (ix < 0x00800000 or ix >> 31 != 0) { | 37 | if (ix < 0x00800000 or ix >> 31 != 0) { |
| 31 | // log(+-0) = -inf | 38 | // log(+-0) = -inf |
| 32 | if (ix << 1 == 0) { | 39 | if (ix <<% 1 == 0) { |
| 33 | return -1 / (x * x); | 40 | return -math.inf(f32); |
| 34 | } | 41 | } |
| 35 | // log(-#) = nan | 42 | // log(-#) = nan |
| 36 | if (ix >> 31 != 0) { | 43 | if (ix >> 31 != 0) { |
| 37 | return (x - x) / 0.0 | 44 | return math.nan(f32); |
| 38 | } | 45 | } |
| 39 | 46 | ||
| 40 | k -= 25; | 47 | k -= 25; |
| ... | @@ -87,12 +94,12 @@ fn log2_64(x_: f64) -> f64 { | ... | @@ -87,12 +94,12 @@ fn log2_64(x_: f64) -> f64 { |
| 87 | 94 | ||
| 88 | if (hx < 0x00100000 or hx >> 31 != 0) { | 95 | if (hx < 0x00100000 or hx >> 31 != 0) { |
| 89 | // log(+-0) = -inf | 96 | // log(+-0) = -inf |
| 90 | if (ix << 1 == 0) { | 97 | if (ix <<% 1 == 0) { |
| 91 | return -1 / (x * x); | 98 | return -math.inf(f64); |
| 92 | } | 99 | } |
| 93 | // log(-#) = nan | 100 | // log(-#) = nan |
| 94 | if (hx >> 31 != 0) { | 101 | if (hx >> 31 != 0) { |
| 95 | return (x - x) / 0.0; | 102 | return math.nan(f64); |
| 96 | } | 103 | } |
| 97 | 104 | ||
| 98 | // subnormal, scale x | 105 | // subnormal, scale x |
| ... | @@ -166,3 +173,17 @@ test "math.log2_64" { | ... | @@ -166,3 +173,17 @@ test "math.log2_64" { |
| 166 | assert(math.approxEq(f64, log2_64(37.45), 5.226894, epsilon)); | 173 | assert(math.approxEq(f64, log2_64(37.45), 5.226894, epsilon)); |
| 167 | assert(math.approxEq(f64, log2_64(123123.234375), 16.909744, epsilon)); | 174 | assert(math.approxEq(f64, log2_64(123123.234375), 16.909744, epsilon)); |
| 168 | } | 175 | } |
| 176 | |||
| 177 | test "math.log2_32.special" { | ||
| 178 | assert(math.isPositiveInf(log2_32(math.inf(f32)))); | ||
| 179 | assert(math.isNegativeInf(log2_32(0.0))); | ||
| 180 | assert(math.isNan(log2_32(-1.0))); | ||
| 181 | assert(math.isNan(log2_32(math.nan(f32)))); | ||
| 182 | } | ||
| 183 | |||
| 184 | test "math.log2_64.special" { | ||
| 185 | assert(math.isPositiveInf(log2_64(math.inf(f64)))); | ||
| 186 | assert(math.isNegativeInf(log2_64(0.0))); | ||
| 187 | assert(math.isNan(log2_64(-1.0))); | ||
| 188 | assert(math.isNan(log2_64(math.nan(f64)))); | ||
| 189 | } |
std/math/modf.zig+46-2| ... | @@ -1,3 +1,8 @@ | ... | @@ -1,3 +1,8 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - modf(+-inf) = +-inf, nan | ||
| 4 | // - modf(nan) = nan, nan | ||
| 5 | |||
| 1 | const math = @import("index.zig"); | 6 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 7 | const assert = @import("../debug.zig").assert; |
| 3 | 8 | ||
| ... | @@ -29,10 +34,17 @@ fn modf32(x: f32) -> modf32_result { | ... | @@ -29,10 +34,17 @@ fn modf32(x: f32) -> modf32_result { |
| 29 | const e = i32((u >> 23) & 0xFF) - 0x7F; | 34 | const e = i32((u >> 23) & 0xFF) - 0x7F; |
| 30 | const us = u & 0x80000000; | 35 | const us = u & 0x80000000; |
| 31 | 36 | ||
| 37 | // TODO: Shouldn't need this. | ||
| 38 | if (math.isInf(x)) { | ||
| 39 | result.ipart = x; | ||
| 40 | result.fpart = math.nan(f32); | ||
| 41 | return result; | ||
| 42 | } | ||
| 43 | |||
| 32 | // no fractional part | 44 | // no fractional part |
| 33 | if (e >= 23) { | 45 | if (e >= 23) { |
| 34 | result.ipart = x; | 46 | result.ipart = x; |
| 35 | if (e == 0x80 and u << 9 != 0) { // nan | 47 | if (e == 0x80 and u <<% 9 != 0) { // nan |
| 36 | result.fpart = x; | 48 | result.fpart = x; |
| 37 | } else { | 49 | } else { |
| 38 | result.fpart = @bitCast(f32, us); | 50 | result.fpart = @bitCast(f32, us); |
| ... | @@ -67,10 +79,16 @@ fn modf64(x: f64) -> modf64_result { | ... | @@ -67,10 +79,16 @@ fn modf64(x: f64) -> modf64_result { |
| 67 | const e = i32((u >> 52) & 0x7FF) - 0x3FF; | 79 | const e = i32((u >> 52) & 0x7FF) - 0x3FF; |
| 68 | const us = u & (1 << 63); | 80 | const us = u & (1 << 63); |
| 69 | 81 | ||
| 82 | if (math.isInf(x)) { | ||
| 83 | result.ipart = x; | ||
| 84 | result.fpart = math.nan(f64); | ||
| 85 | return result; | ||
| 86 | } | ||
| 87 | |||
| 70 | // no fractional part | 88 | // no fractional part |
| 71 | if (e >= 52) { | 89 | if (e >= 52) { |
| 72 | result.ipart = x; | 90 | result.ipart = x; |
| 73 | if (e == 0x400 and u << 12 != 0) { // nan | 91 | if (e == 0x400 and u <<% 12 != 0) { // nan |
| 74 | result.fpart = x; | 92 | result.fpart = x; |
| 75 | } else { | 93 | } else { |
| 76 | result.fpart = @bitCast(f64, us); | 94 | result.fpart = @bitCast(f64, us); |
| ... | @@ -158,3 +176,29 @@ test "math.modf64" { | ... | @@ -158,3 +176,29 @@ test "math.modf64" { |
| 158 | assert(math.approxEq(f64, r.ipart, 1234, epsilon)); | 176 | assert(math.approxEq(f64, r.ipart, 1234, epsilon)); |
| 159 | assert(math.approxEq(f64, r.fpart, 0.340780, epsilon)); | 177 | assert(math.approxEq(f64, r.fpart, 0.340780, epsilon)); |
| 160 | } | 178 | } |
| 179 | |||
| 180 | test "math.modf32.special" { | ||
| 181 | var r: modf32_result = undefined; | ||
| 182 | |||
| 183 | r = modf32(math.inf(f32)); | ||
| 184 | assert(math.isPositiveInf(r.ipart) and math.isNan(r.fpart)); | ||
| 185 | |||
| 186 | r = modf32(-math.inf(f32)); | ||
| 187 | assert(math.isNegativeInf(r.ipart) and math.isNan(r.fpart)); | ||
| 188 | |||
| 189 | r = modf32(math.nan(f32)); | ||
| 190 | assert(math.isNan(r.ipart) and math.isNan(r.fpart)); | ||
| 191 | } | ||
| 192 | |||
| 193 | test "math.modf64.special" { | ||
| 194 | var r: modf64_result = undefined; | ||
| 195 | |||
| 196 | r = modf64(math.inf(f64)); | ||
| 197 | assert(math.isPositiveInf(r.ipart) and math.isNan(r.fpart)); | ||
| 198 | |||
| 199 | r = modf64(-math.inf(f64)); | ||
| 200 | assert(math.isNegativeInf(r.ipart) and math.isNan(r.fpart)); | ||
| 201 | |||
| 202 | r = modf64(math.nan(f64)); | ||
| 203 | assert(math.isNan(r.ipart) and math.isNan(r.fpart)); | ||
| 204 | } |
std/math/nan.zig+12| ... | @@ -9,3 +9,15 @@ pub fn nan_workaround(comptime T: type) -> T { | ... | @@ -9,3 +9,15 @@ pub fn nan_workaround(comptime T: type) -> T { |
| 9 | else => @compileError("nan not implemented for " ++ @typeName(T)), | 9 | else => @compileError("nan not implemented for " ++ @typeName(T)), |
| 10 | } | 10 | } |
| 11 | } | 11 | } |
| 12 | |||
| 13 | pub const snan = snan_workaround; | ||
| 14 | |||
| 15 | // Note: A signalling nan is identical to a standard right now by may have a different bit | ||
| 16 | // representation in the future when required. | ||
| 17 | pub fn snan_workaround(comptime T: type) -> T { | ||
| 18 | switch (T) { | ||
| 19 | f32 => @bitCast(f32, math.nan_u32), | ||
| 20 | f64 => @bitCast(f64, math.nan_u64), | ||
| 21 | else => @compileError("snan not implemented for " ++ @typeName(T)), | ||
| 22 | } | ||
| 23 | } |
std/math/pow.zig+67-4| ... | @@ -1,3 +1,26 @@ | ... | @@ -1,3 +1,26 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 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 | ||
| 23 | |||
| 1 | const math = @import("index.zig"); | 24 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 25 | const assert = @import("../debug.zig").assert; |
| 3 | 26 | ||
| ... | @@ -59,9 +82,9 @@ pub fn pow_workaround(comptime T: type, x: T, y: T) -> T { | ... | @@ -59,9 +82,9 @@ pub fn pow_workaround(comptime T: type, x: T, y: T) -> T { |
| 59 | } | 82 | } |
| 60 | 83 | ||
| 61 | if (math.isInf(y)) { | 84 | if (math.isInf(y)) { |
| 62 | // pow(-1, inf) = -1 for all x | 85 | // pow(-1, inf) = 1 for all x |
| 63 | if (x == -1) { | 86 | if (x == -1) { |
| 64 | return -1; | 87 | return 1.0; |
| 65 | } | 88 | } |
| 66 | // pow(x, +inf) = +0 for |x| < 1 | 89 | // pow(x, +inf) = +0 for |x| < 1 |
| 67 | // pow(x, -inf) = +0 for |x| > 1 | 90 | // pow(x, -inf) = +0 for |x| > 1 |
| ... | @@ -156,17 +179,57 @@ fn isOddInteger(x: f64) -> bool { | ... | @@ -156,17 +179,57 @@ fn isOddInteger(x: f64) -> bool { |
| 156 | test "math.pow" { | 179 | test "math.pow" { |
| 157 | const epsilon = 0.000001; | 180 | const epsilon = 0.000001; |
| 158 | 181 | ||
| 159 | // assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero | 182 | // TODO: Error on release |
| 183 | assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); | ||
| 160 | assert(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon)); | 184 | assert(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon)); |
| 161 | assert(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon)); | 185 | assert(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon)); |
| 162 | assert(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon)); | 186 | assert(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon)); |
| 163 | assert(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon)); | 187 | assert(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon)); |
| 164 | assert(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon)); | 188 | assert(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon)); |
| 165 | 189 | ||
| 166 | // assert(math.approxEq(f32, pow(f64, 0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero | 190 | assert(math.approxEq(f64, pow(f64, 0.0, 3.3), 0.0, epsilon)); |
| 167 | assert(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon)); | 191 | assert(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon)); |
| 168 | assert(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon)); | 192 | assert(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon)); |
| 169 | assert(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon)); | 193 | assert(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon)); |
| 170 | assert(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon)); | 194 | assert(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon)); |
| 171 | assert(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon)); | 195 | assert(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon)); |
| 172 | } | 196 | } |
| 197 | |||
| 198 | test "math.pow.special" { | ||
| 199 | const epsilon = 0.000001; | ||
| 200 | |||
| 201 | assert(pow(f32, 4, 0.0) == 1.0); | ||
| 202 | assert(pow(f32, 7, -0.0) == 1.0); | ||
| 203 | assert(pow(f32, 45, 1.0) == 45); | ||
| 204 | assert(pow(f32, -45, 1.0) == -45); | ||
| 205 | assert(math.isNan(pow(f32, math.nan(f32), 5.0))); | ||
| 206 | assert(math.isNan(pow(f32, 5.0, math.nan(f32)))); | ||
| 207 | assert(math.isPositiveInf(pow(f32, 0.0, -1.0))); | ||
| 208 | assert(math.isNegativeInf(pow(f32, -0.0, -3.0))); | ||
| 209 | assert(math.isPositiveInf(pow(f32, 0.0, -math.inf(f32)))); | ||
| 210 | assert(math.isPositiveInf(pow(f32, -0.0, -math.inf(f32)))); | ||
| 211 | assert(pow(f32, 0.0, math.inf(f32)) == 0.0); | ||
| 212 | assert(pow(f32, -0.0, math.inf(f32)) == 0.0); | ||
| 213 | assert(math.isPositiveInf(pow(f32, 0.0, -2.0))); | ||
| 214 | assert(math.isPositiveInf(pow(f32, -0.0, -2.0))); | ||
| 215 | assert(pow(f32, 0.0, 1.0) == 0.0); | ||
| 216 | assert(pow(f32, -0.0, 1.0) == -0.0); | ||
| 217 | assert(pow(f32, 0.0, 2.0) == 0.0); | ||
| 218 | assert(pow(f32, -0.0, 2.0) == 0.0); | ||
| 219 | assert(math.approxEq(f32, pow(f32, -1.0, math.inf(f32)), 1.0, epsilon)); | ||
| 220 | assert(math.approxEq(f32, pow(f32, -1.0, -math.inf(f32)), 1.0, epsilon)); | ||
| 221 | assert(math.isPositiveInf(pow(f32, 1.2, math.inf(f32)))); | ||
| 222 | assert(math.isPositiveInf(pow(f32, -1.2, math.inf(f32)))); | ||
| 223 | assert(pow(f32, 1.2, -math.inf(f32)) == 0.0); | ||
| 224 | assert(pow(f32, -1.2, -math.inf(f32)) == 0.0); | ||
| 225 | assert(pow(f32, 0.2, math.inf(f32)) == 0.0); | ||
| 226 | assert(pow(f32, -0.2, math.inf(f32)) == 0.0); | ||
| 227 | assert(math.isPositiveInf(pow(f32, 0.2, -math.inf(f32)))); | ||
| 228 | assert(math.isPositiveInf(pow(f32, -0.2, -math.inf(f32)))); | ||
| 229 | assert(math.isPositiveInf(pow(f32, math.inf(f32), 1.0))); | ||
| 230 | assert(pow(f32, math.inf(f32), -1.0) == 0.0); | ||
| 231 | assert(pow(f32, -math.inf(f32), 5.0) == pow(f32, -0.0, -5.0)); | ||
| 232 | assert(pow(f32, -math.inf(f32), -5.2) == pow(f32, -0.0, 5.2)); | ||
| 233 | assert(math.isNan(pow(f32, -1.0, 1.2))); | ||
| 234 | assert(math.isNan(pow(f32, -12.4, 78.5))); | ||
| 235 | } |
std/math/round.zig+22| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - round(+-0) = +-0 | ||
| 4 | // - round(+-inf) = +-inf | ||
| 5 | // - round(nan) = nan | ||
| 6 | |||
| 1 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | const math = @import("index.zig"); | 9 | const math = @import("index.zig"); |
| ... | @@ -106,3 +112,19 @@ test "math.round64" { | ... | @@ -106,3 +112,19 @@ test "math.round64" { |
| 106 | assert(round64(0.2) == 0.0); | 112 | assert(round64(0.2) == 0.0); |
| 107 | assert(round64(1.8) == 2.0); | 113 | assert(round64(1.8) == 2.0); |
| 108 | } | 114 | } |
| 115 | |||
| 116 | test "math.round32.special" { | ||
| 117 | assert(round32(0.0) == 0.0); | ||
| 118 | assert(round32(-0.0) == -0.0); | ||
| 119 | assert(math.isPositiveInf(round32(math.inf(f32)))); | ||
| 120 | assert(math.isNegativeInf(round32(-math.inf(f32)))); | ||
| 121 | assert(math.isNan(round32(math.nan(f32)))); | ||
| 122 | } | ||
| 123 | |||
| 124 | test "math.round64.special" { | ||
| 125 | assert(round64(0.0) == 0.0); | ||
| 126 | assert(round64(-0.0) == -0.0); | ||
| 127 | assert(math.isPositiveInf(round64(math.inf(f64)))); | ||
| 128 | assert(math.isNegativeInf(round64(-math.inf(f64)))); | ||
| 129 | assert(math.isNan(round64(math.nan(f64)))); | ||
| 130 | } |
std/math/sin.zig+22| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - sin(+-0) = +-0 | ||
| 4 | // - sin(+-inf) = nan | ||
| 5 | // - sin(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -164,3 +170,19 @@ test "math.sin64" { | ... | @@ -164,3 +170,19 @@ test "math.sin64" { |
| 164 | assert(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); | 170 | assert(math.approxEq(f64, sin64(37.45), -0.246543, epsilon)); |
| 165 | assert(math.approxEq(f64, sin64(89.123), 0.916166, epsilon)); | 171 | assert(math.approxEq(f64, sin64(89.123), 0.916166, epsilon)); |
| 166 | } | 172 | } |
| 173 | |||
| 174 | test "math.sin32.special" { | ||
| 175 | assert(sin32(0.0) == 0.0); | ||
| 176 | assert(sin32(-0.0) == -0.0); | ||
| 177 | assert(math.isNan(sin32(math.inf(f32)))); | ||
| 178 | assert(math.isNan(sin32(-math.inf(f32)))); | ||
| 179 | assert(math.isNan(sin32(math.nan(f32)))); | ||
| 180 | } | ||
| 181 | |||
| 182 | test "math.sin64.special" { | ||
| 183 | assert(sin64(0.0) == 0.0); | ||
| 184 | assert(sin64(-0.0) == -0.0); | ||
| 185 | assert(math.isNan(sin64(math.inf(f64)))); | ||
| 186 | assert(math.isNan(sin64(-math.inf(f64)))); | ||
| 187 | assert(math.isNan(sin64(math.nan(f64)))); | ||
| 188 | } |
std/math/sinh.zig+26-1| ... | @@ -1,6 +1,12 @@ | ... | @@ -1,6 +1,12 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - sinh(+-0) = +-0 | ||
| 4 | // - sinh(+-inf) = +-inf | ||
| 5 | // - sinh(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | const expo2 = @import("_expo2.zig").expo2; | 9 | const expo2 = @import("expo2.zig").expo2; |
| 4 | 10 | ||
| 5 | // TODO issue #393 | 11 | // TODO issue #393 |
| 6 | pub const sinh = sinh_workaround; | 12 | pub const sinh = sinh_workaround; |
| ... | @@ -45,6 +51,8 @@ fn sinh32(x: f32) -> f32 { | ... | @@ -45,6 +51,8 @@ fn sinh32(x: f32) -> f32 { |
| 45 | } | 51 | } |
| 46 | 52 | ||
| 47 | fn sinh64(x: f64) -> f64 { | 53 | fn sinh64(x: f64) -> f64 { |
| 54 | @setFloatMode(this, @import("builtin").FloatMode.Strict); | ||
| 55 | |||
| 48 | const u = @bitCast(u64, x); | 56 | const u = @bitCast(u64, x); |
| 49 | const w = u32(u >> 32); | 57 | const w = u32(u >> 32); |
| 50 | const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); | 58 | const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); |
| ... | @@ -94,3 +102,20 @@ test "math.sinh64" { | ... | @@ -94,3 +102,20 @@ test "math.sinh64" { |
| 94 | assert(math.approxEq(f64, sinh64(0.8923), 1.015512, epsilon)); | 102 | assert(math.approxEq(f64, sinh64(0.8923), 1.015512, epsilon)); |
| 95 | assert(math.approxEq(f64, sinh64(1.5), 2.129279, epsilon)); | 103 | assert(math.approxEq(f64, sinh64(1.5), 2.129279, epsilon)); |
| 96 | } | 104 | } |
| 105 | |||
| 106 | test "math.sinh32.special" { | ||
| 107 | assert(sinh32(0.0) == 0.0); | ||
| 108 | assert(sinh32(-0.0) == -0.0); | ||
| 109 | assert(math.isPositiveInf(sinh32(math.inf(f32)))); | ||
| 110 | assert(math.isNegativeInf(sinh32(-math.inf(f32)))); | ||
| 111 | assert(math.isNan(sinh32(math.nan(f32)))); | ||
| 112 | } | ||
| 113 | |||
| 114 | test "math.sinh64.special" { | ||
| 115 | // TODO: Error on release mode (like pow) | ||
| 116 | assert(sinh64(0.0) == 0.0); | ||
| 117 | assert(sinh64(-0.0) == -0.0); | ||
| 118 | assert(math.isPositiveInf(sinh64(math.inf(f64)))); | ||
| 119 | assert(math.isNegativeInf(sinh64(-math.inf(f64)))); | ||
| 120 | assert(math.isNan(sinh64(math.nan(f64)))); | ||
| 121 | } |
std/math/sqrt.zig+26-3| ... | @@ -1,3 +1,10 @@ | ... | @@ -1,3 +1,10 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - sqrt(+inf) = +inf | ||
| 4 | // - sqrt(+-0) = +-0 | ||
| 5 | // - sqrt(x) = nan if x < 0 | ||
| 6 | // - sqrt(nan) = nan | ||
| 7 | |||
| 1 | const math = @import("index.zig"); | 8 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 9 | const assert = @import("../debug.zig").assert; |
| 3 | 10 | ||
| ... | @@ -28,7 +35,7 @@ fn sqrt32(x: f32) -> f32 { | ... | @@ -28,7 +35,7 @@ fn sqrt32(x: f32) -> f32 { |
| 28 | return x; // sqrt (+-0) = +-0 | 35 | return x; // sqrt (+-0) = +-0 |
| 29 | } | 36 | } |
| 30 | if (ix < 0) { | 37 | if (ix < 0) { |
| 31 | return (x - x) / (x - x); // sqrt(-ve) = snan | 38 | return math.snan(f32); |
| 32 | } | 39 | } |
| 33 | } | 40 | } |
| 34 | 41 | ||
| ... | @@ -106,12 +113,12 @@ fn sqrt64(x: f64) -> f64 { | ... | @@ -106,12 +113,12 @@ fn sqrt64(x: f64) -> f64 { |
| 106 | } | 113 | } |
| 107 | 114 | ||
| 108 | // sqrt(+-0) = +-0 | 115 | // sqrt(+-0) = +-0 |
| 109 | if ((ix0 & ~sign) | ix0 == 0) { | 116 | if (x == 0.0) { |
| 110 | return x; | 117 | return x; |
| 111 | } | 118 | } |
| 112 | // sqrt(-ve) = snan | 119 | // sqrt(-ve) = snan |
| 113 | if (ix0 & sign != 0) { | 120 | if (ix0 & sign != 0) { |
| 114 | return (x - x) / (x - x); | 121 | return math.snan(f64); |
| 115 | } | 122 | } |
| 116 | 123 | ||
| 117 | // normalize x | 124 | // normalize x |
| ... | @@ -254,3 +261,19 @@ test "math.sqrt64" { | ... | @@ -254,3 +261,19 @@ test "math.sqrt64" { |
| 254 | assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon)); | 261 | assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon)); |
| 255 | assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon)); | 262 | assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon)); |
| 256 | } | 263 | } |
| 264 | |||
| 265 | test "math.sqrt32.special" { | ||
| 266 | assert(math.isPositiveInf(sqrt32(math.inf(f32)))); | ||
| 267 | assert(sqrt32(0.0) == 0.0); | ||
| 268 | assert(sqrt32(-0.0) == -0.0); | ||
| 269 | assert(math.isNan(sqrt32(-1.0))); | ||
| 270 | assert(math.isNan(sqrt32(math.nan(f32)))); | ||
| 271 | } | ||
| 272 | |||
| 273 | test "math.sqrt64.special" { | ||
| 274 | assert(math.isPositiveInf(sqrt64(math.inf(f64)))); | ||
| 275 | assert(sqrt64(0.0) == 0.0); | ||
| 276 | assert(sqrt64(-0.0) == -0.0); | ||
| 277 | assert(math.isNan(sqrt64(-1.0))); | ||
| 278 | assert(math.isNan(sqrt64(math.nan(f64)))); | ||
| 279 | } |
std/math/tan.zig+22| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - tan(+-0) = +-0 | ||
| 4 | // - tan(+-inf) = nan | ||
| 5 | // - tan(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -150,3 +156,19 @@ test "math.tan64" { | ... | @@ -150,3 +156,19 @@ test "math.tan64" { |
| 150 | assert(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); | 156 | assert(math.approxEq(f64, tan64(37.45), -0.254397, epsilon)); |
| 151 | assert(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); | 157 | assert(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon)); |
| 152 | } | 158 | } |
| 159 | |||
| 160 | test "math.tan32.special" { | ||
| 161 | assert(tan32(0.0) == 0.0); | ||
| 162 | assert(tan32(-0.0) == -0.0); | ||
| 163 | assert(math.isNan(tan32(math.inf(f32)))); | ||
| 164 | assert(math.isNan(tan32(-math.inf(f32)))); | ||
| 165 | assert(math.isNan(tan32(math.nan(f32)))); | ||
| 166 | } | ||
| 167 | |||
| 168 | test "math.tan64.special" { | ||
| 169 | assert(tan64(0.0) == 0.0); | ||
| 170 | assert(tan64(-0.0) == -0.0); | ||
| 171 | assert(math.isNan(tan64(math.inf(f64)))); | ||
| 172 | assert(math.isNan(tan64(-math.inf(f64)))); | ||
| 173 | assert(math.isNan(tan64(math.nan(f64)))); | ||
| 174 | } |
std/math/tanh.zig+34-3| ... | @@ -1,6 +1,12 @@ | ... | @@ -1,6 +1,12 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - sinh(+-0) = +-0 | ||
| 4 | // - sinh(+-inf) = +-1 | ||
| 5 | // - sinh(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | const expo2 = @import("_expo2.zig").expo2; | 9 | const expo2 = @import("expo2.zig").expo2; |
| 4 | 10 | ||
| 5 | // TODO issue #393 | 11 | // TODO issue #393 |
| 6 | pub const tanh = tanh_workaround; | 12 | pub const tanh = tanh_workaround; |
| ... | @@ -64,11 +70,19 @@ fn tanh64(x: f64) -> f64 { | ... | @@ -64,11 +70,19 @@ fn tanh64(x: f64) -> f64 { |
| 64 | 70 | ||
| 65 | var t: f64 = undefined; | 71 | var t: f64 = undefined; |
| 66 | 72 | ||
| 73 | // TODO: Shouldn't need these checks. | ||
| 74 | if (x == 0.0) { | ||
| 75 | return x; | ||
| 76 | } | ||
| 77 | if (math.isNan(x)) { | ||
| 78 | return x; | ||
| 79 | } | ||
| 80 | |||
| 67 | // |x| < log(3) / 2 ~= 0.5493 or nan | 81 | // |x| < log(3) / 2 ~= 0.5493 or nan |
| 68 | if (w > 0x3Fe193EA) { | 82 | if (w > 0x3FE193EA) { |
| 69 | // |x| > 20 or nan | 83 | // |x| > 20 or nan |
| 70 | if (w > 0x40340000) { | 84 | if (w > 0x40340000) { |
| 71 | t = 1.0 + 0 / x; | 85 | t = 1.0; // TODO + 0 / x; |
| 72 | } else { | 86 | } else { |
| 73 | t = math.expm1(2 * x); | 87 | t = math.expm1(2 * x); |
| 74 | t = 1 - 2 / (t + 2); | 88 | t = 1 - 2 / (t + 2); |
| ... | @@ -121,3 +135,20 @@ test "math.tanh64" { | ... | @@ -121,3 +135,20 @@ test "math.tanh64" { |
| 121 | assert(math.approxEq(f64, tanh64(1.5), 0.905148, epsilon)); | 135 | assert(math.approxEq(f64, tanh64(1.5), 0.905148, epsilon)); |
| 122 | assert(math.approxEq(f64, tanh64(37.45), 1.0, epsilon)); | 136 | assert(math.approxEq(f64, tanh64(37.45), 1.0, epsilon)); |
| 123 | } | 137 | } |
| 138 | |||
| 139 | test "math.tanh32.special" { | ||
| 140 | // TODO: Error on release (like pow) | ||
| 141 | assert(tanh32(0.0) == 0.0); | ||
| 142 | assert(tanh32(-0.0) == -0.0); | ||
| 143 | assert(tanh32(math.inf(f32)) == 1.0); | ||
| 144 | assert(tanh32(-math.inf(f32)) == -1.0); | ||
| 145 | assert(math.isNan(tanh32(math.nan(f32)))); | ||
| 146 | } | ||
| 147 | |||
| 148 | test "math.tanh64.special" { | ||
| 149 | assert(tanh64(0.0) == 0.0); | ||
| 150 | assert(tanh64(-0.0) == -0.0); | ||
| 151 | assert(tanh64(math.inf(f64)) == 1.0); | ||
| 152 | assert(tanh64(-math.inf(f64)) == -1.0); | ||
| 153 | assert(math.isNan(tanh64(math.nan(f64)))); | ||
| 154 | } |
std/math/trunc.zig+22| ... | @@ -1,3 +1,9 @@ | ... | @@ -1,3 +1,9 @@ |
| 1 | // Special Cases: | ||
| 2 | // | ||
| 3 | // - trunc(+-0) = +-0 | ||
| 4 | // - trunc(+-inf) = +-inf | ||
| 5 | // - trunc(nan) = nan | ||
| 6 | |||
| 1 | const math = @import("index.zig"); | 7 | const math = @import("index.zig"); |
| 2 | const assert = @import("../debug.zig").assert; | 8 | const assert = @import("../debug.zig").assert; |
| 3 | 9 | ||
| ... | @@ -70,3 +76,19 @@ test "math.trunc64" { | ... | @@ -70,3 +76,19 @@ test "math.trunc64" { |
| 70 | assert(trunc64(-1.3) == -1.0); | 76 | assert(trunc64(-1.3) == -1.0); |
| 71 | assert(trunc64(0.2) == 0.0); | 77 | assert(trunc64(0.2) == 0.0); |
| 72 | } | 78 | } |
| 79 | |||
| 80 | test "math.trunc32.special" { | ||
| 81 | assert(trunc32(0.0) == 0.0); // 0x3F800000 | ||
| 82 | assert(trunc32(-0.0) == -0.0); | ||
| 83 | assert(math.isPositiveInf(trunc32(math.inf(f32)))); | ||
| 84 | assert(math.isNegativeInf(trunc32(-math.inf(f32)))); | ||
| 85 | assert(math.isNan(trunc32(math.nan(f32)))); | ||
| 86 | } | ||
| 87 | |||
| 88 | test "math.trunc64.special" { | ||
| 89 | assert(trunc64(0.0) == 0.0); | ||
| 90 | assert(trunc64(-0.0) == -0.0); | ||
| 91 | assert(math.isPositiveInf(trunc64(math.inf(f64)))); | ||
| 92 | assert(math.isNegativeInf(trunc64(-math.inf(f64)))); | ||
| 93 | assert(math.isNan(trunc64(math.nan(f64)))); | ||
| 94 | } |