authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-05-01 18:12:16+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-05-01 18:13:33+12:00
logf94964cd057ace39caa05c10d9626ac6a4beb23b
tree14d6ac67d878caccbb2ce5217de420f57f9c094a
parent7bbc8eb16c44d0aa9fee72fbe5607b44468a2872

std.math: Add upstream changes/fixes and simplify go derived code

This also starts the documentation effort for the math/ subdirectory. The intent is to use this as a somewhat representative test-case for any work on the documentation generator.

5 files changed, 214 insertions(+), 352 deletions(-)

std/math/cos.zig+46-100
...@@ -1,18 +1,23 @@...@@ -1,18 +1,23 @@
1// Special Cases:1// Ported from go, which is licensed under a BSD-3 license.
2// https://golang.org/LICENSE
2//3//
3// - cos(+-inf) = nan4// https://golang.org/src/math/sin.go
4// - cos(nan) = nan
55
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const std = @import("../std.zig");7const std = @import("../std.zig");
8const math = std.math;8const math = std.math;
9const expect = std.testing.expect;9const expect = std.testing.expect;
1010
11/// Returns the cosine of the radian value x.
12///
13/// Special Cases:
14/// - cos(+-inf) = nan
15/// - cos(nan) = nan
11pub fn cos(x: var) @typeOf(x) {16pub fn cos(x: var) @typeOf(x) {
12 const T = @typeOf(x);17 const T = @typeOf(x);
13 return switch (T) {18 return switch (T) {
14 f32 => cos32(x),19 f32 => cos_(f32, x),
15 f64 => cos64(x),20 f64 => cos_(f64, x),
16 else => @compileError("cos not implemented for " ++ @typeName(T)),21 else => @compileError("cos not implemented for " ++ @typeName(T)),
17 };22 };
18}23}
...@@ -33,78 +38,24 @@ const C3 = 2.48015872888517045348E-5;...@@ -33,78 +38,24 @@ const C3 = 2.48015872888517045348E-5;
33const C4 = -1.38888888888730564116E-3;38const C4 = -1.38888888888730564116E-3;
34const C5 = 4.16666666666665929218E-2;39const C5 = 4.16666666666665929218E-2;
3540
36// NOTE: This is taken from the go stdlib. The musl implementation is much more complex.41const pi4a = 7.85398125648498535156e-1;
37//42const pi4b = 3.77489470793079817668E-8;
38// This may have slight differences on some edge cases and may need to replaced if so.43const pi4c = 2.69515142907905952645E-15;
39fn cos32(x_: f32) f32 {44const m4pi = 1.273239544735162542821171882678754627704620361328125;
40 const pi4a = 7.85398125648498535156e-1;
41 const pi4b = 3.77489470793079817668E-8;
42 const pi4c = 2.69515142907905952645E-15;
43 const m4pi = 1.273239544735162542821171882678754627704620361328125;
44
45 var x = x_;
46 if (math.isNan(x) or math.isInf(x)) {
47 return math.nan(f32);
48 }
4945
50 var sign = false;46fn cos_(comptime T: type, x_: T) T {
51 if (x < 0) {47 const I = @IntType(true, T.bit_count);
52 x = -x;
53 }
54
55 var y = math.floor(x * m4pi);
56 var j = @floatToInt(i64, y);
57
58 if (j & 1 == 1) {
59 j += 1;
60 y += 1;
61 }
62
63 j &= 7;
64 if (j > 3) {
65 j -= 4;
66 sign = !sign;
67 }
68 if (j > 1) {
69 sign = !sign;
70 }
71
72 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
73 const w = z * z;
74
75 const r = r: {
76 if (j == 1 or j == 2) {
77 break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
78 } else {
79 break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
80 }
81 };
82
83 if (sign) {
84 return -r;
85 } else {
86 return r;
87 }
88}
89
90fn cos64(x_: f64) f64 {
91 const pi4a = 7.85398125648498535156e-1;
92 const pi4b = 3.77489470793079817668E-8;
93 const pi4c = 2.69515142907905952645E-15;
94 const m4pi = 1.273239544735162542821171882678754627704620361328125;
9548
96 var x = x_;49 var x = x_;
97 if (math.isNan(x) or math.isInf(x)) {50 if (math.isNan(x) or math.isInf(x)) {
98 return math.nan(f64);51 return math.nan(f32);
99 }52 }
10053
101 var sign = false;54 var sign = false;
102 if (x < 0) {55 x = math.fabs(x);
103 x = -x;
104 }
10556
106 var y = math.floor(x * m4pi);57 var y = math.floor(x * m4pi);
107 var j = @floatToInt(i64, y);58 var j = @floatToInt(I, y);
10859
109 if (j & 1 == 1) {60 if (j & 1 == 1) {
110 j += 1;61 j += 1;
...@@ -123,56 +74,51 @@ fn cos64(x_: f64) f64 {...@@ -123,56 +74,51 @@ fn cos64(x_: f64) f64 {
123 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;74 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
124 const w = z * z;75 const w = z * z;
12576
126 const r = r: {77 const r = if (j == 1 or j == 2)
127 if (j == 1 or j == 2) {78 z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
128 break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));79 else
129 } else {80 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
130 break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
131 }
132 };
13381
134 if (sign) {82 return if (sign) -r else r;
135 return -r;
136 } else {
137 return r;
138 }
139}83}
14084
141test "math.cos" {85test "math.cos" {
142 expect(cos(f32(0.0)) == cos32(0.0));86 expect(cos(f32(0.0)) == cos_(f32, 0.0));
143 expect(cos(f64(0.0)) == cos64(0.0));87 expect(cos(f64(0.0)) == cos_(f64, 0.0));
144}88}
14589
146test "math.cos32" {90test "math.cos32" {
147 const epsilon = 0.000001;91 const epsilon = 0.000001;
14892
149 expect(math.approxEq(f32, cos32(0.0), 1.0, epsilon));93 expect(math.approxEq(f32, cos_(f32, 0.0), 1.0, epsilon));
150 expect(math.approxEq(f32, cos32(0.2), 0.980067, epsilon));94 expect(math.approxEq(f32, cos_(f32, 0.2), 0.980067, epsilon));
151 expect(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon));95 expect(math.approxEq(f32, cos_(f32, 0.8923), 0.627623, epsilon));
152 expect(math.approxEq(f32, cos32(1.5), 0.070737, epsilon));96 expect(math.approxEq(f32, cos_(f32, 1.5), 0.070737, epsilon));
153 expect(math.approxEq(f32, cos32(37.45), 0.969132, epsilon));97 expect(math.approxEq(f32, cos_(f32, -1.5), 0.070737, epsilon));
154 expect(math.approxEq(f32, cos32(89.123), 0.400798, epsilon));98 expect(math.approxEq(f32, cos_(f32, 37.45), 0.969132, epsilon));
99 expect(math.approxEq(f32, cos_(f32, 89.123), 0.400798, epsilon));
155}100}
156101
157test "math.cos64" {102test "math.cos64" {
158 const epsilon = 0.000001;103 const epsilon = 0.000001;
159104
160 expect(math.approxEq(f64, cos64(0.0), 1.0, epsilon));105 expect(math.approxEq(f64, cos_(f64, 0.0), 1.0, epsilon));
161 expect(math.approxEq(f64, cos64(0.2), 0.980067, epsilon));106 expect(math.approxEq(f64, cos_(f64, 0.2), 0.980067, epsilon));
162 expect(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon));107 expect(math.approxEq(f64, cos_(f64, 0.8923), 0.627623, epsilon));
163 expect(math.approxEq(f64, cos64(1.5), 0.070737, epsilon));108 expect(math.approxEq(f64, cos_(f64, 1.5), 0.070737, epsilon));
164 expect(math.approxEq(f64, cos64(37.45), 0.969132, epsilon));109 expect(math.approxEq(f64, cos_(f64, -1.5), 0.070737, epsilon));
165 expect(math.approxEq(f64, cos64(89.123), 0.40080, epsilon));110 expect(math.approxEq(f64, cos_(f64, 37.45), 0.969132, epsilon));
111 expect(math.approxEq(f64, cos_(f64, 89.123), 0.40080, epsilon));
166}112}
167113
168test "math.cos32.special" {114test "math.cos32.special" {
169 expect(math.isNan(cos32(math.inf(f32))));115 expect(math.isNan(cos_(f32, math.inf(f32))));
170 expect(math.isNan(cos32(-math.inf(f32))));116 expect(math.isNan(cos_(f32, -math.inf(f32))));
171 expect(math.isNan(cos32(math.nan(f32))));117 expect(math.isNan(cos_(f32, math.nan(f32))));
172}118}
173119
174test "math.cos64.special" {120test "math.cos64.special" {
175 expect(math.isNan(cos64(math.inf(f64))));121 expect(math.isNan(cos_(f64, math.inf(f64))));
176 expect(math.isNan(cos64(-math.inf(f64))));122 expect(math.isNan(cos_(f64, -math.inf(f64))));
177 expect(math.isNan(cos64(math.nan(f64))));123 expect(math.isNan(cos_(f64, math.nan(f64))));
178}124}
std/math/fma.zig+9-1
...@@ -1,7 +1,14 @@...@@ -1,7 +1,14 @@
1// Ported from musl, which is licensed under the MIT license:
2// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
3//
4// https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c
6
1const std = @import("../std.zig");7const std = @import("../std.zig");
2const math = std.math;8const math = std.math;
3const expect = std.testing.expect;9const expect = std.testing.expect;
410
11/// Returns x * y + z with a single rounding error.
5pub fn fma(comptime T: type, x: T, y: T, z: T) T {12pub fn fma(comptime T: type, x: T, y: T, z: T) T {
6 return switch (T) {13 return switch (T) {
7 f32 => fma32(x, y, z),14 f32 => fma32(x, y, z),
...@@ -16,7 +23,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 {...@@ -16,7 +23,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 {
16 const u = @bitCast(u64, xy_z);23 const u = @bitCast(u64, xy_z);
17 const e = (u >> 52) & 0x7FF;24 const e = (u >> 52) & 0x7FF;
1825
19 if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) {26 if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or (xy_z - xy == z and xy_z - z == xy)) {
20 return @floatCast(f32, xy_z);27 return @floatCast(f32, xy_z);
21 } else {28 } else {
22 // TODO: Handle inexact case with double-rounding29 // TODO: Handle inexact case with double-rounding
...@@ -24,6 +31,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 {...@@ -24,6 +31,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 {
24 }31 }
25}32}
2633
34// NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately.
27fn fma64(x: f64, y: f64, z: f64) f64 {35fn fma64(x: f64, y: f64, z: f64) f64 {
28 if (!math.isFinite(x) or !math.isFinite(y)) {36 if (!math.isFinite(x) or !math.isFinite(y)) {
29 return x * y + z;37 return x * y + z;
std/math/pow.zig+57-39
...@@ -1,32 +1,36 @@...@@ -1,32 +1,36 @@
1// Special Cases:1// Ported from go, which is licensed under a BSD-3 license.
2// https://golang.org/LICENSE
2//3//
3// pow(x, +-0) = 1 for any x4// https://golang.org/src/math/pow.go
4// pow(1, y) = 1 for any y
5// pow(x, 1) = x for any x
6// pow(nan, y) = nan
7// pow(x, nan) = nan
8// pow(+-0, y) = +-inf for y an odd integer < 0
9// pow(+-0, -inf) = +inf
10// pow(+-0, +inf) = +0
11// pow(+-0, y) = +inf for finite y < 0 and not an odd integer
12// pow(+-0, y) = +-0 for y an odd integer > 0
13// pow(+-0, y) = +0 for finite y > 0 and not an odd integer
14// pow(-1, +-inf) = 1
15// pow(x, +inf) = +inf for |x| > 1
16// pow(x, -inf) = +0 for |x| > 1
17// pow(x, +inf) = +0 for |x| < 1
18// pow(x, -inf) = +inf for |x| < 1
19// pow(+inf, y) = +inf for y > 0
20// pow(+inf, y) = +0 for y < 0
21// pow(-inf, y) = pow(-0, -y)
22// pow(x, y) = nan for finite x < 0 and finite non-integer y
235
24const builtin = @import("builtin");6const builtin = @import("builtin");
25const std = @import("../std.zig");7const std = @import("../std.zig");
26const math = std.math;8const math = std.math;
27const expect = std.testing.expect;9const expect = std.testing.expect;
2810
29// This implementation is taken from the go stlib, musl is a bit more complex.11/// Returns x raised to the power of y (x^y).
12///
13/// Special Cases:
14/// - pow(x, +-0) = 1 for any x
15/// - pow(1, y) = 1 for any y
16/// - pow(x, 1) = x for any x
17/// - pow(nan, y) = nan
18/// - pow(x, nan) = nan
19/// - pow(+-0, y) = +-inf for y an odd integer < 0
20/// - pow(+-0, -inf) = +inf
21/// - pow(+-0, +inf) = +0
22/// - pow(+-0, y) = +inf for finite y < 0 and not an odd integer
23/// - pow(+-0, y) = +-0 for y an odd integer > 0
24/// - pow(+-0, y) = +0 for finite y > 0 and not an odd integer
25/// - pow(-1, +-inf) = 1
26/// - pow(x, +inf) = +inf for |x| > 1
27/// - pow(x, -inf) = +0 for |x| > 1
28/// - pow(x, +inf) = +0 for |x| < 1
29/// - pow(x, -inf) = +inf for |x| < 1
30/// - pow(+inf, y) = +inf for y > 0
31/// - pow(+inf, y) = +0 for y < 0
32/// - pow(-inf, y) = pow(-0, -y)
33/// - pow(x, y) = nan for finite x < 0 and finite non-integer y
30pub fn pow(comptime T: type, x: T, y: T) T {34pub fn pow(comptime T: type, x: T, y: T) T {
31 if (@typeInfo(T) == builtin.TypeId.Int) {35 if (@typeInfo(T) == builtin.TypeId.Int) {
32 return math.powi(T, x, y) catch unreachable;36 return math.powi(T, x, y) catch unreachable;
...@@ -53,15 +57,6 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -53,15 +57,6 @@ pub fn pow(comptime T: type, x: T, y: T) T {
53 return x;57 return x;
54 }58 }
5559
56 // special case sqrt
57 if (y == 0.5) {
58 return math.sqrt(x);
59 }
60
61 if (y == -0.5) {
62 return 1 / math.sqrt(x);
63 }
64
65 if (x == 0) {60 if (x == 0) {
66 if (y < 0) {61 if (y < 0) {
67 // pow(+-0, y) = +- 0 for y an odd integer62 // pow(+-0, y) = +- 0 for y an odd integer
...@@ -112,14 +107,16 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -112,14 +107,16 @@ pub fn pow(comptime T: type, x: T, y: T) T {
112 }107 }
113 }108 }
114109
115 var ay = y;110 // special case sqrt
116 var flip = false;111 if (y == 0.5) {
117 if (ay < 0) {112 return math.sqrt(x);
118 ay = -ay;113 }
119 flip = true;114
115 if (y == -0.5) {
116 return 1 / math.sqrt(x);
120 }117 }
121118
122 const r1 = math.modf(ay);119 const r1 = math.modf(math.fabs(y));
123 var yi = r1.ipart;120 var yi = r1.ipart;
124 var yf = r1.fpart;121 var yf = r1.fpart;
125122
...@@ -148,8 +145,18 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -148,8 +145,18 @@ pub fn pow(comptime T: type, x: T, y: T) T {
148 var xe = r2.exponent;145 var xe = r2.exponent;
149 var x1 = r2.significand;146 var x1 = r2.significand;
150147
151 var i = @floatToInt(i32, yi);148 var i = @floatToInt(@IntType(true, T.bit_count), yi);
152 while (i != 0) : (i >>= 1) {149 while (i != 0) : (i >>= 1) {
150 const overflow_shift = math.floatExponentBits(T) + 1;
151 if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) {
152 // catch xe before it overflows the left shift below
153 // Since i != 0 it has at least one bit still set, so ae will accumulate xe
154 // on at least one more iteration, ae += xe is a lower bound on ae
155 // the lower bound on ae exceeds the size of a float exp
156 // so the final call to Ldexp will produce under/overflow (0/Inf)
157 ae += xe;
158 break;
159 }
153 if (i & 1 == 1) {160 if (i & 1 == 1) {
154 a1 *= x1;161 a1 *= x1;
155 ae += xe;162 ae += xe;
...@@ -163,7 +170,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -163,7 +170,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
163 }170 }
164171
165 // a *= a1 * 2^ae172 // a *= a1 * 2^ae
166 if (flip) {173 if (y < 0) {
167 a1 = 1 / a1;174 a1 = 1 / a1;
168 ae = -ae;175 ae = -ae;
169 }176 }
...@@ -202,6 +209,9 @@ test "math.pow.special" {...@@ -202,6 +209,9 @@ test "math.pow.special" {
202 expect(pow(f32, 45, 1.0) == 45);209 expect(pow(f32, 45, 1.0) == 45);
203 expect(pow(f32, -45, 1.0) == -45);210 expect(pow(f32, -45, 1.0) == -45);
204 expect(math.isNan(pow(f32, math.nan(f32), 5.0)));211 expect(math.isNan(pow(f32, math.nan(f32), 5.0)));
212 expect(math.isPositiveInf(pow(f32, -math.inf(f32), 0.5)));
213 expect(math.isPositiveInf(pow(f32, -0, -0.5)));
214 expect(pow(f32, -0, 0.5) == 0);
205 expect(math.isNan(pow(f32, 5.0, math.nan(f32))));215 expect(math.isNan(pow(f32, 5.0, math.nan(f32))));
206 expect(math.isPositiveInf(pow(f32, 0.0, -1.0)));216 expect(math.isPositiveInf(pow(f32, 0.0, -1.0)));
207 //expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required?217 //expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required?
...@@ -232,3 +242,11 @@ test "math.pow.special" {...@@ -232,3 +242,11 @@ test "math.pow.special" {
232 expect(math.isNan(pow(f32, -1.0, 1.2)));242 expect(math.isNan(pow(f32, -1.0, 1.2)));
233 expect(math.isNan(pow(f32, -12.4, 78.5)));243 expect(math.isNan(pow(f32, -12.4, 78.5)));
234}244}
245
246test "math.pow.overflow" {
247 expect(math.isPositiveInf(pow(f64, 2, 1 << 32)));
248 expect(pow(f64, 2, -(1 << 32)) == 0);
249 expect(math.isNegativeInf(pow(f64, -2, (1 << 32) + 1)));
250 expect(pow(f64, 0.5, 1 << 45) == 0);
251 expect(math.isPositiveInf(pow(f64, 0.5, -(1 << 45))));
252}
std/math/sin.zig+52-108
...@@ -1,19 +1,24 @@...@@ -1,19 +1,24 @@
1// Special Cases:1// Ported from go, which is licensed under a BSD-3 license.
2// https://golang.org/LICENSE
2//3//
3// - sin(+-0) = +-04// https://golang.org/src/math/sin.go
4// - sin(+-inf) = nan
5// - sin(nan) = nan
65
7const builtin = @import("builtin");6const builtin = @import("builtin");
8const std = @import("../std.zig");7const std = @import("../std.zig");
9const math = std.math;8const math = std.math;
10const expect = std.testing.expect;9const expect = std.testing.expect;
1110
11/// Returns the sine of the radian value x.
12///
13/// Special Cases:
14/// - sin(+-0) = +-0
15/// - sin(+-inf) = nan
16/// - sin(nan) = nan
12pub fn sin(x: var) @typeOf(x) {17pub fn sin(x: var) @typeOf(x) {
13 const T = @typeOf(x);18 const T = @typeOf(x);
14 return switch (T) {19 return switch (T) {
15 f32 => sin32(x),20 f32 => sin_(T, x),
16 f64 => sin64(x),21 f64 => sin_(T, x),
17 else => @compileError("sin not implemented for " ++ @typeName(T)),22 else => @compileError("sin not implemented for " ++ @typeName(T)),
18 };23 };
19}24}
...@@ -34,83 +39,27 @@ const C3 = 2.48015872888517045348E-5;...@@ -34,83 +39,27 @@ const C3 = 2.48015872888517045348E-5;
34const C4 = -1.38888888888730564116E-3;39const C4 = -1.38888888888730564116E-3;
35const C5 = 4.16666666666665929218E-2;40const C5 = 4.16666666666665929218E-2;
3641
37// NOTE: This is taken from the go stdlib. The musl implementation is much more complex.42const pi4a = 7.85398125648498535156e-1;
38//43const pi4b = 3.77489470793079817668E-8;
39// This may have slight differences on some edge cases and may need to replaced if so.44const pi4c = 2.69515142907905952645E-15;
40fn sin32(x_: f32) f32 {45const m4pi = 1.273239544735162542821171882678754627704620361328125;
41 const pi4a = 7.85398125648498535156e-1;
42 const pi4b = 3.77489470793079817668E-8;
43 const pi4c = 2.69515142907905952645E-15;
44 const m4pi = 1.273239544735162542821171882678754627704620361328125;
45
46 var x = x_;
47 if (x == 0 or math.isNan(x)) {
48 return x;
49 }
50 if (math.isInf(x)) {
51 return math.nan(f32);
52 }
53
54 var sign = false;
55 if (x < 0) {
56 x = -x;
57 sign = true;
58 }
59
60 var y = math.floor(x * m4pi);
61 var j = @floatToInt(i64, y);
62
63 if (j & 1 == 1) {
64 j += 1;
65 y += 1;
66 }
6746
68 j &= 7;47fn sin_(comptime T: type, x_: T) T {
69 if (j > 3) {48 const I = @IntType(true, T.bit_count);
70 j -= 4;
71 sign = !sign;
72 }
73
74 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
75 const w = z * z;
76
77 const r = r: {
78 if (j == 1 or j == 2) {
79 break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
80 } else {
81 break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
82 }
83 };
84
85 if (sign) {
86 return -r;
87 } else {
88 return r;
89 }
90}
91
92fn sin64(x_: f64) f64 {
93 const pi4a = 7.85398125648498535156e-1;
94 const pi4b = 3.77489470793079817668E-8;
95 const pi4c = 2.69515142907905952645E-15;
96 const m4pi = 1.273239544735162542821171882678754627704620361328125;
9749
98 var x = x_;50 var x = x_;
99 if (x == 0 or math.isNan(x)) {51 if (x == 0 or math.isNan(x)) {
100 return x;52 return x;
101 }53 }
102 if (math.isInf(x)) {54 if (math.isInf(x)) {
103 return math.nan(f64);55 return math.nan(T);
104 }56 }
10557
106 var sign = false;58 var sign = x < 0;
107 if (x < 0) {59 x = math.fabs(x);
108 x = -x;
109 sign = true;
110 }
11160
112 var y = math.floor(x * m4pi);61 var y = math.floor(x * m4pi);
113 var j = @floatToInt(i64, y);62 var j = @floatToInt(I, y);
11463
115 if (j & 1 == 1) {64 if (j & 1 == 1) {
116 j += 1;65 j += 1;
...@@ -126,61 +75,56 @@ fn sin64(x_: f64) f64 {...@@ -126,61 +75,56 @@ fn sin64(x_: f64) f64 {
126 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;75 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
127 const w = z * z;76 const w = z * z;
12877
129 const r = r: {78 const r = if (j == 1 or j == 2)
130 if (j == 1 or j == 2) {79 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
131 break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));80 else
132 } else {81 z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
133 break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
134 }
135 };
13682
137 if (sign) {83 return if (sign) -r else r;
138 return -r;
139 } else {
140 return r;
141 }
142}84}
14385
144test "math.sin" {86test "math.sin" {
145 expect(sin(f32(0.0)) == sin32(0.0));87 expect(sin(f32(0.0)) == sin_(f32, 0.0));
146 expect(sin(f64(0.0)) == sin64(0.0));88 expect(sin(f64(0.0)) == sin_(f64, 0.0));
147 expect(comptime (math.sin(f64(2))) == math.sin(f64(2)));89 expect(comptime (math.sin(f64(2))) == math.sin(f64(2)));
148}90}
14991
150test "math.sin32" {92test "math.sin32" {
151 const epsilon = 0.000001;93 const epsilon = 0.000001;
15294
153 expect(math.approxEq(f32, sin32(0.0), 0.0, epsilon));95 expect(math.approxEq(f32, sin_(f32, 0.0), 0.0, epsilon));
154 expect(math.approxEq(f32, sin32(0.2), 0.198669, epsilon));96 expect(math.approxEq(f32, sin_(f32, 0.2), 0.198669, epsilon));
155 expect(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon));97 expect(math.approxEq(f32, sin_(f32, 0.8923), 0.778517, epsilon));
156 expect(math.approxEq(f32, sin32(1.5), 0.997495, epsilon));98 expect(math.approxEq(f32, sin_(f32, 1.5), 0.997495, epsilon));
157 expect(math.approxEq(f32, sin32(37.45), -0.246544, epsilon));99 expect(math.approxEq(f32, sin_(f32, -1.5), -0.997495, epsilon));
158 expect(math.approxEq(f32, sin32(89.123), 0.916166, epsilon));100 expect(math.approxEq(f32, sin_(f32, 37.45), -0.246544, epsilon));
101 expect(math.approxEq(f32, sin_(f32, 89.123), 0.916166, epsilon));
159}102}
160103
161test "math.sin64" {104test "math.sin64" {
162 const epsilon = 0.000001;105 const epsilon = 0.000001;
163106
164 expect(math.approxEq(f64, sin64(0.0), 0.0, epsilon));107 expect(math.approxEq(f64, sin_(f64, 0.0), 0.0, epsilon));
165 expect(math.approxEq(f64, sin64(0.2), 0.198669, epsilon));108 expect(math.approxEq(f64, sin_(f64, 0.2), 0.198669, epsilon));
166 expect(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon));109 expect(math.approxEq(f64, sin_(f64, 0.8923), 0.778517, epsilon));
167 expect(math.approxEq(f64, sin64(1.5), 0.997495, epsilon));110 expect(math.approxEq(f64, sin_(f64, 1.5), 0.997495, epsilon));
168 expect(math.approxEq(f64, sin64(37.45), -0.246543, epsilon));111 expect(math.approxEq(f64, sin_(f64, -1.5), -0.997495, epsilon));
169 expect(math.approxEq(f64, sin64(89.123), 0.916166, epsilon));112 expect(math.approxEq(f64, sin_(f64, 37.45), -0.246543, epsilon));
113 expect(math.approxEq(f64, sin_(f64, 89.123), 0.916166, epsilon));
170}114}
171115
172test "math.sin32.special" {116test "math.sin32.special" {
173 expect(sin32(0.0) == 0.0);117 expect(sin_(f32, 0.0) == 0.0);
174 expect(sin32(-0.0) == -0.0);118 expect(sin_(f32, -0.0) == -0.0);
175 expect(math.isNan(sin32(math.inf(f32))));119 expect(math.isNan(sin_(f32, math.inf(f32))));
176 expect(math.isNan(sin32(-math.inf(f32))));120 expect(math.isNan(sin_(f32, -math.inf(f32))));
177 expect(math.isNan(sin32(math.nan(f32))));121 expect(math.isNan(sin_(f32, math.nan(f32))));
178}122}
179123
180test "math.sin64.special" {124test "math.sin64.special" {
181 expect(sin64(0.0) == 0.0);125 expect(sin_(f64, 0.0) == 0.0);
182 expect(sin64(-0.0) == -0.0);126 expect(sin_(f64, -0.0) == -0.0);
183 expect(math.isNan(sin64(math.inf(f64))));127 expect(math.isNan(sin_(f64, math.inf(f64))));
184 expect(math.isNan(sin64(-math.inf(f64))));128 expect(math.isNan(sin_(f64, -math.inf(f64))));
185 expect(math.isNan(sin64(math.nan(f64))));129 expect(math.isNan(sin_(f64, math.nan(f64))));
186}130}
std/math/tan.zig+50-104
...@@ -1,19 +1,24 @@...@@ -1,19 +1,24 @@
1// Special Cases:1// Ported from go, which is licensed under a BSD-3 license.
2// https://golang.org/LICENSE
2//3//
3// - tan(+-0) = +-04// https://golang.org/src/math/tan.go
4// - tan(+-inf) = nan
5// - tan(nan) = nan
65
7const builtin = @import("builtin");6const builtin = @import("builtin");
8const std = @import("../std.zig");7const std = @import("../std.zig");
9const math = std.math;8const math = std.math;
10const expect = std.testing.expect;9const expect = std.testing.expect;
1110
11/// Returns the tangent of the radian value x.
12///
13/// Special Cases:
14/// - tan(+-0) = +-0
15/// - tan(+-inf) = nan
16/// - tan(nan) = nan
12pub fn tan(x: var) @typeOf(x) {17pub fn tan(x: var) @typeOf(x) {
13 const T = @typeOf(x);18 const T = @typeOf(x);
14 return switch (T) {19 return switch (T) {
15 f32 => tan32(x),20 f32 => tan_(f32, x),
16 f64 => tan64(x),21 f64 => tan_(f64, x),
17 else => @compileError("tan not implemented for " ++ @typeName(T)),22 else => @compileError("tan not implemented for " ++ @typeName(T)),
18 };23 };
19}24}
...@@ -27,80 +32,27 @@ const Tq2 = -1.32089234440210967447E6;...@@ -27,80 +32,27 @@ const Tq2 = -1.32089234440210967447E6;
27const Tq3 = 2.50083801823357915839E7;32const Tq3 = 2.50083801823357915839E7;
28const Tq4 = -5.38695755929454629881E7;33const Tq4 = -5.38695755929454629881E7;
2934
30// NOTE: This is taken from the go stdlib. The musl implementation is much more complex.35const pi4a = 7.85398125648498535156e-1;
31//36const pi4b = 3.77489470793079817668E-8;
32// This may have slight differences on some edge cases and may need to replaced if so.37const pi4c = 2.69515142907905952645E-15;
33fn tan32(x_: f32) f32 {38const m4pi = 1.273239544735162542821171882678754627704620361328125;
34 const pi4a = 7.85398125648498535156e-1;
35 const pi4b = 3.77489470793079817668E-8;
36 const pi4c = 2.69515142907905952645E-15;
37 const m4pi = 1.273239544735162542821171882678754627704620361328125;
38
39 var x = x_;
40 if (x == 0 or math.isNan(x)) {
41 return x;
42 }
43 if (math.isInf(x)) {
44 return math.nan(f32);
45 }
46
47 var sign = false;
48 if (x < 0) {
49 x = -x;
50 sign = true;
51 }
52
53 var y = math.floor(x * m4pi);
54 var j = @floatToInt(i64, y);
55
56 if (j & 1 == 1) {
57 j += 1;
58 y += 1;
59 }
60
61 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
62 const w = z * z;
63
64 var r = r: {
65 if (w > 1e-14) {
66 break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));
67 } else {
68 break :r z;
69 }
70 };
71
72 if (j & 2 == 2) {
73 r = -1 / r;
74 }
75 if (sign) {
76 r = -r;
77 }
78
79 return r;
80}
8139
82fn tan64(x_: f64) f64 {40fn tan_(comptime T: type, x_: T) T {
83 const pi4a = 7.85398125648498535156e-1;41 const I = @IntType(true, T.bit_count);
84 const pi4b = 3.77489470793079817668E-8;
85 const pi4c = 2.69515142907905952645E-15;
86 const m4pi = 1.273239544735162542821171882678754627704620361328125;
8742
88 var x = x_;43 var x = x_;
89 if (x == 0 or math.isNan(x)) {44 if (x == 0 or math.isNan(x)) {
90 return x;45 return x;
91 }46 }
92 if (math.isInf(x)) {47 if (math.isInf(x)) {
93 return math.nan(f64);48 return math.nan(T);
94 }49 }
9550
96 var sign = false;51 var sign = x < 0;
97 if (x < 0) {52 x = math.fabs(x);
98 x = -x;
99 sign = true;
100 }
10153
102 var y = math.floor(x * m4pi);54 var y = math.floor(x * m4pi);
103 var j = @floatToInt(i64, y);55 var j = @floatToInt(I, y);
10456
105 if (j & 1 == 1) {57 if (j & 1 == 1) {
106 j += 1;58 j += 1;
...@@ -110,63 +62,57 @@ fn tan64(x_: f64) f64 {...@@ -110,63 +62,57 @@ fn tan64(x_: f64) f64 {
110 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;62 const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
111 const w = z * z;63 const w = z * z;
11264
113 var r = r: {65 var r = if (w > 1e-14)
114 if (w > 1e-14) {66 z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4))
115 break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));67 else
116 } else {68 z;
117 break :r z;
118 }
119 };
12069
121 if (j & 2 == 2) {70 if (j & 2 == 2) {
122 r = -1 / r;71 r = -1 / r;
123 }72 }
124 if (sign) {
125 r = -r;
126 }
12773
128 return r;74 return if (sign) -r else r;
129}75}
13076
131test "math.tan" {77test "math.tan" {
132 expect(tan(f32(0.0)) == tan32(0.0));78 expect(tan(f32(0.0)) == tan_(f32, 0.0));
133 expect(tan(f64(0.0)) == tan64(0.0));79 expect(tan(f64(0.0)) == tan_(f64, 0.0));
134}80}
13581
136test "math.tan32" {82test "math.tan32" {
137 const epsilon = 0.000001;83 const epsilon = 0.000001;
13884
139 expect(math.approxEq(f32, tan32(0.0), 0.0, epsilon));85 expect(math.approxEq(f32, tan_(f32, 0.0), 0.0, epsilon));
140 expect(math.approxEq(f32, tan32(0.2), 0.202710, epsilon));86 expect(math.approxEq(f32, tan_(f32, 0.2), 0.202710, epsilon));
141 expect(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon));87 expect(math.approxEq(f32, tan_(f32, 0.8923), 1.240422, epsilon));
142 expect(math.approxEq(f32, tan32(1.5), 14.101420, epsilon));88 expect(math.approxEq(f32, tan_(f32, 1.5), 14.101420, epsilon));
143 expect(math.approxEq(f32, tan32(37.45), -0.254397, epsilon));89 expect(math.approxEq(f32, tan_(f32, 37.45), -0.254397, epsilon));
144 expect(math.approxEq(f32, tan32(89.123), 2.285852, epsilon));90 expect(math.approxEq(f32, tan_(f32, 89.123), 2.285852, epsilon));
145}91}
14692
147test "math.tan64" {93test "math.tan64" {
148 const epsilon = 0.000001;94 const epsilon = 0.000001;
14995
150 expect(math.approxEq(f64, tan64(0.0), 0.0, epsilon));96 expect(math.approxEq(f64, tan_(f64, 0.0), 0.0, epsilon));
151 expect(math.approxEq(f64, tan64(0.2), 0.202710, epsilon));97 expect(math.approxEq(f64, tan_(f64, 0.2), 0.202710, epsilon));
152 expect(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon));98 expect(math.approxEq(f64, tan_(f64, 0.8923), 1.240422, epsilon));
153 expect(math.approxEq(f64, tan64(1.5), 14.101420, epsilon));99 expect(math.approxEq(f64, tan_(f64, 1.5), 14.101420, epsilon));
154 expect(math.approxEq(f64, tan64(37.45), -0.254397, epsilon));100 expect(math.approxEq(f64, tan_(f64, 37.45), -0.254397, epsilon));
155 expect(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon));101 expect(math.approxEq(f64, tan_(f64, 89.123), 2.2858376, epsilon));
156}102}
157103
158test "math.tan32.special" {104test "math.tan32.special" {
159 expect(tan32(0.0) == 0.0);105 expect(tan_(f32, 0.0) == 0.0);
160 expect(tan32(-0.0) == -0.0);106 expect(tan_(f32, -0.0) == -0.0);
161 expect(math.isNan(tan32(math.inf(f32))));107 expect(math.isNan(tan_(f32, math.inf(f32))));
162 expect(math.isNan(tan32(-math.inf(f32))));108 expect(math.isNan(tan_(f32, -math.inf(f32))));
163 expect(math.isNan(tan32(math.nan(f32))));109 expect(math.isNan(tan_(f32, math.nan(f32))));
164}110}
165111
166test "math.tan64.special" {112test "math.tan64.special" {
167 expect(tan64(0.0) == 0.0);113 expect(tan_(f64, 0.0) == 0.0);
168 expect(tan64(-0.0) == -0.0);114 expect(tan_(f64, -0.0) == -0.0);
169 expect(math.isNan(tan64(math.inf(f64))));115 expect(math.isNan(tan_(f64, math.inf(f64))));
170 expect(math.isNan(tan64(-math.inf(f64))));116 expect(math.isNan(tan_(f64, -math.inf(f64))));
171 expect(math.isNan(tan64(math.nan(f64))));117 expect(math.isNan(tan_(f64, math.nan(f64))));
172}118}