| ... | ... | @@ -10,6 +10,7 @@ const std = @import("../std.zig"); |
| 10 | 10 | const math = std.math; |
| 11 | 11 | const mem = std.mem; |
| 12 | 12 | const expect = std.testing.expect; |
| 13 | const expectEqual = std.testing.expectEqual; |
| 13 | 14 | |
| 14 | 15 | /// Returns e raised to the power of x, minus 1 (e^x - 1). This is more accurate than exp(e, x) - 1 |
| 15 | 16 | /// when x is near 0. |
| ... | ... | @@ -39,9 +40,9 @@ fn expm1_32(x_: f32) f32 { |
| 39 | 40 | const Q2: f32 = 1.5807170421e-3; |
| 40 | 41 | |
| 41 | 42 | var x = x_; |
| 42 | | const ux = @as(u32, @bitCast(x)); |
| 43 | const ux: u32 = @bitCast(x); |
| 43 | 44 | const hx = ux & 0x7FFFFFFF; |
| 44 | | const sign = hx >> 31; |
| 45 | const sign = ux >> 31; |
| 45 | 46 | |
| 46 | 47 | // TODO: Shouldn't need this check explicitly. |
| 47 | 48 | if (math.isNegativeInf(x)) { |
| ... | ... | @@ -147,7 +148,7 @@ fn expm1_32(x_: f32) f32 { |
| 147 | 148 | return y - 1.0; |
| 148 | 149 | } |
| 149 | 150 | |
| 150 | | const uf = @as(f32, @bitCast(@as(u32, @intCast(0x7F -% k)) << 23)); |
| 151 | const uf: f32 = @bitCast(@as(u32, @intCast(0x7F -% k)) << 23); |
| 151 | 152 | if (k < 23) { |
| 152 | 153 | return (x - e + (1 - uf)) * twopk; |
| 153 | 154 | } else { |
| ... | ... | @@ -286,39 +287,77 @@ fn expm1_64(x_: f64) f64 { |
| 286 | 287 | } |
| 287 | 288 | } |
| 288 | 289 | |
| 289 | | test expm1 { |
| 290 | | try expect(expm1(@as(f32, 0.0)) == expm1_32(0.0)); |
| 291 | | try expect(expm1(@as(f64, 0.0)) == expm1_64(0.0)); |
| 290 | test "expm1_32() special" { |
| 291 | try expectEqual(expm1_32(0.0), 0.0); |
| 292 | try expectEqual(expm1_32(-0.0), 0.0); |
| 293 | try expectEqual(expm1_32(math.ln2), 1.0); |
| 294 | try expectEqual(expm1_32(math.inf(f32)), math.inf(f32)); |
| 295 | try expectEqual(expm1_32(-math.inf(f32)), -1.0); |
| 296 | try expect(math.isNan(expm1_32(math.nan(f32)))); |
| 297 | try expect(math.isNan(expm1_32(math.snan(f32)))); |
| 292 | 298 | } |
| 293 | 299 | |
| 294 | | test expm1_32 { |
| 295 | | const epsilon = 0.000001; |
| 296 | | |
| 297 | | try expect(math.isPositiveZero(expm1_32(0.0))); |
| 298 | | try expect(math.approxEqAbs(f32, expm1_32(0.0), 0.0, epsilon)); |
| 299 | | try expect(math.approxEqAbs(f32, expm1_32(0.2), 0.221403, epsilon)); |
| 300 | | try expect(math.approxEqAbs(f32, expm1_32(0.8923), 1.440737, epsilon)); |
| 301 | | try expect(math.approxEqAbs(f32, expm1_32(1.5), 3.481689, epsilon)); |
| 300 | test "expm1_32() sanity" { |
| 301 | try expectEqual(expm1_32(-0x1.0223a0p+3), -0x1.ffd6e0p-1); |
| 302 | try expectEqual(expm1_32(0x1.161868p+2), 0x1.30712ap+6); |
| 303 | try expectEqual(expm1_32(-0x1.0c34b4p+3), -0x1.ffe1fap-1); |
| 304 | try expectEqual(expm1_32(-0x1.a206f0p+2), -0x1.ff4116p-1); |
| 305 | try expectEqual(expm1_32(0x1.288bbcp+3), 0x1.4ab480p+13); // Disagrees with GCC in last bit |
| 306 | try expectEqual(expm1_32(0x1.52efd0p-1), 0x1.e09536p-1); |
| 307 | try expectEqual(expm1_32(-0x1.a05cc8p-2), -0x1.561c3ep-2); |
| 308 | try expectEqual(expm1_32(0x1.1f9efap-1), 0x1.81ec4ep-1); |
| 309 | try expectEqual(expm1_32(0x1.8c5db0p-1), 0x1.2b3364p+0); |
| 310 | try expectEqual(expm1_32(-0x1.5b86eap-1), -0x1.f8951ap-2); |
| 302 | 311 | } |
| 303 | 312 | |
| 304 | | test expm1_64 { |
| 305 | | const epsilon = 0.000001; |
| 313 | test "expm1_32() boundary" { |
| 314 | // TODO: The last value before inf is actually 0x1.62e300p+6 -> 0x1.ff681ep+127 |
| 315 | // try expectEqual(expm1_32(0x1.62e42ep+6), 0x1.ffff08p+127); // Last value before result is inf |
| 316 | try expectEqual(expm1_32(0x1.62e430p+6), math.inf(f32)); // First value that gives inf |
| 317 | try expectEqual(expm1_32(0x1.fffffep+127), math.inf(f32)); // Max input value |
| 318 | try expectEqual(expm1_32(0x1p-149), 0x1p-149); // Min positive input value |
| 319 | try expectEqual(expm1_32(-0x1p-149), -0x1p-149); // Min negative input value |
| 320 | try expectEqual(expm1_32(0x1p-126), 0x1p-126); // First positive subnormal input |
| 321 | try expectEqual(expm1_32(-0x1p-126), -0x1p-126); // First negative subnormal input |
| 322 | try expectEqual(expm1_32(0x1.fffffep-125), 0x1.fffffep-125); // Last positive value before subnormal |
| 323 | try expectEqual(expm1_32(-0x1.fffffep-125), -0x1.fffffep-125); // Last negative value before subnormal |
| 324 | try expectEqual(expm1_32(-0x1.154244p+4), -0x1.fffffep-1); // Last value before result is -1 |
| 325 | try expectEqual(expm1_32(-0x1.154246p+4), -1); // First value where result is -1 |
| 326 | } |
| 306 | 327 | |
| 307 | | try expect(math.isPositiveZero(expm1_64(0.0))); |
| 308 | | try expect(math.approxEqAbs(f64, expm1_64(0.0), 0.0, epsilon)); |
| 309 | | try expect(math.approxEqAbs(f64, expm1_64(0.2), 0.221403, epsilon)); |
| 310 | | try expect(math.approxEqAbs(f64, expm1_64(0.8923), 1.440737, epsilon)); |
| 311 | | try expect(math.approxEqAbs(f64, expm1_64(1.5), 3.481689, epsilon)); |
| 328 | test "expm1_64() special" { |
| 329 | try expectEqual(expm1_64(0.0), 0.0); |
| 330 | try expectEqual(expm1_64(-0.0), 0.0); |
| 331 | try expectEqual(expm1_64(math.ln2), 1.0); |
| 332 | try expectEqual(expm1_64(math.inf(f64)), math.inf(f64)); |
| 333 | try expectEqual(expm1_64(-math.inf(f64)), -1.0); |
| 334 | try expect(math.isNan(expm1_64(math.nan(f64)))); |
| 335 | try expect(math.isNan(expm1_64(math.snan(f64)))); |
| 312 | 336 | } |
| 313 | 337 | |
| 314 | | test "expm1_32.special" { |
| 315 | | try expect(math.isPositiveInf(expm1_32(math.inf(f32)))); |
| 316 | | try expect(expm1_32(-math.inf(f32)) == -1.0); |
| 317 | | try expect(math.isNan(expm1_32(math.nan(f32)))); |
| 338 | test "expm1_64() sanity" { |
| 339 | try expectEqual(expm1_64(-0x1.02239f3c6a8f1p+3), -0x1.ffd6df9b02b3ep-1); |
| 340 | try expectEqual(expm1_64(0x1.161868e18bc67p+2), 0x1.30712ed238c04p+6); |
| 341 | try expectEqual(expm1_64(-0x1.0c34b3e01e6e7p+3), -0x1.ffe1f94e493e7p-1); |
| 342 | try expectEqual(expm1_64(-0x1.a206f0a19dcc4p+2), -0x1.ff4115c03f78dp-1); |
| 343 | try expectEqual(expm1_64(0x1.288bbb0d6a1e6p+3), 0x1.4ab477496e07ep+13); |
| 344 | try expectEqual(expm1_64(0x1.52efd0cd80497p-1), 0x1.e095382100a01p-1); |
| 345 | try expectEqual(expm1_64(-0x1.a05cc754481d1p-2), -0x1.561c3e0582be6p-2); |
| 346 | try expectEqual(expm1_64(0x1.1f9ef934745cbp-1), 0x1.81ec4cd4d4a8fp-1); |
| 347 | try expectEqual(expm1_64(0x1.8c5db097f7442p-1), 0x1.2b3363a944bf7p+0); |
| 348 | try expectEqual(expm1_64(-0x1.5b86ea8118a0ep-1), -0x1.f8951aebffbafp-2); |
| 318 | 349 | } |
| 319 | 350 | |
| 320 | | test "expm1_64.special" { |
| 321 | | try expect(math.isPositiveInf(expm1_64(math.inf(f64)))); |
| 322 | | try expect(expm1_64(-math.inf(f64)) == -1.0); |
| 323 | | try expect(math.isNan(expm1_64(math.nan(f64)))); |
| 351 | test "expm1_64() boundary" { |
| 352 | try expectEqual(expm1_64(0x1.62e42fefa39efp+9), 0x1.fffffffffff2ap+1023); // Last value before result is inf |
| 353 | try expectEqual(expm1_64(0x1.62e42fefa39f0p+9), math.inf(f64)); // First value that gives inf |
| 354 | try expectEqual(expm1_64(0x1.fffffffffffffp+1023), math.inf(f64)); // Max input value |
| 355 | try expectEqual(expm1_64(0x1p-1074), 0x1p-1074); // Min positive input value |
| 356 | try expectEqual(expm1_64(-0x1p-1074), -0x1p-1074); // Min negative input value |
| 357 | try expectEqual(expm1_64(0x1p-1022), 0x1p-1022); // First positive subnormal input |
| 358 | try expectEqual(expm1_64(-0x1p-1022), -0x1p-1022); // First negative subnormal input |
| 359 | try expectEqual(expm1_64(0x1.fffffffffffffp-1021), 0x1.fffffffffffffp-1021); // Last positive value before subnormal |
| 360 | try expectEqual(expm1_64(-0x1.fffffffffffffp-1021), -0x1.fffffffffffffp-1021); // Last negative value before subnormal |
| 361 | try expectEqual(expm1_64(-0x1.2b708872320e1p+5), -0x1.fffffffffffffp-1); // Last value before result is -1 |
| 362 | try expectEqual(expm1_64(-0x1.2b708872320e2p+5), -1); // First value where result is -1 |
| 324 | 363 | } |