authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2017-06-16 20:26:10+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2017-06-16 20:32:31+12:00
log4c16f9a3c35b23b9917f2a27b91ba8cd20e6fd82
tree778f0f06734f7dc17e9269ee1cf5b513f7b504c0
parent865b53f2860405a718262abf9a794d2bf9529dbc

Add math library

This covers the majority of the functions as covered by the C99 specification for a math library. Code is adapted primarily from musl libc, with the pow and standard trigonometric functions adapted from the Go stdlib. Changes: - Remove assert expose in index and import as needed. - Add float log function and merge with existing base 2 integer implementation. See https://github.com/tiehuis/zig-fmath. See #374.

47 files changed, 6150 insertions(+), 174 deletions(-)

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