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