authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2017-06-20 23:01:04+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2017-06-20 23:10:22+12:00
log5bbec42a4edb3f6758a9d67fcf763c660ac3f204
tree3dc324d083f142935df53c5900edfc04c0f956f8
parentc9fc8bd802f5ed52c4cc78b93f18fc5dc9b6bb7f

Add math special case tests and general fixes

- Should cover special case inputs for most functions - Fixed a number of runtime panicking behaviour reliant on shift overflow/division by zero etc.

37 files changed, 964 insertions(+), 82 deletions(-)

std/math/_expo2.zig deleted-28
...@@ -1,28 +0,0 @@
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+16-2
...@@ -1,3 +1,7 @@...@@ -1,3 +1,7 @@
1// Special Cases:
2//
3// - acos(x) = nan if x < -1 or x > 1
4
1const math = @import("index.zig");5const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;6const assert = @import("../debug.zig").assert;
37
...@@ -40,7 +44,7 @@ fn acos32(x: f32) -> f32 {...@@ -40,7 +44,7 @@ fn acos32(x: f32) -> f32 {
40 return 0;44 return 0;
41 }45 }
42 } else {46 } else {
43 return 0 / (x - x);47 return math.nan(f32);
44 }48 }
45 }49 }
4650
...@@ -109,7 +113,7 @@ fn acos64(x: f64) -> f64 {...@@ -109,7 +113,7 @@ fn acos64(x: f64) -> f64 {
109 }113 }
110 }114 }
111115
112 return 0 / (x - x);116 return math.nan(f32);
113 }117 }
114118
115 // |x| < 0.5119 // |x| < 0.5
...@@ -166,3 +170,13 @@ test "math.acos64" {...@@ -166,3 +170,13 @@ test "math.acos64" {
166 assert(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon));170 assert(math.approxEq(f64, acos64(0.8923), 0.468382, epsilon));
167 assert(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon));171 assert(math.approxEq(f64, acos64(-0.2), 1.772154, epsilon));
168}172}
173
174test "math.acos32.special" {
175 assert(math.isNan(acos32(-2)));
176 assert(math.isNan(acos32(1.5)));
177}
178
179test "math.acos64.special" {
180 assert(math.isNan(acos64(-2)));
181 assert(math.isNan(acos64(1.5)));
182}
std/math/acosh.zig+15
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1// Special Cases:
2//
3// - acosh(x) = snan if x < 1
4// - acosh(nan) = nan
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
...@@ -72,3 +77,13 @@ test "math.acosh64" {...@@ -72,3 +77,13 @@ test "math.acosh64" {
72 assert(math.approxEq(f64, acosh64(89.123), 5.183133, epsilon));77 assert(math.approxEq(f64, acosh64(89.123), 5.183133, epsilon));
73 assert(math.approxEq(f64, acosh64(123123.234375), 12.414088, epsilon));78 assert(math.approxEq(f64, acosh64(123123.234375), 12.414088, epsilon));
74}79}
80
81test "math.acosh32.special" {
82 assert(math.isNan(acosh32(math.nan(f32))));
83 assert(math.isSignalNan(acosh32(0.5)));
84}
85
86test "math.acosh64.special" {
87 assert(math.isNan(acosh64(math.nan(f64))));
88 assert(math.isSignalNan(acosh64(0.5)));
89}
std/math/asin.zig+21-2
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1// Special Cases:
2//
3// - asin(+-0) = +-0
4// - asin(x) = nan if x < -1 or x > 1
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
...@@ -36,7 +41,7 @@ fn asin32(x: f32) -> f32 {...@@ -36,7 +41,7 @@ fn asin32(x: f32) -> f32 {
36 if (ix == 0x3F800000) {41 if (ix == 0x3F800000) {
37 return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact42 return x * pio2 + 0x1.0p-120; // asin(+-1) = +-pi/2 with inexact
38 } else {43 } else {
39 return 0 / (x - x); // asin(|x| > 1) is nan44 return math.nan(f32); // asin(|x| > 1) is nan
40 }45 }
41 }46 }
4247
...@@ -95,7 +100,7 @@ fn asin64(x: f64) -> f64 {...@@ -95,7 +100,7 @@ fn asin64(x: f64) -> f64 {
95 if ((ix - 0x3FF00000) | lx == 0) {100 if ((ix - 0x3FF00000) | lx == 0) {
96 return x * pio2_hi + 0x1.0p-120;101 return x * pio2_hi + 0x1.0p-120;
97 } else {102 } else {
98 return 0/ (x - x);103 return math.nan(f64);
99 }104 }
100 }105 }
101106
...@@ -158,3 +163,17 @@ test "math.asin64" {...@@ -158,3 +163,17 @@ test "math.asin64" {
158 assert(math.approxEq(f64, asin64(0.5), 0.523599, epsilon));163 assert(math.approxEq(f64, asin64(0.5), 0.523599, epsilon));
159 assert(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon));164 assert(math.approxEq(f64, asin64(0.8923), 1.102415, epsilon));
160}165}
166
167test "math.asin32.special" {
168 assert(asin32(0.0) == 0.0);
169 assert(asin32(-0.0) == -0.0);
170 assert(math.isNan(asin32(-2)));
171 assert(math.isNan(asin32(1.5)));
172}
173
174test "math.asin64.special" {
175 assert(asin64(0.0) == 0.0);
176 assert(asin64(-0.0) == -0.0);
177 assert(math.isNan(asin64(-2)));
178 assert(math.isNan(asin64(1.5)));
179}
std/math/asinh.zig+31
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - asinh(+-0) = +-0
4// - asinh(+-inf) = +-inf
5// - asinh(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -21,6 +27,11 @@ fn asinh32(x: f32) -> f32 {...@@ -21,6 +27,11 @@ fn asinh32(x: f32) -> f32 {
2127
22 var rx = @bitCast(f32, i); // |x|28 var rx = @bitCast(f32, i); // |x|
2329
30 // TODO: Shouldn't need this explicit check.
31 if (math.isNegativeInf(x)) {
32 return x;
33 }
34
24 // |x| >= 0x1p12 or inf or nan35 // |x| >= 0x1p12 or inf or nan
25 if (i >= 0x3F800000 + (12 << 23)) {36 if (i >= 0x3F800000 + (12 << 23)) {
26 rx = math.ln(rx) + 0.69314718055994530941723212145817656;37 rx = math.ln(rx) + 0.69314718055994530941723212145817656;
...@@ -48,6 +59,10 @@ fn asinh64(x: f64) -> f64 {...@@ -48,6 +59,10 @@ fn asinh64(x: f64) -> f64 {
4859
49 var rx = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x|60 var rx = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x|
5061
62 if (math.isNegativeInf(x)) {
63 return x;
64 }
65
51 // |x| >= 0x1p26 or inf or nan66 // |x| >= 0x1p26 or inf or nan
52 if (e >= 0x3FF + 26) {67 if (e >= 0x3FF + 26) {
53 rx = math.ln(rx) + 0.693147180559945309417232121458176568;68 rx = math.ln(rx) + 0.693147180559945309417232121458176568;
...@@ -96,3 +111,19 @@ test "math.asinh64" {...@@ -96,3 +111,19 @@ test "math.asinh64" {
96 assert(math.approxEq(f64, asinh64(89.123), 5.183196, epsilon));111 assert(math.approxEq(f64, asinh64(89.123), 5.183196, epsilon));
97 assert(math.approxEq(f64, asinh64(123123.234375), 12.414088, epsilon));112 assert(math.approxEq(f64, asinh64(123123.234375), 12.414088, epsilon));
98}113}
114
115test "math.asinh32.special" {
116 assert(asinh32(0.0) == 0.0);
117 assert(asinh32(-0.0) == -0.0);
118 assert(math.isPositiveInf(asinh32(math.inf(f32))));
119 assert(math.isNegativeInf(asinh32(-math.inf(f32))));
120 assert(math.isNan(asinh32(math.nan(f32))));
121}
122
123test "math.asinh64.special" {
124 assert(asinh64(0.0) == 0.0);
125 assert(asinh64(-0.0) == -0.0);
126 assert(math.isPositiveInf(asinh64(math.inf(f64))));
127 assert(math.isNegativeInf(asinh64(-math.inf(f64))));
128 assert(math.isNan(asinh64(math.nan(f64))));
129}
std/math/atan.zig+23
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1// Special Cases:
2//
3// - atan(+-0) = +-0
4// - atan(+-inf) = +-pi/2
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
...@@ -228,3 +233,21 @@ test "math.atan64" {...@@ -228,3 +233,21 @@ test "math.atan64" {
228 assert(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon));233 assert(math.approxEq(f64, atan64(0.8923), 0.728545, epsilon));
229 assert(math.approxEq(f64, atan64(1.5), 0.982794, epsilon));234 assert(math.approxEq(f64, atan64(1.5), 0.982794, epsilon));
230}235}
236
237test "math.atan32.special" {
238 const epsilon = 0.000001;
239
240 assert(atan32(0.0) == 0.0);
241 assert(atan32(-0.0) == -0.0);
242 assert(math.approxEq(f32, atan32(math.inf(f32)), math.pi_2, epsilon));
243 assert(math.approxEq(f32, atan32(-math.inf(f32)), -math.pi_2, epsilon));
244}
245
246test "math.atan64.special" {
247 const epsilon = 0.000001;
248
249 assert(atan64(0.0) == 0.0);
250 assert(atan64(-0.0) == -0.0);
251 assert(math.approxEq(f64, atan64(math.inf(f64)), math.pi_2, epsilon));
252 assert(math.approxEq(f64, atan64(-math.inf(f64)), -math.pi_2, epsilon));
253}
std/math/atan2.zig+68
...@@ -1,3 +1,23 @@...@@ -1,3 +1,23 @@
1// Special Cases:
2//
3// atan2(y, nan) = nan
4// atan2(nan, x) = nan
5// atan2(+0, x>=0) = +0
6// atan2(-0, x>=0) = -0
7// atan2(+0, x<=-0) = +pi
8// atan2(-0, x<=-0) = -pi
9// atan2(y>0, 0) = +pi/2
10// atan2(y<0, 0) = -pi/2
11// atan2(+inf, +inf) = +pi/4
12// atan2(-inf, +inf) = -pi/4
13// atan2(+inf, -inf) = 3pi/4
14// atan2(-inf, -inf) = -3pi/4
15// atan2(y, +inf) = 0
16// atan2(y>0, -inf) = +pi
17// atan2(y<0, -inf) = -pi
18// atan2(+inf, x) = +pi/2
19// atan2(-inf, x) = -pi/2
20
1const math = @import("index.zig");21const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;22const assert = @import("../debug.zig").assert;
323
...@@ -215,3 +235,51 @@ test "math.atan2_64" {...@@ -215,3 +235,51 @@ test "math.atan2_64" {
215 assert(math.approxEq(f64, atan2_64(0.34, -0.4), 2.437099, epsilon));235 assert(math.approxEq(f64, atan2_64(0.34, -0.4), 2.437099, epsilon));
216 assert(math.approxEq(f64, atan2_64(0.34, 1.243), 0.267001, epsilon));236 assert(math.approxEq(f64, atan2_64(0.34, 1.243), 0.267001, epsilon));
217}237}
238
239test "math.atan2_32.special" {
240 const epsilon = 0.000001;
241
242 assert(math.isNan(atan2_32(1.0, math.nan(f32))));
243 assert(math.isNan(atan2_32(math.nan(f32), 1.0)));
244 assert(atan2_32(0.0, 5.0) == 0.0);
245 assert(atan2_32(-0.0, 5.0) == -0.0);
246 assert(math.approxEq(f32, atan2_32(0.0, -5.0), math.pi, epsilon));
247 assert(math.approxEq(f32, atan2_32(-0.0, -5.0), -math.pi, epsilon));
248 assert(math.approxEq(f32, atan2_32(1.0, 0.0), math.pi_2, epsilon));
249 assert(math.approxEq(f32, atan2_32(1.0, -0.0), math.pi_2, epsilon));
250 assert(math.approxEq(f32, atan2_32(-1.0, 0.0), -math.pi_2, epsilon));
251 assert(math.approxEq(f32, atan2_32(-1.0, -0.0), -math.pi_2, epsilon));
252 assert(math.approxEq(f32, atan2_32(math.inf(f32), math.inf(f32)), math.pi_4, epsilon));
253 assert(math.approxEq(f32, atan2_32(-math.inf(f32), math.inf(f32)), -math.pi_4, epsilon));
254 assert(math.approxEq(f32, atan2_32(math.inf(f32), -math.inf(f32)), 3.0 * math.pi_4, epsilon));
255 assert(math.approxEq(f32, atan2_32(-math.inf(f32), -math.inf(f32)), -3.0 * math.pi_4, epsilon));
256 assert(atan2_32(1.0, math.inf(f32)) == 0.0);
257 assert(math.approxEq(f32, atan2_32(1.0, -math.inf(f32)), math.pi, epsilon));
258 assert(math.approxEq(f32, atan2_32(-1.0, -math.inf(f32)), -math.pi, epsilon));
259 assert(math.approxEq(f32, atan2_32(math.inf(f32), 1.0), math.pi_2, epsilon));
260 assert(math.approxEq(f32, atan2_32(-math.inf(f32), 1.0), -math.pi_2, epsilon));
261}
262
263test "math.atan2_64.special" {
264 const epsilon = 0.000001;
265
266 assert(math.isNan(atan2_64(1.0, math.nan(f64))));
267 assert(math.isNan(atan2_64(math.nan(f64), 1.0)));
268 assert(atan2_64(0.0, 5.0) == 0.0);
269 assert(atan2_64(-0.0, 5.0) == -0.0);
270 assert(math.approxEq(f64, atan2_64(0.0, -5.0), math.pi, epsilon));
271 assert(math.approxEq(f64, atan2_64(-0.0, -5.0), -math.pi, epsilon));
272 assert(math.approxEq(f64, atan2_64(1.0, 0.0), math.pi_2, epsilon));
273 assert(math.approxEq(f64, atan2_64(1.0, -0.0), math.pi_2, epsilon));
274 assert(math.approxEq(f64, atan2_64(-1.0, 0.0), -math.pi_2, epsilon));
275 assert(math.approxEq(f64, atan2_64(-1.0, -0.0), -math.pi_2, epsilon));
276 assert(math.approxEq(f64, atan2_64(math.inf(f64), math.inf(f64)), math.pi_4, epsilon));
277 assert(math.approxEq(f64, atan2_64(-math.inf(f64), math.inf(f64)), -math.pi_4, epsilon));
278 assert(math.approxEq(f64, atan2_64(math.inf(f64), -math.inf(f64)), 3.0 * math.pi_4, epsilon));
279 assert(math.approxEq(f64, atan2_64(-math.inf(f64), -math.inf(f64)), -3.0 * math.pi_4, epsilon));
280 assert(atan2_64(1.0, math.inf(f64)) == 0.0);
281 assert(math.approxEq(f64, atan2_64(1.0, -math.inf(f64)), math.pi, epsilon));
282 assert(math.approxEq(f64, atan2_64(-1.0, -math.inf(f64)), -math.pi, epsilon));
283 assert(math.approxEq(f64, atan2_64(math.inf(f64), 1.0), math.pi_2, epsilon));
284 assert(math.approxEq(f64, atan2_64(-math.inf(f64), 1.0), -math.pi_2, epsilon));
285}
std/math/atanh.zig+30-2
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - atanh(+-1) = +-inf with signal
4// - atanh(x) = nan if |x| > 1 with signal
5// - atanh(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -21,6 +27,10 @@ fn atanh_32(x: f32) -> f32 {...@@ -21,6 +27,10 @@ fn atanh_32(x: f32) -> f32 {
2127
22 var y = @bitCast(f32, i); // |x|28 var y = @bitCast(f32, i); // |x|
2329
30 if (y == 1.0) {
31 return math.copysign(f32, math.inf(f32), x);
32 }
33
24 if (u < 0x3F800000 - (1 << 23)) {34 if (u < 0x3F800000 - (1 << 23)) {
25 if (u < 0x3F800000 - (32 << 23)) {35 if (u < 0x3F800000 - (32 << 23)) {
26 // underflow36 // underflow
...@@ -33,7 +43,6 @@ fn atanh_32(x: f32) -> f32 {...@@ -33,7 +43,6 @@ fn atanh_32(x: f32) -> f32 {
33 y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y));43 y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y));
34 }44 }
35 } else {45 } else {
36 // avoid overflow
37 y = 0.5 * math.log1p(2 * (y / (1 - y)));46 y = 0.5 * math.log1p(2 * (y / (1 - y)));
38 }47 }
3948
...@@ -47,6 +56,10 @@ fn atanh_64(x: f64) -> f64 {...@@ -47,6 +56,10 @@ fn atanh_64(x: f64) -> f64 {
4756
48 var y = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x|57 var y = @bitCast(f64, u & (@maxValue(u64) >> 1)); // |x|
4958
59 if (y == 1.0) {
60 return math.copysign(f64, math.inf(f64), x);
61 }
62
50 if (e < 0x3FF - 1) {63 if (e < 0x3FF - 1) {
51 if (e < 0x3FF - 32) {64 if (e < 0x3FF - 32) {
52 // underflow65 // underflow
...@@ -59,7 +72,6 @@ fn atanh_64(x: f64) -> f64 {...@@ -59,7 +72,6 @@ fn atanh_64(x: f64) -> f64 {
59 y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y));72 y = 0.5 * math.log1p(2 * y + 2 * y * y / (1 - y));
60 }73 }
61 } else {74 } else {
62 // avoid overflow
63 y = 0.5 * math.log1p(2 * (y / (1 - y)));75 y = 0.5 * math.log1p(2 * (y / (1 - y)));
64 }76 }
6577
...@@ -86,3 +98,19 @@ test "math.atanh_64" {...@@ -86,3 +98,19 @@ test "math.atanh_64" {
86 assert(math.approxEq(f64, atanh_64(0.2), 0.202733, epsilon));98 assert(math.approxEq(f64, atanh_64(0.2), 0.202733, epsilon));
87 assert(math.approxEq(f64, atanh_64(0.8923), 1.433099, epsilon));99 assert(math.approxEq(f64, atanh_64(0.8923), 1.433099, epsilon));
88}100}
101
102test "math.atanh32.special" {
103 assert(math.isPositiveInf(atanh_32(1)));
104 assert(math.isNegativeInf(atanh_32(-1)));
105 assert(math.isSignalNan(atanh_32(1.5)));
106 assert(math.isSignalNan(atanh_32(-1.5)));
107 assert(math.isNan(atanh_32(math.nan(f32))));
108}
109
110test "math.atanh64.special" {
111 assert(math.isPositiveInf(atanh_64(1)));
112 assert(math.isNegativeInf(atanh_64(-1)));
113 assert(math.isSignalNan(atanh_64(1.5)));
114 assert(math.isSignalNan(atanh_64(-1.5)));
115 assert(math.isNan(atanh_64(math.nan(f64))));
116}
std/math/cbrt.zig+22
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - cbrt(+-0) = +-0
4// - cbrt(+-inf) = +-inf
5// - cbrt(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -135,3 +141,19 @@ test "math.cbrt64" {...@@ -135,3 +141,19 @@ test "math.cbrt64" {
135 assert(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon));141 assert(math.approxEq(f64, cbrt64(37.45), 3.345676, epsilon));
136 assert(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon));142 assert(math.approxEq(f64, cbrt64(123123.234375), 49.748501, epsilon));
137}143}
144
145test "math.cbrt.special" {
146 assert(cbrt32(0.0) == 0.0);
147 assert(cbrt32(-0.0) == -0.0);
148 assert(math.isPositiveInf(cbrt32(math.inf(f32))));
149 assert(math.isNegativeInf(cbrt32(-math.inf(f32))));
150 assert(math.isNan(cbrt32(math.nan(f32))));
151}
152
153test "math.cbrt64.special" {
154 assert(cbrt64(0.0) == 0.0);
155 assert(cbrt64(-0.0) == -0.0);
156 assert(math.isPositiveInf(cbrt64(math.inf(f64))));
157 assert(math.isNegativeInf(cbrt64(-math.inf(f64))));
158 assert(math.isNan(cbrt64(math.nan(f64))));
159}
std/math/ceil.zig+27
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - ceil(+-0) = +-0
4// - ceil(+-inf) = +-inf
5// - ceil(nan) = nan
6
1const builtin = @import("builtin");7const builtin = @import("builtin");
2const math = @import("index.zig");8const math = @import("index.zig");
3const assert = @import("../debug.zig").assert;9const assert = @import("../debug.zig").assert;
...@@ -19,6 +25,11 @@ fn ceil32(x: f32) -> f32 {...@@ -19,6 +25,11 @@ fn ceil32(x: f32) -> f32 {
19 var e = i32((u >> 23) & 0xFF) - 0x7F;25 var e = i32((u >> 23) & 0xFF) - 0x7F;
20 var m: u32 = undefined;26 var m: u32 = undefined;
2127
28 // TODO: Shouldn't need this explicit check.
29 if (x == 0.0) {
30 return x;
31 }
32
22 if (e >= 23) {33 if (e >= 23) {
23 return x;34 return x;
24 }35 }
...@@ -90,3 +101,19 @@ test "math.ceil64" {...@@ -90,3 +101,19 @@ test "math.ceil64" {
90 assert(ceil64(-1.3) == -1.0);101 assert(ceil64(-1.3) == -1.0);
91 assert(ceil64(0.2) == 1.0);102 assert(ceil64(0.2) == 1.0);
92}103}
104
105test "math.ceil32.special" {
106 assert(ceil32(0.0) == 0.0);
107 assert(ceil32(-0.0) == -0.0);
108 assert(math.isPositiveInf(ceil32(math.inf(f32))));
109 assert(math.isNegativeInf(ceil32(-math.inf(f32))));
110 assert(math.isNan(ceil32(math.nan(f32))));
111}
112
113test "math.ceil64.special" {
114 assert(ceil64(0.0) == 0.0);
115 assert(ceil64(-0.0) == -0.0);
116 assert(math.isPositiveInf(ceil64(math.inf(f64))));
117 assert(math.isNegativeInf(ceil64(-math.inf(f64))));
118 assert(math.isNan(ceil64(math.nan(f64))));
119}
std/math/cos.zig+17-1
...@@ -1,7 +1,11 @@...@@ -1,7 +1,11 @@
1// Special Cases:
2//
3// - cos(+-inf) = nan
4// - cos(nan) = nan
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
4
5// TODO issue #3939// TODO issue #393
6pub const cos = cos_workaround;10pub const cos = cos_workaround;
711
...@@ -163,3 +167,15 @@ test "math.cos64" {...@@ -163,3 +167,15 @@ test "math.cos64" {
163 assert(math.approxEq(f64, cos64(37.45), 0.969132, epsilon));167 assert(math.approxEq(f64, cos64(37.45), 0.969132, epsilon));
164 assert(math.approxEq(f64, cos64(89.123), 0.40080, epsilon));168 assert(math.approxEq(f64, cos64(89.123), 0.40080, epsilon));
165}169}
170
171test "math.cos32.special" {
172 assert(math.isNan(cos32(math.inf(f32))));
173 assert(math.isNan(cos32(-math.inf(f32))));
174 assert(math.isNan(cos32(math.nan(f32))));
175}
176
177test "math.cos64.special" {
178 assert(math.isNan(cos64(math.inf(f64))));
179 assert(math.isNan(cos64(-math.inf(f64))));
180 assert(math.isNan(cos64(math.nan(f64))));
181}
std/math/cosh.zig+28-1
...@@ -1,5 +1,11 @@...@@ -1,5 +1,11 @@
1// Special Cases:
2//
3// - cosh(+-0) = 1
4// - cosh(+-inf) = +inf
5// - cosh(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const expo2 = @import("_expo2.zig").expo2;8const expo2 = @import("expo2.zig").expo2;
3const assert = @import("../debug.zig").assert;9const assert = @import("../debug.zig").assert;
410
5// TODO issue #39311// TODO issue #393
...@@ -47,6 +53,11 @@ fn cosh64(x: f64) -> f64 {...@@ -47,6 +53,11 @@ fn cosh64(x: f64) -> f64 {
47 const w = u32(u >> 32);53 const w = u32(u >> 32);
48 const ax = @bitCast(f64, u & (@maxValue(u64) >> 1));54 const ax = @bitCast(f64, u & (@maxValue(u64) >> 1));
4955
56 // TODO: Shouldn't need this explicit check.
57 if (x == 0.0) {
58 return 1.0;
59 }
60
50 // |x| < log(2)61 // |x| < log(2)
51 if (w < 0x3FE62E42) {62 if (w < 0x3FE62E42) {
52 if (w < 0x3FF00000 - (26 << 20)) {63 if (w < 0x3FF00000 - (26 << 20)) {
...@@ -92,3 +103,19 @@ test "math.cosh64" {...@@ -92,3 +103,19 @@ test "math.cosh64" {
92 assert(math.approxEq(f64, cosh64(0.8923), 1.425225, epsilon));103 assert(math.approxEq(f64, cosh64(0.8923), 1.425225, epsilon));
93 assert(math.approxEq(f64, cosh64(1.5), 2.352410, epsilon));104 assert(math.approxEq(f64, cosh64(1.5), 2.352410, epsilon));
94}105}
106
107test "math.cosh32.special" {
108 assert(cosh32(0.0) == 1.0);
109 assert(cosh32(-0.0) == 1.0);
110 assert(math.isPositiveInf(cosh32(math.inf(f32))));
111 assert(math.isPositiveInf(cosh32(-math.inf(f32))));
112 assert(math.isNan(cosh32(math.nan(f32))));
113}
114
115test "math.cosh64.special" {
116 assert(cosh64(0.0) == 1.0);
117 assert(cosh64(-0.0) == 1.0);
118 assert(math.isPositiveInf(cosh64(math.inf(f64))));
119 assert(math.isPositiveInf(cosh64(-math.inf(f64))));
120 assert(math.isNan(cosh64(math.nan(f64))));
121}
std/math/exp.zig+16
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1// Special Cases:
2//
3// - exp(+inf) = +inf
4// - exp(nan) = nan
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
...@@ -192,3 +197,14 @@ test "math.exp64" {...@@ -192,3 +197,14 @@ test "math.exp64" {
192 assert(math.approxEq(f64, exp64(0.8923), 2.440737, epsilon));197 assert(math.approxEq(f64, exp64(0.8923), 2.440737, epsilon));
193 assert(math.approxEq(f64, exp64(1.5), 4.481689, epsilon));198 assert(math.approxEq(f64, exp64(1.5), 4.481689, epsilon));
194}199}
200
201test "math.exp32.special" {
202 assert(math.isPositiveInf(exp32(math.inf(f32))));
203 assert(math.isNan(exp32(math.nan(f32))));
204}
205
206test "math.exp64.special" {
207 // TODO: Error on release (like pow)
208 assert(math.isPositiveInf(exp64(math.inf(f64))));
209 assert(math.isNan(exp64(math.nan(f64))));
210}
std/math/exp2.zig+20-1
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1// Special Cases:
2//
3// - exp2(+inf) = +inf
4// - exp2(nan) = nan
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
...@@ -363,6 +368,11 @@ fn exp2_64(x: f64) -> f64 {...@@ -363,6 +368,11 @@ fn exp2_64(x: f64) -> f64 {
363 const ux = @bitCast(u64, x);368 const ux = @bitCast(u64, x);
364 const ix = u32(ux >> 32) & 0x7FFFFFFF;369 const ix = u32(ux >> 32) & 0x7FFFFFFF;
365370
371 // TODO: This should be handled beneath.
372 if (math.isNan(x)) {
373 return math.nan(f64);
374 }
375
366 // |x| >= 1022 or nan376 // |x| >= 1022 or nan
367 if (ix >= 0x408FF000) {377 if (ix >= 0x408FF000) {
368 // x >= 1024 or nan378 // x >= 1024 or nan
...@@ -432,5 +442,14 @@ test "math.exp2_64" {...@@ -432,5 +442,14 @@ test "math.exp2_64" {
432 assert(math.approxEq(f64, exp2_64(0.2), 1.148698, epsilon));442 assert(math.approxEq(f64, exp2_64(0.2), 1.148698, epsilon));
433 assert(math.approxEq(f64, exp2_64(0.8923), 1.856133, epsilon));443 assert(math.approxEq(f64, exp2_64(0.8923), 1.856133, epsilon));
434 assert(math.approxEq(f64, exp2_64(1.5), 2.828427, epsilon));444 assert(math.approxEq(f64, exp2_64(1.5), 2.828427, epsilon));
435 // assert(math.approxEq(f64, exp2_64(37.45), 18379273786760560.000000, epsilon));445}
446
447test "math.exp2_32.special" {
448 assert(math.isPositiveInf(exp2_32(math.inf(f32))));
449 assert(math.isNan(exp2_32(math.nan(f32))));
450}
451
452test "math.exp2_64.special" {
453 assert(math.isPositiveInf(exp2_64(math.inf(f64))));
454 assert(math.isNan(exp2_64(math.nan(f64))));
436}455}
std/math/expm1.zig+34-3
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - expm1(+inf) = +inf
4// - expm1(-inf) = -1
5// - expm1(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -26,6 +32,11 @@ fn expm1_32(x_: f32) -> f32 {...@@ -26,6 +32,11 @@ fn expm1_32(x_: f32) -> f32 {
26 const hx = ux & 0x7FFFFFFF;32 const hx = ux & 0x7FFFFFFF;
27 const sign = hx >> 31;33 const sign = hx >> 31;
2834
35 // TODO: Shouldn't need this check explicitly.
36 if (math.isNegativeInf(x)) {
37 return -1.0;
38 }
39
29 // |x| >= 27 * ln240 // |x| >= 27 * ln2
30 if (hx >= 0x4195B844) {41 if (hx >= 0x4195B844) {
31 // nan42 // nan
...@@ -113,7 +124,7 @@ fn expm1_32(x_: f32) -> f32 {...@@ -113,7 +124,7 @@ fn expm1_32(x_: f32) -> f32 {
113 }124 }
114 }125 }
115126
116 const twopk = @bitCast(f32, u32(0x7F + k) << 23);127 const twopk = @bitCast(f32, u32((0x7F + k) <<% 23));
117128
118 if (k < 0 or k > 56) {129 if (k < 0 or k > 56) {
119 var y = x - e + 1.0;130 var y = x - e + 1.0;
...@@ -150,6 +161,10 @@ fn expm1_64(x_: f64) -> f64 {...@@ -150,6 +161,10 @@ fn expm1_64(x_: f64) -> f64 {
150 const hx = u32(ux >> 32) & 0x7FFFFFFF;161 const hx = u32(ux >> 32) & 0x7FFFFFFF;
151 const sign = hx >> 63;162 const sign = hx >> 63;
152163
164 if (math.isNegativeInf(x)) {
165 return -1.0;
166 }
167
153 // |x| >= 56 * ln2168 // |x| >= 56 * ln2
154 if (hx >= 0x4043687A) {169 if (hx >= 0x4043687A) {
155 // exp1md(nan) = nan170 // exp1md(nan) = nan
...@@ -162,7 +177,7 @@ fn expm1_64(x_: f64) -> f64 {...@@ -162,7 +177,7 @@ fn expm1_64(x_: f64) -> f64 {
162 }177 }
163 if (x > o_threshold) {178 if (x > o_threshold) {
164 math.raiseOverflow();179 math.raiseOverflow();
165 return math.nan(f64);180 return math.inf(f64);
166 }181 }
167 }182 }
168183
...@@ -238,7 +253,7 @@ fn expm1_64(x_: f64) -> f64 {...@@ -238,7 +253,7 @@ fn expm1_64(x_: f64) -> f64 {
238 }253 }
239 }254 }
240255
241 const twopk = @bitCast(f64, u64(0x3FF + k) << 52);256 const twopk = @bitCast(f64, u64(0x3FF + k) <<% 52);
242257
243 if (k < 0 or k > 56) {258 if (k < 0 or k > 56) {
244 var y = x - e + 1.0;259 var y = x - e + 1.0;
...@@ -283,3 +298,19 @@ test "math.expm1_64" {...@@ -283,3 +298,19 @@ test "math.expm1_64" {
283 assert(math.approxEq(f64, expm1_64(0.8923), 1.440737, epsilon));298 assert(math.approxEq(f64, expm1_64(0.8923), 1.440737, epsilon));
284 assert(math.approxEq(f64, expm1_64(1.5), 3.481689, epsilon));299 assert(math.approxEq(f64, expm1_64(1.5), 3.481689, epsilon));
285}300}
301
302test "math.expm1_32.special" {
303 const epsilon = 0.000001;
304
305 assert(math.isPositiveInf(expm1_32(math.inf(f32))));
306 assert(expm1_32(-math.inf(f32)) == -1.0);
307 assert(math.isNan(expm1_32(math.nan(f32))));
308}
309
310test "math.expm1_64.special" {
311 const epsilon = 0.000001;
312
313 assert(math.isPositiveInf(expm1_64(math.inf(f64))));
314 assert(expm1_64(-math.inf(f64)) == -1.0);
315 assert(math.isNan(expm1_64(math.nan(f64))));
316}
std/math/expo2.zig created+28
...@@ -0,0 +1,28 @@
1const math = @import("index.zig");
2
3pub fn expo2(x: var) -> @typeOf(x) {
4 const T = @typeOf(x);
5 switch (T) {
6 f32 => expo2f(x),
7 f64 => expo2d(x),
8 else => @compileError("expo2 not implemented for " ++ @typeName(T)),
9 }
10}
11
12fn expo2f(x: f32) -> f32 {
13 const k: u32 = 235;
14 const kln2 = 0x1.45C778p+7;
15
16 const u = (0x7F + k / 2) << 23;
17 const scale = @bitCast(f32, u);
18 math.exp(x - kln2) * scale * scale
19}
20
21fn expo2d(x: f64) -> f64 {
22 const k: u32 = 2043;
23 const kln2 = 0x1.62066151ADD8BP+10;
24
25 const u = (0x3FF + k / 2) << 20;
26 const scale = @bitCast(f64, u64(u) << 32);
27 math.exp(x - kln2) * scale * scale
28}
std/math/fabs.zig+17
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1// Special Cases:
2//
3// - fabs(+-inf) = +inf
4// - fabs(nan) = nan
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
...@@ -39,3 +44,15 @@ test "math.fabs64" {...@@ -39,3 +44,15 @@ test "math.fabs64" {
39 assert(fabs64(1.0) == 1.0);44 assert(fabs64(1.0) == 1.0);
40 assert(fabs64(-1.0) == 1.0);45 assert(fabs64(-1.0) == 1.0);
41}46}
47
48test "math.fabs32.special" {
49 assert(math.isPositiveInf(fabs(math.inf(f32))));
50 assert(math.isPositiveInf(fabs(-math.inf(f32))));
51 assert(math.isNan(fabs(math.nan(f32))));
52}
53
54test "math.fabs64.special" {
55 assert(math.isPositiveInf(fabs(math.inf(f64))));
56 assert(math.isPositiveInf(fabs(-math.inf(f64))));
57 assert(math.isNan(fabs(math.nan(f64))));
58}
std/math/floor.zig+27
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - floor(+-0) = +-0
4// - floor(+-inf) = +-inf
5// - floor(nan) = nan
6
1const builtin = @import("builtin");7const builtin = @import("builtin");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
3const math = @import("index.zig");9const math = @import("index.zig");
...@@ -19,6 +25,11 @@ fn floor32(x: f32) -> f32 {...@@ -19,6 +25,11 @@ fn floor32(x: f32) -> f32 {
19 const e = i32((u >> 23) & 0xFF) - 0x7F;25 const e = i32((u >> 23) & 0xFF) - 0x7F;
20 var m: u32 = undefined;26 var m: u32 = undefined;
2127
28 // TODO: Shouldn't need this explicit check.
29 if (x == 0.0) {
30 return x;
31 }
32
22 if (e >= 23) {33 if (e >= 23) {
23 return x;34 return x;
24 }35 }
...@@ -90,3 +101,19 @@ test "math.floor64" {...@@ -90,3 +101,19 @@ test "math.floor64" {
90 assert(floor64(-1.3) == -2.0);101 assert(floor64(-1.3) == -2.0);
91 assert(floor64(0.2) == 0.0);102 assert(floor64(0.2) == 0.0);
92}103}
104
105test "math.floor32.special" {
106 assert(floor32(0.0) == 0.0);
107 assert(floor32(-0.0) == -0.0);
108 assert(math.isPositiveInf(floor32(math.inf(f32))));
109 assert(math.isNegativeInf(floor32(-math.inf(f32))));
110 assert(math.isNan(floor32(math.nan(f32))));
111}
112
113test "math.floor64.special" {
114 assert(floor64(0.0) == 0.0);
115 assert(floor64(-0.0) == -0.0);
116 assert(math.isPositiveInf(floor64(math.inf(f64))));
117 assert(math.isNegativeInf(floor64(-math.inf(f64))));
118 assert(math.isNan(floor64(math.nan(f64))));
119}
std/math/frexp.zig+45
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - frexp(+-0) = +-0, 0
4// - frexp(+-inf) = +-inf, 0
5// - frexp(nan) = nan, 0
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -114,3 +120,42 @@ test "math.frexp64" {...@@ -114,3 +120,42 @@ test "math.frexp64" {
114 r = frexp64(78.0234);120 r = frexp64(78.0234);
115 assert(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7);121 assert(math.approxEq(f64, r.significand, 0.609558, epsilon) and r.exponent == 7);
116}122}
123
124test "math.frexp32.special" {
125 var r: frexp32_result = undefined;
126
127 r = frexp32(0.0);
128 assert(r.significand == 0.0 and r.exponent == 0);
129
130 r = frexp32(-0.0);
131 assert(r.significand == -0.0 and r.exponent == 0);
132
133 r = frexp32(math.inf(f32));
134 assert(math.isPositiveInf(r.significand) and r.exponent == 0);
135
136 r = frexp32(-math.inf(f32));
137 assert(math.isNegativeInf(r.significand) and r.exponent == 0);
138
139 r = frexp32(math.nan(f32));
140 assert(math.isNan(r.significand) and r.exponent == 0);
141}
142
143test "math.frexp64.special" {
144 // TODO: Error on release mode (like pow)
145 var r: frexp64_result = undefined;
146
147 r = frexp64(0.0);
148 assert(r.significand == 0.0 and r.exponent == 0);
149
150 r = frexp64(-0.0);
151 assert(r.significand == -0.0 and r.exponent == 0);
152
153 r = frexp64(math.inf(f64));
154 assert(math.isPositiveInf(r.significand) and r.exponent == 0);
155
156 r = frexp64(-math.inf(f64));
157 assert(math.isNegativeInf(r.significand) and r.exponent == 0);
158
159 r = frexp64(math.nan(f64));
160 assert(math.isNan(r.significand) and r.exponent == 0);
161}
std/math/hypot.zig+25
...@@ -1,3 +1,10 @@...@@ -1,3 +1,10 @@
1// Special Cases:
2//
3// - hypot(+-inf, y) = +inf
4// - hypot(x, +-inf) = +inf
5// - hypot(nan, y) = nan
6// - hypot(x, nan) = nan
7
1const math = @import("index.zig");8const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;9const assert = @import("../debug.zig").assert;
310
...@@ -136,3 +143,21 @@ test "math.hypot64" {...@@ -136,3 +143,21 @@ test "math.hypot64" {
136 assert(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon));143 assert(math.approxEq(f64, hypot64(89.123, 382.028905), 392.286876, epsilon));
137 assert(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon));144 assert(math.approxEq(f64, hypot64(123123.234375, 529428.707813), 543556.885247, epsilon));
138}145}
146
147test "math.hypot32.special" {
148 assert(math.isPositiveInf(hypot32(math.inf(f32), 0.0)));
149 assert(math.isPositiveInf(hypot32(-math.inf(f32), 0.0)));
150 assert(math.isPositiveInf(hypot32(0.0, math.inf(f32))));
151 assert(math.isPositiveInf(hypot32(0.0, -math.inf(f32))));
152 assert(math.isNan(hypot32(math.nan(f32), 0.0)));
153 assert(math.isNan(hypot32(0.0, math.nan(f32))));
154}
155
156test "math.hypot64.special" {
157 assert(math.isPositiveInf(hypot64(math.inf(f64), 0.0)));
158 assert(math.isPositiveInf(hypot64(-math.inf(f64), 0.0)));
159 assert(math.isPositiveInf(hypot64(0.0, math.inf(f64))));
160 assert(math.isPositiveInf(hypot64(0.0, -math.inf(f64))));
161 assert(math.isNan(hypot64(math.nan(f64), 0.0)));
162 assert(math.isNan(hypot64(0.0, math.nan(f64))));
163}
std/math/ilogb.zig+31-2
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - ilogb(+-inf) = @maxValue(i32)
4// - ilogb(0) = @maxValue(i32)
5// - ilogb(nan) = @maxValue(i32)
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -21,6 +27,11 @@ fn ilogb32(x: f32) -> i32 {...@@ -21,6 +27,11 @@ fn ilogb32(x: f32) -> i32 {
21 var u = @bitCast(u32, x);27 var u = @bitCast(u32, x);
22 var e = i32((u >> 23) & 0xFF);28 var e = i32((u >> 23) & 0xFF);
2329
30 // TODO: We should be able to merge this with the lower check.
31 if (math.isNan(x)) {
32 return @maxValue(i32);
33 }
34
24 if (e == 0) {35 if (e == 0) {
25 u <<= 9;36 u <<= 9;
26 if (u == 0) {37 if (u == 0) {
...@@ -38,7 +49,7 @@ fn ilogb32(x: f32) -> i32 {...@@ -38,7 +49,7 @@ fn ilogb32(x: f32) -> i32 {
3849
39 if (e == 0xFF) {50 if (e == 0xFF) {
40 math.raiseInvalid();51 math.raiseInvalid();
41 if (u << 9 != 0) {52 if (u <<% 9 != 0) {
42 return fp_ilogbnan;53 return fp_ilogbnan;
43 } else {54 } else {
44 return @maxValue(i32);55 return @maxValue(i32);
...@@ -52,6 +63,10 @@ fn ilogb64(x: f64) -> i32 {...@@ -52,6 +63,10 @@ fn ilogb64(x: f64) -> i32 {
52 var u = @bitCast(u64, x);63 var u = @bitCast(u64, x);
53 var e = i32((u >> 52) & 0x7FF);64 var e = i32((u >> 52) & 0x7FF);
5465
66 if (math.isNan(x)) {
67 return @maxValue(i32);
68 }
69
55 if (e == 0) {70 if (e == 0) {
56 u <<= 12;71 u <<= 12;
57 if (u == 0) {72 if (u == 0) {
...@@ -69,7 +84,7 @@ fn ilogb64(x: f64) -> i32 {...@@ -69,7 +84,7 @@ fn ilogb64(x: f64) -> i32 {
6984
70 if (e == 0x7FF) {85 if (e == 0x7FF) {
71 math.raiseInvalid();86 math.raiseInvalid();
72 if (u << 12 != 0) {87 if (u <<% 12 != 0) {
73 return fp_ilogbnan;88 return fp_ilogbnan;
74 } else {89 } else {
75 return @maxValue(i32);90 return @maxValue(i32);
...@@ -101,3 +116,17 @@ test "math.ilogb64" {...@@ -101,3 +116,17 @@ test "math.ilogb64" {
101 assert(ilogb64(-123984) == 16);116 assert(ilogb64(-123984) == 16);
102 assert(ilogb64(2398.23) == 11);117 assert(ilogb64(2398.23) == 11);
103}118}
119
120test "math.ilogb32.special" {
121 assert(ilogb32(math.inf(f32)) == @maxValue(i32));
122 assert(ilogb32(-math.inf(f32)) == @maxValue(i32));
123 assert(ilogb32(0.0) == @minValue(i32));
124 assert(ilogb32(math.nan(f32)) == @maxValue(i32));
125}
126
127test "math.ilogb64.special" {
128 assert(ilogb64(math.inf(f64)) == @maxValue(i32));
129 assert(ilogb64(-math.inf(f64)) == @maxValue(i32));
130 assert(ilogb64(0.0) == @minValue(i32));
131 assert(ilogb64(math.nan(f64)) == @maxValue(i32));
132}
std/math/index.zig+2
...@@ -42,6 +42,7 @@ pub const inf_u64 = u64(0x7FF << 52);...@@ -42,6 +42,7 @@ pub const inf_u64 = u64(0x7FF << 52);
42pub const inf_f64 = @bitCast(f64, inf_u64);42pub const inf_f64 = @bitCast(f64, inf_u64);
4343
44pub const nan = @import("nan.zig").nan;44pub const nan = @import("nan.zig").nan;
45pub const snan = @import("nan.zig").snan;
45pub const inf = @import("inf.zig").inf;46pub const inf = @import("inf.zig").inf;
4647
47pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool {48pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool {
...@@ -90,6 +91,7 @@ pub fn raiseDivByZero() {...@@ -90,6 +91,7 @@ pub fn raiseDivByZero() {
90}91}
9192
92pub const isNan = @import("isnan.zig").isNan;93pub const isNan = @import("isnan.zig").isNan;
94pub const isSignalNan = @import("isnan.zig").isSignalNan;
93pub const fabs = @import("fabs.zig").fabs;95pub const fabs = @import("fabs.zig").fabs;
94pub const ceil = @import("ceil.zig").ceil;96pub const ceil = @import("ceil.zig").ceil;
95pub const floor = @import("floor.zig").floor;97pub const floor = @import("floor.zig").floor;
std/math/isnan.zig+6
...@@ -18,6 +18,12 @@ pub fn isNan(x: var) -> bool {...@@ -18,6 +18,12 @@ pub fn isNan(x: var) -> bool {
18 }18 }
19}19}
2020
21// Note: A signalling nan is identical to a standard right now by may have a different bit
22// representation in the future when required.
23pub fn isSignalNan(x: var) -> bool {
24 isNan(x)
25}
26
21test "math.isNan" {27test "math.isNan" {
22 assert(isNan(math.nan(f32)));28 assert(isNan(math.nan(f32)));
23 assert(isNan(math.nan(f64)));29 assert(isNan(math.nan(f64)));
std/math/ln.zig+27-6
...@@ -1,3 +1,10 @@...@@ -1,3 +1,10 @@
1// Special Cases:
2//
3// - ln(+inf) = +inf
4// - ln(0) = -inf
5// - ln(x) = nan if x < 0
6// - ln(nan) = nan
7
1const math = @import("index.zig");8const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;9const assert = @import("../debug.zig").assert;
310
...@@ -27,12 +34,12 @@ fn lnf(x_: f32) -> f32 {...@@ -27,12 +34,12 @@ fn lnf(x_: f32) -> f32 {
27 // x < 2^(-126)34 // x < 2^(-126)
28 if (ix < 0x00800000 or ix >> 31 != 0) {35 if (ix < 0x00800000 or ix >> 31 != 0) {
29 // log(+-0) = -inf36 // log(+-0) = -inf
30 if (ix << 1 == 0) {37 if (ix <<% 1 == 0) {
31 return -1 / (x * x);38 return -math.inf(f32);
32 }39 }
33 // log(-#) = nan40 // log(-#) = nan
34 if (ix >> 31 != 0) {41 if (ix >> 31 != 0) {
35 return (x - x) / 0.042 return math.nan(f32);
36 }43 }
3744
38 // subnormal, scale x45 // subnormal, scale x
...@@ -82,12 +89,12 @@ fn lnd(x_: f64) -> f64 {...@@ -82,12 +89,12 @@ fn lnd(x_: f64) -> f64 {
8289
83 if (hx < 0x00100000 or hx >> 31 != 0) {90 if (hx < 0x00100000 or hx >> 31 != 0) {
84 // log(+-0) = -inf91 // log(+-0) = -inf
85 if (ix << 1 == 0) {92 if (ix <<% 1 == 0) {
86 return -1 / (x * x);93 return -math.inf(f64);
87 }94 }
88 // log(-#) = nan95 // log(-#) = nan
89 if (hx >> 31 != 0) {96 if (hx >> 31 != 0) {
90 return (x - x) / 0.0;97 return math.nan(f64);
91 }98 }
9299
93 // subnormal, scale x100 // subnormal, scale x
...@@ -148,3 +155,17 @@ test "math.ln64" {...@@ -148,3 +155,17 @@ test "math.ln64" {
148 assert(math.approxEq(f64, lnd(89.123), 4.490017, epsilon));155 assert(math.approxEq(f64, lnd(89.123), 4.490017, epsilon));
149 assert(math.approxEq(f64, lnd(123123.234375), 11.720941, epsilon));156 assert(math.approxEq(f64, lnd(123123.234375), 11.720941, epsilon));
150}157}
158
159test "math.ln32.special" {
160 assert(math.isPositiveInf(lnf(math.inf(f32))));
161 assert(math.isNegativeInf(lnf(0.0)));
162 assert(math.isNan(lnf(-1.0)));
163 assert(math.isNan(lnf(math.nan(f32))));
164}
165
166test "math.ln64.special" {
167 assert(math.isPositiveInf(lnd(math.inf(f64))));
168 assert(math.isNegativeInf(lnd(0.0)));
169 assert(math.isNan(lnd(-1.0)));
170 assert(math.isNan(lnd(math.nan(f64))));
171}
std/math/log10.zig+27-6
...@@ -1,3 +1,10 @@...@@ -1,3 +1,10 @@
1// Special Cases:
2//
3// - log10(+inf) = +inf
4// - log10(0) = -inf
5// - log10(x) = nan if x < 0
6// - log10(nan) = nan
7
1const math = @import("index.zig");8const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;9const assert = @import("../debug.zig").assert;
310
...@@ -31,12 +38,12 @@ fn log10_32(x_: f32) -> f32 {...@@ -31,12 +38,12 @@ fn log10_32(x_: f32) -> f32 {
31 // x < 2^(-126)38 // x < 2^(-126)
32 if (ix < 0x00800000 or ix >> 31 != 0) {39 if (ix < 0x00800000 or ix >> 31 != 0) {
33 // log(+-0) = -inf40 // log(+-0) = -inf
34 if (ix << 1 == 0) {41 if (ix <<% 1 == 0) {
35 return -1 / (x * x);42 return -math.inf(f32);
36 }43 }
37 // log(-#) = nan44 // log(-#) = nan
38 if (ix >> 31 != 0) {45 if (ix >> 31 != 0) {
39 return (x - x) / 0.046 return math.nan(f32);
40 }47 }
4148
42 k -= 25;49 k -= 25;
...@@ -93,12 +100,12 @@ fn log10_64(x_: f64) -> f64 {...@@ -93,12 +100,12 @@ fn log10_64(x_: f64) -> f64 {
93100
94 if (hx < 0x00100000 or hx >> 31 != 0) {101 if (hx < 0x00100000 or hx >> 31 != 0) {
95 // log(+-0) = -inf102 // log(+-0) = -inf
96 if (ix << 1 == 0) {103 if (ix <<% 1 == 0) {
97 return -1 / (x * x);104 return -math.inf(f32);
98 }105 }
99 // log(-#) = nan106 // log(-#) = nan
100 if (hx >> 31 != 0) {107 if (hx >> 31 != 0) {
101 return (x - x) / 0.0;108 return math.nan(f32);
102 }109 }
103110
104 // subnormal, scale x111 // subnormal, scale x
...@@ -176,3 +183,17 @@ test "math.log10_64" {...@@ -176,3 +183,17 @@ test "math.log10_64" {
176 assert(math.approxEq(f64, log10_64(89.123), 1.94999, epsilon));183 assert(math.approxEq(f64, log10_64(89.123), 1.94999, epsilon));
177 assert(math.approxEq(f64, log10_64(123123.234375), 5.09034, epsilon));184 assert(math.approxEq(f64, log10_64(123123.234375), 5.09034, epsilon));
178}185}
186
187test "math.log10_32.special" {
188 assert(math.isPositiveInf(log10_32(math.inf(f32))));
189 assert(math.isNegativeInf(log10_32(0.0)));
190 assert(math.isNan(log10_32(-1.0)));
191 assert(math.isNan(log10_32(math.nan(f32))));
192}
193
194test "math.log10_64.special" {
195 assert(math.isPositiveInf(log10_64(math.inf(f64))));
196 assert(math.isNegativeInf(log10_64(0.0)));
197 assert(math.isNan(log10_64(-1.0)));
198 assert(math.isNan(log10_64(math.nan(f64))));
199}
std/math/log1p.zig+35-9
...@@ -1,3 +1,11 @@...@@ -1,3 +1,11 @@
1// Special Cases:
2//
3// - log1p(+inf) = +inf
4// - log1p(+-0) = +-0
5// - log1p(-1) = -inf
6// - log1p(x) = nan if x < -1
7// - log1p(nan) = nan
8
1const math = @import("index.zig");9const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;10const assert = @import("../debug.zig").assert;
311
...@@ -31,17 +39,17 @@ fn log1p_32(x: f32) -> f32 {...@@ -31,17 +39,17 @@ fn log1p_32(x: f32) -> f32 {
31 if (ix < 0x3ED413D0 or ix >> 31 != 0) {39 if (ix < 0x3ED413D0 or ix >> 31 != 0) {
32 // x <= -1.040 // x <= -1.0
33 if (ix >= 0xBF800000) {41 if (ix >= 0xBF800000) {
34 // log1p(-1) = +inf42 // log1p(-1) = -inf
35 if (x == -1) {43 if (x == -1.0) {
36 return x / 0.0;44 return -math.inf(f32);
37 }45 }
38 // log1p(x < -1) = nan46 // log1p(x < -1) = nan
39 else {47 else {
40 return (x - x) / 0.0;48 return math.nan(f32);
41 }49 }
42 }50 }
43 // |x| < 2^(-24)51 // |x| < 2^(-24)
44 if ((ix << 1) < (0x33800000 << 1)) {52 if ((ix <<% 1) < (0x33800000 << 1)) {
45 // underflow if subnormal53 // underflow if subnormal
46 if (ix & 0x7F800000 == 0) {54 if (ix & 0x7F800000 == 0) {
47 math.forceEval(x * x);55 math.forceEval(x * x);
...@@ -111,16 +119,16 @@ fn log1p_64(x: f64) -> f64 {...@@ -111,16 +119,16 @@ fn log1p_64(x: f64) -> f64 {
111 // x <= -1.0119 // x <= -1.0
112 if (hx >= 0xBFF00000) {120 if (hx >= 0xBFF00000) {
113 // log1p(-1) = -inf121 // log1p(-1) = -inf
114 if (x == 1) {122 if (x == -1.0) {
115 return x / 0.0;123 return -math.inf(f64);
116 }124 }
117 // log1p(x < -1) = nan125 // log1p(x < -1) = nan
118 else {126 else {
119 return (x - x) / 0.0;127 return math.nan(f64);
120 }128 }
121 }129 }
122 // |x| < 2^(-53)130 // |x| < 2^(-53)
123 if ((hx << 1) < (0x3CA00000 << 1)) {131 if ((hx <<% 1) < (0x3CA00000 << 1)) {
124 if ((hx & 0x7FF00000) == 0) {132 if ((hx & 0x7FF00000) == 0) {
125 math.raiseUnderflow();133 math.raiseUnderflow();
126 }134 }
...@@ -198,3 +206,21 @@ test "math.log1p_64" {...@@ -198,3 +206,21 @@ test "math.log1p_64" {
198 assert(math.approxEq(f64, log1p_64(89.123), 4.501175, epsilon));206 assert(math.approxEq(f64, log1p_64(89.123), 4.501175, epsilon));
199 assert(math.approxEq(f64, log1p_64(123123.234375), 11.720949, epsilon));207 assert(math.approxEq(f64, log1p_64(123123.234375), 11.720949, epsilon));
200}208}
209
210test "math.log1p_32.special" {
211 assert(math.isPositiveInf(log1p_32(math.inf(f32))));
212 assert(log1p_32(0.0) == 0.0);
213 assert(log1p_32(-0.0) == -0.0);
214 assert(math.isNegativeInf(log1p_32(-1.0)));
215 assert(math.isNan(log1p_32(-2.0)));
216 assert(math.isNan(log1p_32(math.nan(f32))));
217}
218
219test "math.log1p_64.special" {
220 assert(math.isPositiveInf(log1p_64(math.inf(f64))));
221 assert(log1p_64(0.0) == 0.0);
222 assert(log1p_64(-0.0) == -0.0);
223 assert(math.isNegativeInf(log1p_64(-1.0)));
224 assert(math.isNan(log1p_64(-2.0)));
225 assert(math.isNan(log1p_64(math.nan(f64))));
226}
std/math/log2.zig+27-6
...@@ -1,3 +1,10 @@...@@ -1,3 +1,10 @@
1// Special Cases:
2//
3// - log2(+inf) = +inf
4// - log2(0) = -inf
5// - log2(x) = nan if x < 0
6// - log2(nan) = nan
7
1const math = @import("index.zig");8const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;9const assert = @import("../debug.zig").assert;
310
...@@ -29,12 +36,12 @@ fn log2_32(x_: f32) -> f32 {...@@ -29,12 +36,12 @@ fn log2_32(x_: f32) -> f32 {
29 // x < 2^(-126)36 // x < 2^(-126)
30 if (ix < 0x00800000 or ix >> 31 != 0) {37 if (ix < 0x00800000 or ix >> 31 != 0) {
31 // log(+-0) = -inf38 // log(+-0) = -inf
32 if (ix << 1 == 0) {39 if (ix <<% 1 == 0) {
33 return -1 / (x * x);40 return -math.inf(f32);
34 }41 }
35 // log(-#) = nan42 // log(-#) = nan
36 if (ix >> 31 != 0) {43 if (ix >> 31 != 0) {
37 return (x - x) / 0.044 return math.nan(f32);
38 }45 }
3946
40 k -= 25;47 k -= 25;
...@@ -87,12 +94,12 @@ fn log2_64(x_: f64) -> f64 {...@@ -87,12 +94,12 @@ fn log2_64(x_: f64) -> f64 {
8794
88 if (hx < 0x00100000 or hx >> 31 != 0) {95 if (hx < 0x00100000 or hx >> 31 != 0) {
89 // log(+-0) = -inf96 // log(+-0) = -inf
90 if (ix << 1 == 0) {97 if (ix <<% 1 == 0) {
91 return -1 / (x * x);98 return -math.inf(f64);
92 }99 }
93 // log(-#) = nan100 // log(-#) = nan
94 if (hx >> 31 != 0) {101 if (hx >> 31 != 0) {
95 return (x - x) / 0.0;102 return math.nan(f64);
96 }103 }
97104
98 // subnormal, scale x105 // subnormal, scale x
...@@ -166,3 +173,17 @@ test "math.log2_64" {...@@ -166,3 +173,17 @@ test "math.log2_64" {
166 assert(math.approxEq(f64, log2_64(37.45), 5.226894, epsilon));173 assert(math.approxEq(f64, log2_64(37.45), 5.226894, epsilon));
167 assert(math.approxEq(f64, log2_64(123123.234375), 16.909744, epsilon));174 assert(math.approxEq(f64, log2_64(123123.234375), 16.909744, epsilon));
168}175}
176
177test "math.log2_32.special" {
178 assert(math.isPositiveInf(log2_32(math.inf(f32))));
179 assert(math.isNegativeInf(log2_32(0.0)));
180 assert(math.isNan(log2_32(-1.0)));
181 assert(math.isNan(log2_32(math.nan(f32))));
182}
183
184test "math.log2_64.special" {
185 assert(math.isPositiveInf(log2_64(math.inf(f64))));
186 assert(math.isNegativeInf(log2_64(0.0)));
187 assert(math.isNan(log2_64(-1.0)));
188 assert(math.isNan(log2_64(math.nan(f64))));
189}
std/math/modf.zig+46-2
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1// Special Cases:
2//
3// - modf(+-inf) = +-inf, nan
4// - modf(nan) = nan, nan
5
1const math = @import("index.zig");6const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;7const assert = @import("../debug.zig").assert;
38
...@@ -29,10 +34,17 @@ fn modf32(x: f32) -> modf32_result {...@@ -29,10 +34,17 @@ fn modf32(x: f32) -> modf32_result {
29 const e = i32((u >> 23) & 0xFF) - 0x7F;34 const e = i32((u >> 23) & 0xFF) - 0x7F;
30 const us = u & 0x80000000;35 const us = u & 0x80000000;
3136
37 // TODO: Shouldn't need this.
38 if (math.isInf(x)) {
39 result.ipart = x;
40 result.fpart = math.nan(f32);
41 return result;
42 }
43
32 // no fractional part44 // no fractional part
33 if (e >= 23) {45 if (e >= 23) {
34 result.ipart = x;46 result.ipart = x;
35 if (e == 0x80 and u << 9 != 0) { // nan47 if (e == 0x80 and u <<% 9 != 0) { // nan
36 result.fpart = x;48 result.fpart = x;
37 } else {49 } else {
38 result.fpart = @bitCast(f32, us);50 result.fpart = @bitCast(f32, us);
...@@ -67,10 +79,16 @@ fn modf64(x: f64) -> modf64_result {...@@ -67,10 +79,16 @@ fn modf64(x: f64) -> modf64_result {
67 const e = i32((u >> 52) & 0x7FF) - 0x3FF;79 const e = i32((u >> 52) & 0x7FF) - 0x3FF;
68 const us = u & (1 << 63);80 const us = u & (1 << 63);
6981
82 if (math.isInf(x)) {
83 result.ipart = x;
84 result.fpart = math.nan(f64);
85 return result;
86 }
87
70 // no fractional part88 // no fractional part
71 if (e >= 52) {89 if (e >= 52) {
72 result.ipart = x;90 result.ipart = x;
73 if (e == 0x400 and u << 12 != 0) { // nan91 if (e == 0x400 and u <<% 12 != 0) { // nan
74 result.fpart = x;92 result.fpart = x;
75 } else {93 } else {
76 result.fpart = @bitCast(f64, us);94 result.fpart = @bitCast(f64, us);
...@@ -158,3 +176,29 @@ test "math.modf64" {...@@ -158,3 +176,29 @@ test "math.modf64" {
158 assert(math.approxEq(f64, r.ipart, 1234, epsilon));176 assert(math.approxEq(f64, r.ipart, 1234, epsilon));
159 assert(math.approxEq(f64, r.fpart, 0.340780, epsilon));177 assert(math.approxEq(f64, r.fpart, 0.340780, epsilon));
160}178}
179
180test "math.modf32.special" {
181 var r: modf32_result = undefined;
182
183 r = modf32(math.inf(f32));
184 assert(math.isPositiveInf(r.ipart) and math.isNan(r.fpart));
185
186 r = modf32(-math.inf(f32));
187 assert(math.isNegativeInf(r.ipart) and math.isNan(r.fpart));
188
189 r = modf32(math.nan(f32));
190 assert(math.isNan(r.ipart) and math.isNan(r.fpart));
191}
192
193test "math.modf64.special" {
194 var r: modf64_result = undefined;
195
196 r = modf64(math.inf(f64));
197 assert(math.isPositiveInf(r.ipart) and math.isNan(r.fpart));
198
199 r = modf64(-math.inf(f64));
200 assert(math.isNegativeInf(r.ipart) and math.isNan(r.fpart));
201
202 r = modf64(math.nan(f64));
203 assert(math.isNan(r.ipart) and math.isNan(r.fpart));
204}
std/math/nan.zig+12
...@@ -9,3 +9,15 @@ pub fn nan_workaround(comptime T: type) -> T {...@@ -9,3 +9,15 @@ pub fn nan_workaround(comptime T: type) -> T {
9 else => @compileError("nan not implemented for " ++ @typeName(T)),9 else => @compileError("nan not implemented for " ++ @typeName(T)),
10 }10 }
11}11}
12
13pub const snan = snan_workaround;
14
15// Note: A signalling nan is identical to a standard right now by may have a different bit
16// representation in the future when required.
17pub fn snan_workaround(comptime T: type) -> T {
18 switch (T) {
19 f32 => @bitCast(f32, math.nan_u32),
20 f64 => @bitCast(f64, math.nan_u64),
21 else => @compileError("snan not implemented for " ++ @typeName(T)),
22 }
23}
std/math/pow.zig+67-4
...@@ -1,3 +1,26 @@...@@ -1,3 +1,26 @@
1// Special Cases:
2//
3// pow(x, +-0) = 1 for any x
4// pow(1, y) = 1 for any y
5// pow(x, 1) = x for any x
6// pow(nan, y) = nan
7// pow(x, nan) = nan
8// pow(+-0, y) = +-inf for y an odd integer < 0
9// pow(+-0, -inf) = +inf
10// pow(+-0, +inf) = +0
11// pow(+-0, y) = +inf for finite y < 0 and not an odd integer
12// pow(+-0, y) = +-0 for y an odd integer > 0
13// pow(+-0, y) = +0 for finite y > 0 and not an odd integer
14// pow(-1, +-inf) = 1
15// pow(x, +inf) = +inf for |x| > 1
16// pow(x, -inf) = +0 for |x| > 1
17// pow(x, +inf) = +0 for |x| < 1
18// pow(x, -inf) = +inf for |x| < 1
19// pow(+inf, y) = +inf for y > 0
20// pow(+inf, y) = +0 for y < 0
21// pow(-inf, y) = pow(-0, -y)
22// pow(x, y) = nan for finite x < 0 and finite non-integer y
23
1const math = @import("index.zig");24const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;25const assert = @import("../debug.zig").assert;
326
...@@ -59,9 +82,9 @@ pub fn pow_workaround(comptime T: type, x: T, y: T) -> T {...@@ -59,9 +82,9 @@ pub fn pow_workaround(comptime T: type, x: T, y: T) -> T {
59 }82 }
6083
61 if (math.isInf(y)) {84 if (math.isInf(y)) {
62 // pow(-1, inf) = -1 for all x85 // pow(-1, inf) = 1 for all x
63 if (x == -1) {86 if (x == -1) {
64 return -1;87 return 1.0;
65 }88 }
66 // pow(x, +inf) = +0 for |x| < 189 // pow(x, +inf) = +0 for |x| < 1
67 // pow(x, -inf) = +0 for |x| > 190 // pow(x, -inf) = +0 for |x| > 1
...@@ -156,17 +179,57 @@ fn isOddInteger(x: f64) -> bool {...@@ -156,17 +179,57 @@ fn isOddInteger(x: f64) -> bool {
156test "math.pow" {179test "math.pow" {
157 const epsilon = 0.000001;180 const epsilon = 0.000001;
158181
159 // assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero182 // TODO: Error on release
183 assert(math.approxEq(f32, pow(f32, 0.0, 3.3), 0.0, epsilon));
160 assert(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon));184 assert(math.approxEq(f32, pow(f32, 0.8923, 3.3), 0.686572, epsilon));
161 assert(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon));185 assert(math.approxEq(f32, pow(f32, 0.2, 3.3), 0.004936, epsilon));
162 assert(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon));186 assert(math.approxEq(f32, pow(f32, 1.5, 3.3), 3.811546, epsilon));
163 assert(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon));187 assert(math.approxEq(f32, pow(f32, 37.45, 3.3), 155736.703125, epsilon));
164 assert(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon));188 assert(math.approxEq(f32, pow(f32, 89.123, 3.3), 2722489.5, epsilon));
165189
166 // assert(math.approxEq(f32, pow(f64, 0.0, 3.3), 0.0, epsilon)); // TODO: Handle div zero190 assert(math.approxEq(f64, pow(f64, 0.0, 3.3), 0.0, epsilon));
167 assert(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon));191 assert(math.approxEq(f64, pow(f64, 0.8923, 3.3), 0.686572, epsilon));
168 assert(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon));192 assert(math.approxEq(f64, pow(f64, 0.2, 3.3), 0.004936, epsilon));
169 assert(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon));193 assert(math.approxEq(f64, pow(f64, 1.5, 3.3), 3.811546, epsilon));
170 assert(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon));194 assert(math.approxEq(f64, pow(f64, 37.45, 3.3), 155736.7160616, epsilon));
171 assert(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon));195 assert(math.approxEq(f64, pow(f64, 89.123, 3.3), 2722490.231436, epsilon));
172}196}
197
198test "math.pow.special" {
199 const epsilon = 0.000001;
200
201 assert(pow(f32, 4, 0.0) == 1.0);
202 assert(pow(f32, 7, -0.0) == 1.0);
203 assert(pow(f32, 45, 1.0) == 45);
204 assert(pow(f32, -45, 1.0) == -45);
205 assert(math.isNan(pow(f32, math.nan(f32), 5.0)));
206 assert(math.isNan(pow(f32, 5.0, math.nan(f32))));
207 assert(math.isPositiveInf(pow(f32, 0.0, -1.0)));
208 assert(math.isNegativeInf(pow(f32, -0.0, -3.0)));
209 assert(math.isPositiveInf(pow(f32, 0.0, -math.inf(f32))));
210 assert(math.isPositiveInf(pow(f32, -0.0, -math.inf(f32))));
211 assert(pow(f32, 0.0, math.inf(f32)) == 0.0);
212 assert(pow(f32, -0.0, math.inf(f32)) == 0.0);
213 assert(math.isPositiveInf(pow(f32, 0.0, -2.0)));
214 assert(math.isPositiveInf(pow(f32, -0.0, -2.0)));
215 assert(pow(f32, 0.0, 1.0) == 0.0);
216 assert(pow(f32, -0.0, 1.0) == -0.0);
217 assert(pow(f32, 0.0, 2.0) == 0.0);
218 assert(pow(f32, -0.0, 2.0) == 0.0);
219 assert(math.approxEq(f32, pow(f32, -1.0, math.inf(f32)), 1.0, epsilon));
220 assert(math.approxEq(f32, pow(f32, -1.0, -math.inf(f32)), 1.0, epsilon));
221 assert(math.isPositiveInf(pow(f32, 1.2, math.inf(f32))));
222 assert(math.isPositiveInf(pow(f32, -1.2, math.inf(f32))));
223 assert(pow(f32, 1.2, -math.inf(f32)) == 0.0);
224 assert(pow(f32, -1.2, -math.inf(f32)) == 0.0);
225 assert(pow(f32, 0.2, math.inf(f32)) == 0.0);
226 assert(pow(f32, -0.2, math.inf(f32)) == 0.0);
227 assert(math.isPositiveInf(pow(f32, 0.2, -math.inf(f32))));
228 assert(math.isPositiveInf(pow(f32, -0.2, -math.inf(f32))));
229 assert(math.isPositiveInf(pow(f32, math.inf(f32), 1.0)));
230 assert(pow(f32, math.inf(f32), -1.0) == 0.0);
231 assert(pow(f32, -math.inf(f32), 5.0) == pow(f32, -0.0, -5.0));
232 assert(pow(f32, -math.inf(f32), -5.2) == pow(f32, -0.0, 5.2));
233 assert(math.isNan(pow(f32, -1.0, 1.2)));
234 assert(math.isNan(pow(f32, -12.4, 78.5)));
235}
std/math/round.zig+22
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - round(+-0) = +-0
4// - round(+-inf) = +-inf
5// - round(nan) = nan
6
1const builtin = @import("builtin");7const builtin = @import("builtin");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
3const math = @import("index.zig");9const math = @import("index.zig");
...@@ -106,3 +112,19 @@ test "math.round64" {...@@ -106,3 +112,19 @@ test "math.round64" {
106 assert(round64(0.2) == 0.0);112 assert(round64(0.2) == 0.0);
107 assert(round64(1.8) == 2.0);113 assert(round64(1.8) == 2.0);
108}114}
115
116test "math.round32.special" {
117 assert(round32(0.0) == 0.0);
118 assert(round32(-0.0) == -0.0);
119 assert(math.isPositiveInf(round32(math.inf(f32))));
120 assert(math.isNegativeInf(round32(-math.inf(f32))));
121 assert(math.isNan(round32(math.nan(f32))));
122}
123
124test "math.round64.special" {
125 assert(round64(0.0) == 0.0);
126 assert(round64(-0.0) == -0.0);
127 assert(math.isPositiveInf(round64(math.inf(f64))));
128 assert(math.isNegativeInf(round64(-math.inf(f64))));
129 assert(math.isNan(round64(math.nan(f64))));
130}
std/math/sin.zig+22
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - sin(+-0) = +-0
4// - sin(+-inf) = nan
5// - sin(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -164,3 +170,19 @@ test "math.sin64" {...@@ -164,3 +170,19 @@ test "math.sin64" {
164 assert(math.approxEq(f64, sin64(37.45), -0.246543, epsilon));170 assert(math.approxEq(f64, sin64(37.45), -0.246543, epsilon));
165 assert(math.approxEq(f64, sin64(89.123), 0.916166, epsilon));171 assert(math.approxEq(f64, sin64(89.123), 0.916166, epsilon));
166}172}
173
174test "math.sin32.special" {
175 assert(sin32(0.0) == 0.0);
176 assert(sin32(-0.0) == -0.0);
177 assert(math.isNan(sin32(math.inf(f32))));
178 assert(math.isNan(sin32(-math.inf(f32))));
179 assert(math.isNan(sin32(math.nan(f32))));
180}
181
182test "math.sin64.special" {
183 assert(sin64(0.0) == 0.0);
184 assert(sin64(-0.0) == -0.0);
185 assert(math.isNan(sin64(math.inf(f64))));
186 assert(math.isNan(sin64(-math.inf(f64))));
187 assert(math.isNan(sin64(math.nan(f64))));
188}
std/math/sinh.zig+26-1
...@@ -1,6 +1,12 @@...@@ -1,6 +1,12 @@
1// Special Cases:
2//
3// - sinh(+-0) = +-0
4// - sinh(+-inf) = +-inf
5// - sinh(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
3const expo2 = @import("_expo2.zig").expo2;9const expo2 = @import("expo2.zig").expo2;
410
5// TODO issue #39311// TODO issue #393
6pub const sinh = sinh_workaround;12pub const sinh = sinh_workaround;
...@@ -45,6 +51,8 @@ fn sinh32(x: f32) -> f32 {...@@ -45,6 +51,8 @@ fn sinh32(x: f32) -> f32 {
45}51}
4652
47fn sinh64(x: f64) -> f64 {53fn sinh64(x: f64) -> f64 {
54 @setFloatMode(this, @import("builtin").FloatMode.Strict);
55
48 const u = @bitCast(u64, x);56 const u = @bitCast(u64, x);
49 const w = u32(u >> 32);57 const w = u32(u >> 32);
50 const ax = @bitCast(f64, u & (@maxValue(u64) >> 1));58 const ax = @bitCast(f64, u & (@maxValue(u64) >> 1));
...@@ -94,3 +102,20 @@ test "math.sinh64" {...@@ -94,3 +102,20 @@ test "math.sinh64" {
94 assert(math.approxEq(f64, sinh64(0.8923), 1.015512, epsilon));102 assert(math.approxEq(f64, sinh64(0.8923), 1.015512, epsilon));
95 assert(math.approxEq(f64, sinh64(1.5), 2.129279, epsilon));103 assert(math.approxEq(f64, sinh64(1.5), 2.129279, epsilon));
96}104}
105
106test "math.sinh32.special" {
107 assert(sinh32(0.0) == 0.0);
108 assert(sinh32(-0.0) == -0.0);
109 assert(math.isPositiveInf(sinh32(math.inf(f32))));
110 assert(math.isNegativeInf(sinh32(-math.inf(f32))));
111 assert(math.isNan(sinh32(math.nan(f32))));
112}
113
114test "math.sinh64.special" {
115 // TODO: Error on release mode (like pow)
116 assert(sinh64(0.0) == 0.0);
117 assert(sinh64(-0.0) == -0.0);
118 assert(math.isPositiveInf(sinh64(math.inf(f64))));
119 assert(math.isNegativeInf(sinh64(-math.inf(f64))));
120 assert(math.isNan(sinh64(math.nan(f64))));
121}
std/math/sqrt.zig+26-3
...@@ -1,3 +1,10 @@...@@ -1,3 +1,10 @@
1// Special Cases:
2//
3// - sqrt(+inf) = +inf
4// - sqrt(+-0) = +-0
5// - sqrt(x) = nan if x < 0
6// - sqrt(nan) = nan
7
1const math = @import("index.zig");8const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;9const assert = @import("../debug.zig").assert;
310
...@@ -28,7 +35,7 @@ fn sqrt32(x: f32) -> f32 {...@@ -28,7 +35,7 @@ fn sqrt32(x: f32) -> f32 {
28 return x; // sqrt (+-0) = +-035 return x; // sqrt (+-0) = +-0
29 }36 }
30 if (ix < 0) {37 if (ix < 0) {
31 return (x - x) / (x - x); // sqrt(-ve) = snan38 return math.snan(f32);
32 }39 }
33 }40 }
3441
...@@ -106,12 +113,12 @@ fn sqrt64(x: f64) -> f64 {...@@ -106,12 +113,12 @@ fn sqrt64(x: f64) -> f64 {
106 }113 }
107114
108 // sqrt(+-0) = +-0115 // sqrt(+-0) = +-0
109 if ((ix0 & ~sign) | ix0 == 0) {116 if (x == 0.0) {
110 return x;117 return x;
111 }118 }
112 // sqrt(-ve) = snan119 // sqrt(-ve) = snan
113 if (ix0 & sign != 0) {120 if (ix0 & sign != 0) {
114 return (x - x) / (x - x);121 return math.snan(f64);
115 }122 }
116123
117 // normalize x124 // normalize x
...@@ -254,3 +261,19 @@ test "math.sqrt64" {...@@ -254,3 +261,19 @@ test "math.sqrt64" {
254 assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));261 assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));
255 assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));262 assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));
256}263}
264
265test "math.sqrt32.special" {
266 assert(math.isPositiveInf(sqrt32(math.inf(f32))));
267 assert(sqrt32(0.0) == 0.0);
268 assert(sqrt32(-0.0) == -0.0);
269 assert(math.isNan(sqrt32(-1.0)));
270 assert(math.isNan(sqrt32(math.nan(f32))));
271}
272
273test "math.sqrt64.special" {
274 assert(math.isPositiveInf(sqrt64(math.inf(f64))));
275 assert(sqrt64(0.0) == 0.0);
276 assert(sqrt64(-0.0) == -0.0);
277 assert(math.isNan(sqrt64(-1.0)));
278 assert(math.isNan(sqrt64(math.nan(f64))));
279}
std/math/tan.zig+22
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - tan(+-0) = +-0
4// - tan(+-inf) = nan
5// - tan(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -150,3 +156,19 @@ test "math.tan64" {...@@ -150,3 +156,19 @@ test "math.tan64" {
150 assert(math.approxEq(f64, tan64(37.45), -0.254397, epsilon));156 assert(math.approxEq(f64, tan64(37.45), -0.254397, epsilon));
151 assert(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon));157 assert(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon));
152}158}
159
160test "math.tan32.special" {
161 assert(tan32(0.0) == 0.0);
162 assert(tan32(-0.0) == -0.0);
163 assert(math.isNan(tan32(math.inf(f32))));
164 assert(math.isNan(tan32(-math.inf(f32))));
165 assert(math.isNan(tan32(math.nan(f32))));
166}
167
168test "math.tan64.special" {
169 assert(tan64(0.0) == 0.0);
170 assert(tan64(-0.0) == -0.0);
171 assert(math.isNan(tan64(math.inf(f64))));
172 assert(math.isNan(tan64(-math.inf(f64))));
173 assert(math.isNan(tan64(math.nan(f64))));
174}
std/math/tanh.zig+34-3
...@@ -1,6 +1,12 @@...@@ -1,6 +1,12 @@
1// Special Cases:
2//
3// - sinh(+-0) = +-0
4// - sinh(+-inf) = +-1
5// - sinh(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
3const expo2 = @import("_expo2.zig").expo2;9const expo2 = @import("expo2.zig").expo2;
410
5// TODO issue #39311// TODO issue #393
6pub const tanh = tanh_workaround;12pub const tanh = tanh_workaround;
...@@ -64,11 +70,19 @@ fn tanh64(x: f64) -> f64 {...@@ -64,11 +70,19 @@ fn tanh64(x: f64) -> f64 {
6470
65 var t: f64 = undefined;71 var t: f64 = undefined;
6672
73 // TODO: Shouldn't need these checks.
74 if (x == 0.0) {
75 return x;
76 }
77 if (math.isNan(x)) {
78 return x;
79 }
80
67 // |x| < log(3) / 2 ~= 0.5493 or nan81 // |x| < log(3) / 2 ~= 0.5493 or nan
68 if (w > 0x3Fe193EA) {82 if (w > 0x3FE193EA) {
69 // |x| > 20 or nan83 // |x| > 20 or nan
70 if (w > 0x40340000) {84 if (w > 0x40340000) {
71 t = 1.0 + 0 / x;85 t = 1.0; // TODO + 0 / x;
72 } else {86 } else {
73 t = math.expm1(2 * x);87 t = math.expm1(2 * x);
74 t = 1 - 2 / (t + 2);88 t = 1 - 2 / (t + 2);
...@@ -121,3 +135,20 @@ test "math.tanh64" {...@@ -121,3 +135,20 @@ test "math.tanh64" {
121 assert(math.approxEq(f64, tanh64(1.5), 0.905148, epsilon));135 assert(math.approxEq(f64, tanh64(1.5), 0.905148, epsilon));
122 assert(math.approxEq(f64, tanh64(37.45), 1.0, epsilon));136 assert(math.approxEq(f64, tanh64(37.45), 1.0, epsilon));
123}137}
138
139test "math.tanh32.special" {
140 // TODO: Error on release (like pow)
141 assert(tanh32(0.0) == 0.0);
142 assert(tanh32(-0.0) == -0.0);
143 assert(tanh32(math.inf(f32)) == 1.0);
144 assert(tanh32(-math.inf(f32)) == -1.0);
145 assert(math.isNan(tanh32(math.nan(f32))));
146}
147
148test "math.tanh64.special" {
149 assert(tanh64(0.0) == 0.0);
150 assert(tanh64(-0.0) == -0.0);
151 assert(tanh64(math.inf(f64)) == 1.0);
152 assert(tanh64(-math.inf(f64)) == -1.0);
153 assert(math.isNan(tanh64(math.nan(f64))));
154}
std/math/trunc.zig+22
...@@ -1,3 +1,9 @@...@@ -1,3 +1,9 @@
1// Special Cases:
2//
3// - trunc(+-0) = +-0
4// - trunc(+-inf) = +-inf
5// - trunc(nan) = nan
6
1const math = @import("index.zig");7const math = @import("index.zig");
2const assert = @import("../debug.zig").assert;8const assert = @import("../debug.zig").assert;
39
...@@ -70,3 +76,19 @@ test "math.trunc64" {...@@ -70,3 +76,19 @@ test "math.trunc64" {
70 assert(trunc64(-1.3) == -1.0);76 assert(trunc64(-1.3) == -1.0);
71 assert(trunc64(0.2) == 0.0);77 assert(trunc64(0.2) == 0.0);
72}78}
79
80test "math.trunc32.special" {
81 assert(trunc32(0.0) == 0.0); // 0x3F800000
82 assert(trunc32(-0.0) == -0.0);
83 assert(math.isPositiveInf(trunc32(math.inf(f32))));
84 assert(math.isNegativeInf(trunc32(-math.inf(f32))));
85 assert(math.isNan(trunc32(math.nan(f32))));
86}
87
88test "math.trunc64.special" {
89 assert(trunc64(0.0) == 0.0);
90 assert(trunc64(-0.0) == -0.0);
91 assert(math.isPositiveInf(trunc64(math.inf(f64))));
92 assert(math.isNegativeInf(trunc64(-math.inf(f64))));
93 assert(math.isNan(trunc64(math.nan(f64))));
94}