authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-06-27 17:15:41-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-06-27 17:15:41-04:00
log3e8af78895d313f0706389da2ad7e5c60df95964
treeeb59c5ec92f280b01f36e52a1e937530f2bb5422
parent221286433aa5825dcb177596dbf697d873c6f1cb
parente81bf1c38c9330d6f68584627992d3ab820ed50f

Merge branch 'math'

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,9 +246,51 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST
246install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")246install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")
247install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")247install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")
248install(FILES "${CMAKE_SOURCE_DIR}/std/linked_list.zig" DESTINATION "${ZIG_STD_DEST}")248install(FILES "${CMAKE_SOURCE_DIR}/std/linked_list.zig" DESTINATION "${ZIG_STD_DEST}")
249install(FILES "${CMAKE_SOURCE_DIR}/std/math/index.zig" DESTINATION "${ZIG_STD_DEST}/math")249install(FILES "${CMAKE_SOURCE_DIR}/std/math/acos.zig" DESTINATION "${ZIG_STD_DEST}/math")
250install(FILES "${CMAKE_SOURCE_DIR}/std/math/frexp.zig" DESTINATION "${ZIG_STD_DEST}/math")250install(FILES "${CMAKE_SOURCE_DIR}/std/math/acosh.zig" DESTINATION "${ZIG_STD_DEST}/math")
251install(FILES "${CMAKE_SOURCE_DIR}/std/math/asin.zig" DESTINATION "${ZIG_STD_DEST}/math")
252install(FILES "${CMAKE_SOURCE_DIR}/std/math/asinh.zig" DESTINATION "${ZIG_STD_DEST}/math")
253install(FILES "${CMAKE_SOURCE_DIR}/std/math/atan.zig" DESTINATION "${ZIG_STD_DEST}/math")
254install(FILES "${CMAKE_SOURCE_DIR}/std/math/atan2.zig" DESTINATION "${ZIG_STD_DEST}/math")
255install(FILES "${CMAKE_SOURCE_DIR}/std/math/atanh.zig" DESTINATION "${ZIG_STD_DEST}/math")
256install(FILES "${CMAKE_SOURCE_DIR}/std/math/cbrt.zig" DESTINATION "${ZIG_STD_DEST}/math")
257install(FILES "${CMAKE_SOURCE_DIR}/std/math/ceil.zig" DESTINATION "${ZIG_STD_DEST}/math")
258install(FILES "${CMAKE_SOURCE_DIR}/std/math/copysign.zig" DESTINATION "${ZIG_STD_DEST}/math")
259install(FILES "${CMAKE_SOURCE_DIR}/std/math/cos.zig" DESTINATION "${ZIG_STD_DEST}/math")
260install(FILES "${CMAKE_SOURCE_DIR}/std/math/cosh.zig" DESTINATION "${ZIG_STD_DEST}/math")
261install(FILES "${CMAKE_SOURCE_DIR}/std/math/exp.zig" DESTINATION "${ZIG_STD_DEST}/math")
262install(FILES "${CMAKE_SOURCE_DIR}/std/math/exp2.zig" DESTINATION "${ZIG_STD_DEST}/math")
263install(FILES "${CMAKE_SOURCE_DIR}/std/math/expm1.zig" DESTINATION "${ZIG_STD_DEST}/math")
264install(FILES "${CMAKE_SOURCE_DIR}/std/math/expo2.zig" DESTINATION "${ZIG_STD_DEST}/math")
251install(FILES "${CMAKE_SOURCE_DIR}/std/math/fabs.zig" DESTINATION "${ZIG_STD_DEST}/math")265install(FILES "${CMAKE_SOURCE_DIR}/std/math/fabs.zig" DESTINATION "${ZIG_STD_DEST}/math")
266install(FILES "${CMAKE_SOURCE_DIR}/std/math/floor.zig" DESTINATION "${ZIG_STD_DEST}/math")
267install(FILES "${CMAKE_SOURCE_DIR}/std/math/fma.zig" DESTINATION "${ZIG_STD_DEST}/math")
268install(FILES "${CMAKE_SOURCE_DIR}/std/math/frexp.zig" DESTINATION "${ZIG_STD_DEST}/math")
269install(FILES "${CMAKE_SOURCE_DIR}/std/math/hypot.zig" DESTINATION "${ZIG_STD_DEST}/math")
270install(FILES "${CMAKE_SOURCE_DIR}/std/math/ilogb.zig" DESTINATION "${ZIG_STD_DEST}/math")
271install(FILES "${CMAKE_SOURCE_DIR}/std/math/index.zig" DESTINATION "${ZIG_STD_DEST}/math")
272install(FILES "${CMAKE_SOURCE_DIR}/std/math/inf.zig" DESTINATION "${ZIG_STD_DEST}/math")
273install(FILES "${CMAKE_SOURCE_DIR}/std/math/isfinite.zig" DESTINATION "${ZIG_STD_DEST}/math")
274install(FILES "${CMAKE_SOURCE_DIR}/std/math/isinf.zig" DESTINATION "${ZIG_STD_DEST}/math")
275install(FILES "${CMAKE_SOURCE_DIR}/std/math/isnan.zig" DESTINATION "${ZIG_STD_DEST}/math")
276install(FILES "${CMAKE_SOURCE_DIR}/std/math/isnormal.zig" DESTINATION "${ZIG_STD_DEST}/math")
277install(FILES "${CMAKE_SOURCE_DIR}/std/math/ln.zig" DESTINATION "${ZIG_STD_DEST}/math")
278install(FILES "${CMAKE_SOURCE_DIR}/std/math/log.zig" DESTINATION "${ZIG_STD_DEST}/math")
279install(FILES "${CMAKE_SOURCE_DIR}/std/math/log10.zig" DESTINATION "${ZIG_STD_DEST}/math")
280install(FILES "${CMAKE_SOURCE_DIR}/std/math/log1p.zig" DESTINATION "${ZIG_STD_DEST}/math")
281install(FILES "${CMAKE_SOURCE_DIR}/std/math/log2.zig" DESTINATION "${ZIG_STD_DEST}/math")
282install(FILES "${CMAKE_SOURCE_DIR}/std/math/modf.zig" DESTINATION "${ZIG_STD_DEST}/math")
283install(FILES "${CMAKE_SOURCE_DIR}/std/math/nan.zig" DESTINATION "${ZIG_STD_DEST}/math")
284install(FILES "${CMAKE_SOURCE_DIR}/std/math/pow.zig" DESTINATION "${ZIG_STD_DEST}/math")
285install(FILES "${CMAKE_SOURCE_DIR}/std/math/round.zig" DESTINATION "${ZIG_STD_DEST}/math")
286install(FILES "${CMAKE_SOURCE_DIR}/std/math/scalbn.zig" DESTINATION "${ZIG_STD_DEST}/math")
287install(FILES "${CMAKE_SOURCE_DIR}/std/math/signbit.zig" DESTINATION "${ZIG_STD_DEST}/math")
288install(FILES "${CMAKE_SOURCE_DIR}/std/math/sin.zig" DESTINATION "${ZIG_STD_DEST}/math")
289install(FILES "${CMAKE_SOURCE_DIR}/std/math/sinh.zig" DESTINATION "${ZIG_STD_DEST}/math")
290install(FILES "${CMAKE_SOURCE_DIR}/std/math/sqrt.zig" DESTINATION "${ZIG_STD_DEST}/math")
291install(FILES "${CMAKE_SOURCE_DIR}/std/math/tan.zig" DESTINATION "${ZIG_STD_DEST}/math")
292install(FILES "${CMAKE_SOURCE_DIR}/std/math/tanh.zig" DESTINATION "${ZIG_STD_DEST}/math")
293install(FILES "${CMAKE_SOURCE_DIR}/std/math/trunc.zig" DESTINATION "${ZIG_STD_DEST}/math")
252install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}")294install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}")
253install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")295install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")
254install(FILES "${CMAKE_SOURCE_DIR}/std/os/child_process.zig" DESTINATION "${ZIG_STD_DEST}/os")296install(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,6 +438,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
438 }438 }
439439
440 addLLVMFnAttr(fn_table_entry->llvm_value, "nounwind");440 addLLVMFnAttr(fn_table_entry->llvm_value, "nounwind");
441 addLLVMFnAttr(fn_table_entry->llvm_value, "nobuiltin");
441 if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) {442 if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) {
442 ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true");443 ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true");
443 ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim-non-leaf", nullptr);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
5const math = @import("index.zig");
6const assert = @import("../debug.zig").assert;
7
8pub const acos = acos_workaround;
9
10// TODO issue #393
11pub 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
20fn 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
31fn 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
78fn 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
95fn 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
147test "math.acos" {
148 assert(acos_workaround(f32(0.0)) == acos32(0.0));
149 assert(acos_workaround(f64(0.0)) == acos64(0.0));
150}
151
152test "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
163test "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
174test "math.acos32.special" {
175 assert(math.isNan(acos32(-2)));
176 assert(math.isNan(acos32(1.5)));
177}
178
179test "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
6const math = @import("index.zig");
7const assert = @import("../debug.zig").assert;
8
9pub const acosh = acosh_workaround;
10
11// TODO issue #393
12pub 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))
22fn 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
40fn 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
58test "math.acosh" {
59 assert(acosh_workaround(f32(1.5)) == acosh32(1.5));
60 assert(acosh_workaround(f64(1.5)) == acosh64(1.5));
61}
62
63test "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
72test "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
81test "math.acosh32.special" {
82 assert(math.isNan(acosh32(math.nan(f32))));
83 assert(math.isSignalNan(acosh32(0.5)));
84}
85
86test "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
6const math = @import("index.zig");
7const assert = @import("../debug.zig").assert;
8
9pub const asin = asin_workaround;
10
11// TODO issue #393
12pub 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
21fn 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
32fn 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
70fn 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
87fn 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
140test "math.asin" {
141 assert(asin_workaround(f32(0.0)) == asin32(0.0));
142 assert(asin_workaround(f64(0.0)) == asin64(0.0));
143}
144
145test "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
156test "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
167test "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
174test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10pub const asinh = asinh_workaround;
11
12// TODO issue #393
13pub 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)
23fn 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
55fn 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
86test "math.asinh" {
87 assert(asinh_workaround(f32(0.0)) == asinh32(0.0));
88 assert(asinh_workaround(f64(0.0)) == asinh64(0.0));
89}
90
91test "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
103test "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
115test "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
123test "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
6const math = @import("index.zig");
7const assert = @import("../debug.zig").assert;
8
9// TODO issue #393
10pub const atan = atan_workaround;
11
12pub 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
21fn 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
113fn 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
212test "math.atan" {
213 assert(atan_workaround(f32(0.2)) == atan32(0.2));
214 assert(atan_workaround(f64(0.2)) == atan64(0.2));
215}
216
217test "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
227test "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
237test "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
246test "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
21const math = @import("index.zig");
22const assert = @import("../debug.zig").assert;
23
24pub const atan2 = atan2_workaround;
25
26// TODO issue #393
27pub 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
35fn 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
120fn 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
210test "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
215test "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
227test "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
239test "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
263test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10// TODO issue #393
11pub const atanh = atanh_workaround;
12
13pub 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)
23fn 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
52fn 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
81test "math.atanh" {
82 assert(atanh(f32(0.0)) == atanh_32(0.0));
83 assert(atanh(f64(0.0)) == atanh_64(0.0));
84}
85
86test "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
94test "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
102test "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
110test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10// TODO issue #393
11pub const cbrt = cbrt_workaround;
12
13pub 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
22fn 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
62fn 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
118test "math.cbrt" {
119 assert(cbrt(f32(0.0)) == cbrt32(0.0));
120 assert(cbrt(f64(0.0)) == cbrt64(0.0));
121}
122
123test "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
134test "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
145test "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
153test "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
7const builtin = @import("builtin");
8const math = @import("index.zig");
9const assert = @import("../debug.zig").assert;
10
11// TODO issue #393
12pub const ceil = ceil_workaround;
13
14pub 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
23fn 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
57fn 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
88test "math.ceil" {
89 assert(ceil(f32(0.0)) == ceil32(0.0));
90 assert(ceil(f64(0.0)) == ceil64(0.0));
91}
92
93test "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
99test "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
105test "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
113test "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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4// TODO issue #393
5pub const copysign = copysign_workaround;
6
7pub 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
15fn 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
24fn 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
33test "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
38test "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
45test "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
6const math = @import("index.zig");
7const assert = @import("../debug.zig").assert;
8
9// TODO issue #393
10pub const cos = cos_workaround;
11
12pub 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
22const S0 = 1.58962301576546568060E-10;
23const S1 = -2.50507477628578072866E-8;
24const S2 = 2.75573136213857245213E-6;
25const S3 = -1.98412698295895385996E-4;
26const S4 = 8.33333333332211858878E-3;
27const S5 = -1.66666666666666307295E-1;
28
29// cos polynomial coeffiecients
30const C0 = -1.13585365213876817300E-11;
31const C1 = 2.08757008419747316778E-9;
32const C2 = -2.75573141792967388112E-7;
33const C3 = 2.48015872888517045348E-5;
34const C4 = -1.38888888888730564116E-3;
35const 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.
40fn 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
93fn 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
144test "math.cos" {
145 assert(cos(f32(0.0)) == cos32(0.0));
146 assert(cos(f64(0.0)) == cos64(0.0));
147}
148
149test "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
160test "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
171test "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
177test "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
7const math = @import("index.zig");
8const expo2 = @import("expo2.zig").expo2;
9const assert = @import("../debug.zig").assert;
10
11// TODO issue #393
12pub const cosh = cosh_workaround;
13
14pub 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)
26fn 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
51fn 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
84test "math.cosh" {
85 assert(cosh(f32(1.5)) == cosh32(1.5));
86 assert(cosh(f64(1.5)) == cosh64(1.5));
87}
88
89test "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
98test "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
107test "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
115test "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
6const math = @import("index.zig");
7const assert = @import("../debug.zig").assert;
8
9// TODO issue #393
10pub const exp = exp_workaround;
11
12pub 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
21fn 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
98fn 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
184test "math.exp" {
185 assert(exp(f32(0.0)) == exp32(0.0));
186 assert(exp(f64(0.0)) == exp64(0.0));
187}
188
189test "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
199test "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
209test "math.exp32.special" {
210 assert(math.isPositiveInf(exp32(math.inf(f32))));
211 assert(math.isNan(exp32(math.nan(f32))));
212}
213
214test "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
6const math = @import("index.zig");
7const assert = @import("../debug.zig").assert;
8
9// TODO issue #393
10pub const exp2 = exp2_workaround;
11
12pub 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
21const 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
40fn 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
97const 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
357fn 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
423test "math.exp2" {
424 assert(exp2(f32(0.8923)) == exp2_32(0.8923));
425 assert(exp2(f64(0.8923)) == exp2_64(0.8923));
426}
427
428test "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
438test "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
447test "math.exp2_32.special" {
448 assert(math.isPositiveInf(exp2_32(math.inf(f32))));
449 assert(math.isNan(exp2_32(math.nan(f32))));
450}
451
452test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10// TODO issue #393
11pub const expm1 = expm1_workaround;
12
13pub 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
22fn 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
148fn 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
277test "math.exp1m" {
278 assert(expm1(f32(0.0)) == expm1_32(0.0));
279 assert(expm1(f64(0.0)) == expm1_64(0.0));
280}
281
282test "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
292test "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
302test "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
310test "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 @@
1const math = @import("index.zig");
2
3pub 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
12fn 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
21fn 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,10 +1,19 @@
1// Special Cases:
2//
3// - fabs(+-inf) = +inf
4// - fabs(nan) = nan
5
6const math = @import("index.zig");
1const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
28
3pub fn fabs(x: var) -> @typeOf(x) {9// TODO issue #393
10pub const fabs = fabs_workaround;
11
12pub fn fabs_workaround(x: var) -> @typeOf(x) {
4 const T = @typeOf(x);13 const T = @typeOf(x);
5 switch (T) {14 switch (T) {
6 f32 => fabs32(x),15 f32 => @inlineCall(fabs32, x),
7 f64 => fabs64(x),16 f64 => @inlineCall(fabs64, x),
8 else => @compileError("fabs not implemented for " ++ @typeName(T)),17 else => @compileError("fabs not implemented for " ++ @typeName(T)),
9 }18 }
10}19}
...@@ -21,29 +30,29 @@ fn fabs64(x: f64) -> f64 {...@@ -21,29 +30,29 @@ fn fabs64(x: f64) -> f64 {
21 @bitCast(f64, u)30 @bitCast(f64, u)
22}31}
2332
24test "fabs" {33test "math.fabs" {
25 assert(fabs(f32(1.0)) == fabs32(1.0));34 assert(fabs(f32(1.0)) == fabs32(1.0));
26 assert(fabs(f64(1.0)) == fabs64(1.0));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}
3237
33test "fabs32" {38test "math.fabs32" {
34 assert(fabs64(1.0) == 1.0);39 assert(fabs64(1.0) == 1.0);
35 assert(fabs64(-1.0) == 1.0);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}
4142
42test "fabs64" {43test "math.fabs64" {
43 assert(fabs64(1.0) == 1.0);44 assert(fabs64(1.0) == 1.0);
44 assert(fabs64(-1.0) == 1.0);45 assert(fabs64(-1.0) == 1.0);
45 comptime {46}
46 assert(fabs64(1.0) == 1.0);47
47 assert(fabs64(-1.0) == 1.0);48test "math.fabs32.special" {
48 }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
54test "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
7const builtin = @import("builtin");
8const assert = @import("../debug.zig").assert;
9const math = @import("index.zig");
10
11// TODO issue #393
12pub const floor = floor_workaround;
13
14pub 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
23fn 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
57fn 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
88test "math.floor" {
89 assert(floor(f32(1.3)) == floor32(1.3));
90 assert(floor(f64(1.3)) == floor64(1.3));
91}
92
93test "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
99test "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
105test "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
113test "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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4// TODO issue #393
5pub const fma = fma_workaround;
6
7pub 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
15fn 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
29fn 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
76const dd = struct { hi: f64, lo: f64, };
77
78fn 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
86fn 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
108fn 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
122fn 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
136test "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
141test "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
153test "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,89 +1,173 @@
1const 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
2const math = @import("index.zig");7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10// TODO issue #393
11pub const frexp = frexp_workaround;
312
4pub fn frexp(x: var, e: &i32) -> @typeOf(x) {13fn frexp_result(comptime T: type) -> type {
14 struct {
15 significand: T,
16 exponent: i32,
17 }
18}
19pub const frexp32_result = frexp_result(f32);
20pub const frexp64_result = frexp_result(f64);
21
22pub fn frexp_workaround(x: var) -> frexp_result(@typeOf(x)) {
5 const T = @typeOf(x);23 const T = @typeOf(x);
6 switch (T) {24 switch (T) {
7 f32 => frexp32(x, e),25 f32 => @inlineCall(frexp32, x),
8 f64 => frexp64(x, e),26 f64 => @inlineCall(frexp64, x),
9 else => @compileError("frexp not implemented for " ++ @typeName(T)),27 else => @compileError("frexp not implemented for " ++ @typeName(T)),
10 }28 }
11}29}
1230
13fn frexp32(x_: f32, e: &i32) -> f32 {31fn frexp32(x: f32) -> frexp32_result {
14 var x = x_;32 var result: frexp32_result = undefined;
33
15 var y = @bitCast(u32, x);34 var y = @bitCast(u32, x);
16 const ee = i32(y >> 23) & 0xFF;35 const e = i32(y >> 23) & 0xFF;
1736
18 if (ee == 0) {37 if (e == 0) {
19 if (x != 0) {38 if (x != 0) {
20 x = frexp32(x * 0x1.0p64, e);39 // subnormal
21 *e -= 64;40 result = frexp32(x * 0x1.0p64);
41 result.exponent -= 64;
22 } else {42 } else {
23 *e = 0;43 // frexp(+-0) = (+-0, 0)
44 result.significand = x;
45 result.exponent = 0;
24 }46 }
25 return x;47 return result;
26 } else if (ee == 0xFF) {48 } else if (e == 0xFF) {
27 return x;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 }
2960
30 *e = ee - 0x7E;61 result.exponent = e - 0x7E;
31 y &= 0x807FFFFF;62 y &= 0x807FFFFF;
32 y |= 0x3F000000;63 y |= 0x3F000000;
33 @bitCast(f32, y)64 result.significand = @bitCast(f32, y);
65 result
34}66}
3567
36fn frexp64(x_: f64, e: &i32) -> f64 {68fn frexp64(x: f64) -> frexp64_result {
37 var x = x_;69 var result: frexp64_result = undefined;
70
38 var y = @bitCast(u64, x);71 var y = @bitCast(u64, x);
39 const ee = i32(y >> 52) & 0x7FF;72 const e = i32(y >> 52) & 0x7FF;
4073
41 if (ee == 0) {74 if (e == 0) {
42 if (x != 0) {75 if (x != 0) {
43 x = frexp64(x * 0x1.0p64, e);76 // subnormal
44 *e -= 64;77 result = frexp64(x * 0x1.0p64);
78 result.exponent -= 64;
45 } else {79 } else {
46 *e = 0;80 // frexp(+-0) = (+-0, 0)
81 result.significand = x;
82 result.exponent = 0;
47 }83 }
48 return x;84 return result;
49 } else if (ee == 0x7FF) {85 } else if (e == 0x7FF) {
50 return x;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 }
5297
53 *e = ee - 0x3FE;98 result.exponent = e - 0x3FE;
54 y &= 0x800FFFFFFFFFFFFF;99 y &= 0x800FFFFFFFFFFFFF;
55 y |= 0x3FE0000000000000;100 y |= 0x3FE0000000000000;
56 @bitCast(f64, y)101 result.significand = @bitCast(f64, y);
102 result
57}103}
58104
59test "frexp" {105test "math.frexp" {
60 var i0: i32 = undefined;106 const a = frexp(f32(1.3));
61 var i1: i32 = undefined;107 const b = frexp32(1.3);
108 assert(a.significand == b.significand and a.exponent == b.exponent);
62109
63 assert(frexp(f32(1.3), &i0) == frexp32(1.3, &i1));110 const c = frexp(f64(1.3));
64 assert(frexp(f64(1.3), &i0) == frexp64(1.3, &i1));111 const d = frexp64(1.3);
112 assert(c.significand == d.significand and c.exponent == d.exponent);
65}113}
66114
67test "frexp32" {115test "math.frexp32" {
68 const epsilon = 0.000001;116 const epsilon = 0.000001;
69 var i: i32 = undefined;117 var r: frexp32_result = undefined;
70 var d: f32 = undefined;
71118
72 d = frexp32(1.3, &i);119 r = frexp32(1.3);
73 assert(math.approxEq(f32, d, 0.65, epsilon) and i == 1);120 assert(math.approxEq(f32, r.significand, 0.65, epsilon) and r.exponent == 1);
74121
75 d = frexp32(78.0234, &i);122 r = frexp32(78.0234);
76 assert(math.approxEq(f32, d, 0.609558, epsilon) and i == 7);123 assert(math.approxEq(f32, r.significand, 0.609558, epsilon) and r.exponent == 7);
77}124}
78125
79test "frexp64" {126test "math.frexp64" {
80 const epsilon = 0.000001;127 const epsilon = 0.000001;
81 var i: i32 = undefined;128 var r: frexp64_result = undefined;
82 var d: f64 = 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
137test "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
156test "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);
83167
84 d = frexp64(1.3, &i);168 r = frexp64(-math.inf(f64));
85 assert(math.approxEq(f64, d, 0.65, epsilon) and i == 1);169 assert(math.isNegativeInf(r.significand) and r.exponent == 0);
86170
87 d = frexp64(78.0234, &i);171 r = frexp64(math.nan(f64));
88 assert(math.approxEq(f64, d, 0.609558, epsilon) and i == 7);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
8const math = @import("index.zig");
9const assert = @import("../debug.zig").assert;
10
11// TODO issue #393
12pub const hypot = hypot_workaround;
13
14pub 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
22fn 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
57fn 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
66fn 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
118test "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
123test "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
135test "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
147test "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
156test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10// TODO issue #393
11pub const ilogb = ilogb_workaround;
12
13pub 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?
23const fp_ilogbnan = -1 - i32(@maxValue(u32) >> 1);
24const fp_ilogb0 = fp_ilogbnan;
25
26fn 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
62fn 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
97test "math.ilogb" {
98 assert(ilogb(f32(0.2)) == ilogb32(0.2));
99 assert(ilogb(f64(0.2)) == ilogb64(0.2));
100}
101
102test "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
111test "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
120test "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
127test "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,7 +1,189 @@
1const assert = @import("../debug.zig").assert;
2const builtin = @import("builtin");1const builtin = @import("builtin");
2const TypeId = builtin.TypeId;
3const assert = @import("../debug.zig").assert;
4
5pub const e = 2.7182818284590452354; // e
6pub const log2_e = 1.4426950408889634074; // log_2(e)
7pub const log10_e = 0.43429448190325182765; // log_10(e)
8pub const ln_2 = 0.69314718055994530942; // log_e(2)
9pub const ln_10 = 2.30258509299404568402; // log_e(10)
10pub const pi = 3.14159265358979323846; // pi
11pub const pi_2 = 1.57079632679489661923; // pi/2
12pub const pi_4 = 0.78539816339744830962; // pi/4
13pub const r1_pi = 0.31830988618379067154; // 1/pi
14pub const r2_pi = 0.63661977236758134308; // 2/pi
15pub const r2_sqrtpi = 1.12837916709551257390; // 2/sqrt(pi)
16pub const sqrt2 = 1.41421356237309504880; // sqrt(2)
17pub const r1_sqrt2 = 0.70710678118654752440; // 1/sqrt(2)
18
19// float.h details
20pub const f64_true_min = 4.94065645841246544177e-324;
21pub const f64_min = 2.22507385850720138309e-308;
22pub const f64_max = 1.79769313486231570815e+308;
23pub const f64_epsilon = 2.22044604925031308085e-16;
24pub const f64_toint = 1.0 / f64_epsilon;
25
26pub const f32_true_min = 1.40129846432481707092e-45;
27pub const f32_min = 1.17549435082228750797e-38;
28pub const f32_max = 3.40282346638528859812e+38;
29pub const f32_epsilon = 1.1920928955078125e-07;
30pub const f32_toint = 1.0 / f32_epsilon;
31
32pub const nan_u32 = u32(0x7F800001);
33pub const nan_f32 = @bitCast(f32, nan_u32);
34
35pub const inf_u32 = u32(0x7F800000);
36pub const inf_f32 = @bitCast(f32, inf_u32);
37
38pub const nan_u64 = u64(0x7FF << 52) | 1;
39pub const nan_f64 = @bitCast(f64, nan_u64);
40
41pub const inf_u64 = u64(0x7FF << 52);
42pub const inf_f64 = @bitCast(f64, inf_u64);
43
44pub const nan = @import("nan.zig").nan;
45pub const snan = @import("nan.zig").snan;
46pub const inf = @import("inf.zig").inf;
47
48pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool {
49 assert(@typeId(T) == TypeId.Float);
50 fabs(x - y) < epsilon
51}
352
53// TODO: Hide the following in an internal module.
54pub 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
73pub fn raiseInvalid() {
74 // Raise INVALID fpu exception
75}
76
77pub fn raiseUnderflow() {
78 // Raise UNDERFLOW fpu exception
79}
80
81pub fn raiseOverflow() {
82 // Raise OVERFLOW fpu exception
83}
84
85pub fn raiseInexact() {
86 // Raise INEXACT fpu exception
87}
88
89pub fn raiseDivByZero() {
90 // Raise INEXACT fpu exception
91}
92
93pub const isNan = @import("isnan.zig").isNan;
94pub const isSignalNan = @import("isnan.zig").isSignalNan;
95pub const fabs = @import("fabs.zig").fabs;
96pub const ceil = @import("ceil.zig").ceil;
97pub const floor = @import("floor.zig").floor;
98pub const trunc = @import("floor.zig").trunc;
99pub const round = @import("round.zig").round;
4pub const frexp = @import("frexp.zig").frexp;100pub const frexp = @import("frexp.zig").frexp;
101pub const frexp32_result = @import("frexp.zig").frexp32_result;
102pub const frexp64_result = @import("frexp.zig").frexp64_result;
103pub const modf = @import("modf.zig").modf;
104pub const modf32_result = @import("modf.zig").modf32_result;
105pub const modf64_result = @import("modf.zig").modf64_result;
106pub const copysign = @import("copysign.zig").copysign;
107pub const isFinite = @import("isfinite.zig").isFinite;
108pub const isInf = @import("isinf.zig").isInf;
109pub const isPositiveInf = @import("isinf.zig").isPositiveInf;
110pub const isNegativeInf = @import("isinf.zig").isNegativeInf;
111pub const isNormal = @import("isnormal.zig").isNormal;
112pub const signbit = @import("signbit.zig").signbit;
113pub const scalbn = @import("scalbn.zig").scalbn;
114pub const pow = @import("pow.zig").pow;
115pub const sqrt = @import("sqrt.zig").sqrt;
116pub const cbrt = @import("cbrt.zig").cbrt;
117pub const acos = @import("acos.zig").acos;
118pub const asin = @import("asin.zig").asin;
119pub const atan = @import("atan.zig").atan;
120pub const atan2 = @import("atan2.zig").atan2;
121pub const hypot = @import("hypot.zig").hypot;
122pub const exp = @import("exp.zig").exp;
123pub const exp2 = @import("exp2.zig").exp2;
124pub const expm1 = @import("expm1.zig").expm1;
125pub const ilogb = @import("ilogb.zig").ilogb;
126pub const ln = @import("ln.zig").ln;
127pub const log = @import("log.zig").log;
128pub const log2 = @import("log2.zig").log2;
129pub const log10 = @import("log10.zig").log10;
130pub const log1p = @import("log1p.zig").log1p;
131pub const fma = @import("fma.zig").fma;
132pub const asinh = @import("asinh.zig").asinh;
133pub const acosh = @import("acosh.zig").acosh;
134pub const atanh = @import("atanh.zig").atanh;
135pub const sinh = @import("sinh.zig").sinh;
136pub const cosh = @import("cosh.zig").cosh;
137pub const tanh = @import("tanh.zig").tanh;
138pub const cos = @import("cos.zig").cos;
139pub const sin = @import("sin.zig").sin;
140pub const tan = @import("tan.zig").tan;
141
142test "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
5187
6pub const Cmp = enum {188pub const Cmp = enum {
7 Less,189 Less,
...@@ -66,25 +248,6 @@ fn testOverflow() {...@@ -66,25 +248,6 @@ fn testOverflow() {
66}248}
67249
68250
69pub 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
88error Overflow;251error Overflow;
89pub fn absInt(x: var) -> %@typeOf(x) {252pub fn absInt(x: var) -> %@typeOf(x) {
90 const T = @typeOf(x);253 const T = @typeOf(x);
...@@ -244,92 +407,6 @@ fn testRem() {...@@ -244,92 +407,6 @@ fn testRem() {
244 if (rem(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);407 if (rem(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
245}408}
246409
247fn 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
262pub 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
271fn 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
292fn 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
321test "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/// Returns the absolute value of the integer parameter.410/// Returns the absolute value of the integer parameter.
334/// Result is an unsigned integer.411/// Result is an unsigned integer.
335pub fn absCast(x: var) -> @IntType(false, @typeOf(x).bit_count) {412pub fn absCast(x: var) -> @IntType(false, @typeOf(x).bit_count) {
...@@ -377,13 +454,3 @@ test "math.negateCast" {...@@ -377,13 +454,3 @@ test "math.negateCast" {
377454
378 if (negateCast(u32(@maxValue(i32) + 10))) |_| unreachable else |err| assert(err == error.Overflow);455 if (negateCast(u32(@maxValue(i32) + 10))) |_| unreachable else |err| assert(err == error.Overflow);
379}456}
380
381test "math" {
382 _ = @import("frexp.zig");
383}
384
385
386pub 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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4pub 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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4pub 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
21test "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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4pub 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
21pub 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
36pub 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
51test "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
62test "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
73test "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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4pub 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.
23pub fn isSignalNan(x: var) -> bool {
24 isNan(x)
25}
26
27test "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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4pub 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
21test "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
8const math = @import("index.zig");
9const assert = @import("../debug.zig").assert;
10
11pub const ln = ln_workaround;
12
13pub 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
22fn 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
76fn 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
134test "math.ln" {
135 assert(ln(f32(0.2)) == lnf(0.2));
136 assert(ln(f64(0.2)) == lnd(0.2));
137}
138
139test "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
150test "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
161test "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
168test "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 @@
1const math = @import("index.zig");
2const builtin = @import("builtin");
3const assert = @import("../debug.zig").assert;
4
5// TODO issue #393
6pub const log = log_workaround;
7
8pub 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
42test "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
50test "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
58test "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
8const math = @import("index.zig");
9const assert = @import("../debug.zig").assert;
10
11// TODO issue #393
12pub const log10 = log10_workaround;
13
14pub 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
23fn 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
83fn 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
160test "math.log10" {
161 assert(log10(f32(0.2)) == log10_32(0.2));
162 assert(log10(f64(0.2)) == log10_64(0.2));
163}
164
165test "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
176test "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
187test "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
194test "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
9const math = @import("index.zig");
10const assert = @import("../debug.zig").assert;
11
12// TODO issue #393
13pub const log1p = log1p_workaround;
14
15pub 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
24fn 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
100fn 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
181test "math.log1p" {
182 assert(log1p(f32(0.0)) == log1p_32(0.0));
183 assert(log1p(f64(0.0)) == log1p_64(0.0));
184}
185
186test "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
198test "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
210test "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
219test "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
8const math = @import("index.zig");
9const assert = @import("../debug.zig").assert;
10
11// TODO issue #393
12pub const log2 = log2_workaround;
13
14pub 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
23fn 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
79fn 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
152test "math.log2" {
153 assert(log2(f32(0.2)) == log2_32(0.2));
154 assert(log2(f64(0.2)) == log2_64(0.2));
155}
156
157test "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
167test "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
177test "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
184test "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
6const math = @import("index.zig");
7const assert = @import("../debug.zig").assert;
8
9// TODO issue #393
10pub const modf = modf_workaround;
11
12fn modf_result(comptime T: type) -> type {
13 struct {
14 fpart: T,
15 ipart: T,
16 }
17}
18pub const modf32_result = modf_result(f32);
19pub const modf64_result = modf_result(f64);
20
21pub 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
30fn 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
75fn 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
119test "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
130test "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
155test "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
180test "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
193test "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 @@
1const math = @import("index.zig");
2
3pub const nan = nan_workaround;
4
5pub 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
13pub 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.
17pub 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
24const math = @import("index.zig");
25const assert = @import("../debug.zig").assert;
26
27// TODO issue #393
28pub const pow = pow_workaround;
29
30// This implementation is taken from the go stlib, musl is a bit more complex.
31pub 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
174fn isOddInteger(x: f64) -> bool {
175 const r = math.modf(x);
176 r.fpart == 0.0 and i64(r.ipart) & 1 == 1
177}
178
179test "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
197test "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
7const builtin = @import("builtin");
8const assert = @import("../debug.zig").assert;
9const math = @import("index.zig");
10
11// TODO issue #393
12pub const round = round_workaround;
13
14pub 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
23fn 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
60fn 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
97test "math.round" {
98 assert(round(f32(1.3)) == round32(1.3));
99 assert(round(f64(1.3)) == round64(1.3));
100}
101
102test "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
109test "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
116test "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
124test "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 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4// TODO issue #393
5pub const scalbn = scalbn_workaround;
6
7pub 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
16fn 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
46fn 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
76test "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
81test "math.scalbn32" {
82 assert(scalbn32(1.5, 4) == 24.0);
83}
84
85test "math.scalbn64" {
86 assert(scalbn64(1.5, 4) == 24.0);
87}
std/math/signbit.zig created+39
...@@ -0,0 +1,39 @@
1const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;
3
4// TODO issue #393
5pub const signbit = signbit_workaround;
6
7pub 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
16fn signbit32(x: f32) -> bool {
17 const bits = @bitCast(u32, x);
18 bits >> 31 != 0
19}
20
21fn signbit64(x: f64) -> bool {
22 const bits = @bitCast(u64, x);
23 bits >> 63 != 0
24}
25
26test "math.signbit" {
27 assert(signbit(f32(4.0)) == signbit32(4.0));
28 assert(signbit(f64(4.0)) == signbit64(4.0));
29}
30
31test "math.signbit32" {
32 assert(!signbit32(4.0));
33 assert(signbit32(-3.0));
34}
35
36test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10// TODO issue #393
11pub const sin = sin_workaround;
12
13pub 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
23const S0 = 1.58962301576546568060E-10;
24const S1 = -2.50507477628578072866E-8;
25const S2 = 2.75573136213857245213E-6;
26const S3 = -1.98412698295895385996E-4;
27const S4 = 8.33333333332211858878E-3;
28const S5 = -1.66666666666666307295E-1;
29
30// cos polynomial coeffiecients
31const C0 = -1.13585365213876817300E-11;
32const C1 = 2.08757008419747316778E-9;
33const C2 = -2.75573141792967388112E-7;
34const C3 = 2.48015872888517045348E-5;
35const C4 = -1.38888888888730564116E-3;
36const 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.
41fn 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
95fn 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
147test "math.sin" {
148 assert(sin(f32(0.0)) == sin32(0.0));
149 assert(sin(f64(0.0)) == sin64(0.0));
150}
151
152test "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
163test "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
174test "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
182test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9const expo2 = @import("expo2.zig").expo2;
10
11// TODO issue #393
12pub const sinh = sinh_workaround;
13
14pub 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)
26fn 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
57fn 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
91test "math.sinh" {
92 assert(sinh(f32(1.5)) == sinh32(1.5));
93 assert(sinh(f64(1.5)) == sinh64(1.5));
94}
95
96test "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
105test "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
114test "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
122test "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
8const math = @import("index.zig");
9const assert = @import("../debug.zig").assert;
10
11// TODO issue #393
12pub const sqrt = sqrt_workaround;
13
14pub 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
23fn 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.
102fn 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
232test "math.sqrt" {
233 assert(sqrt(f32(0.0)) == sqrt32(0.0));
234 assert(sqrt(f64(0.0)) == sqrt64(0.0));
235}
236
237test "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
251test "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
265test "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
273test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10pub const tan = tan_workaround;
11
12pub 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
21const Tp0 = -1.30936939181383777646E4;
22const Tp1 = 1.15351664838587416140E6;
23const Tp2 = -1.79565251976484877988E7;
24
25const Tq1 = 1.36812963470692954678E4;
26const Tq2 = -1.32089234440210967447E6;
27const Tq3 = 2.50083801823357915839E7;
28const 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.
33fn 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
84fn 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
133test "math.tan" {
134 assert(tan(f32(0.0)) == tan32(0.0));
135 assert(tan(f64(0.0)) == tan64(0.0));
136}
137
138test "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
149test "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
160test "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
168test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9const expo2 = @import("expo2.zig").expo2;
10
11// TODO issue #393
12pub const tanh = tanh_workaround;
13
14pub 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)
26fn 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
70fn 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
115test "math.tanh" {
116 assert(tanh(f32(1.5)) == tanh32(1.5));
117 assert(tanh(f64(1.5)) == tanh64(1.5));
118}
119
120test "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
130test "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
140test "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
148test "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
7const math = @import("index.zig");
8const assert = @import("../debug.zig").assert;
9
10pub const trunc = trunc_workaround;
11
12pub 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
21fn 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
42fn 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
63test "math.trunc" {
64 assert(trunc(f32(1.3)) == trunc32(1.3));
65 assert(trunc(f64(1.3)) == trunc64(1.3));
66}
67
68test "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
74test "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
80test "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
88test "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,7 +60,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
60 if (ex == 0) {60 if (ex == 0) {
61 i = ux <<% exp_bits;61 i = ux <<% exp_bits;
62 while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<%= 1}) {}62 while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<%= 1}) {}
63 ux <<%= twosComplementCast(uint, -ex + 1);63 ux <<%= @bitCast(u32, -ex + 1);
64 } else {64 } else {
65 ux &= @maxValue(uint) >> exp_bits;65 ux &= @maxValue(uint) >> exp_bits;
66 ux |= 1 <<% digits;66 ux |= 1 <<% digits;
...@@ -68,7 +68,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {...@@ -68,7 +68,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
68 if (ey == 0) {68 if (ey == 0) {
69 i = uy <<% exp_bits;69 i = uy <<% exp_bits;
70 while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<%= 1}) {}70 while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<%= 1}) {}
71 uy <<= twosComplementCast(uint, -ey + 1);71 uy <<= @bitCast(u32, -ey + 1);
72 } else {72 } else {
73 uy &= @maxValue(uint) >> exp_bits;73 uy &= @maxValue(uint) >> exp_bits;
74 uy |= 1 <<% digits;74 uy |= 1 <<% digits;
...@@ -95,9 +95,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {...@@ -95,9 +95,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
95 // scale result up95 // scale result up
96 if (ex > 0) {96 if (ex > 0) {
97 ux -%= 1 <<% digits;97 ux -%= 1 <<% digits;
98 ux |= twosComplementCast(uint, ex) <<% digits;98 ux |= @bitCast(u32, ex) <<% digits;
99 } else {99 } else {
100 ux >>= twosComplementCast(uint, -ex + 1);100 ux >>= @bitCast(u32, -ex + 1);
101 }101 }
102 if (T == f32) {102 if (T == f32) {
103 ux |= sx;103 ux |= sx;
...@@ -116,8 +116,3 @@ fn isNan(comptime T: type, bits: T) -> bool {...@@ -116,8 +116,3 @@ fn isNan(comptime T: type, bits: T) -> bool {
116 unreachable;116 unreachable;
117 }117 }
118}118}
119
120// TODO this should be a builtin function and it shouldn't do a ptr cast
121fn twosComplementCast(comptime T: type, src: var) -> T {
122 return *@ptrCast(&const @IntType(T.is_signed, @typeOf(src).bit_count), &src);
123}