authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-28 13:34:38-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-04-28 13:34:38-04:00
log360ecc1a2f72967f3a3882b3327e130bdc4e18c0
treec02dfab372e5b79bc2130d666c5e0a0e5cb3af2e
parentd5fcb509881e1b022d2bcef303b53b4f67db1c9a
parent11911f55a73a49e2fda85bddd38d1993b93547c9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11532 from ziglang/compiler-rt-math

compiler-rt math functions reorg

108 files changed, 6138 insertions(+), 5683 deletions(-)

CMakeLists.txt+33-1
......@@ -445,7 +445,6 @@ set(ZIG_STAGE2_SOURCES
445445 "${CMAKE_SOURCE_DIR}/lib/std/math/big.zig"
446446 "${CMAKE_SOURCE_DIR}/lib/std/math/big/int.zig"
447447 "${CMAKE_SOURCE_DIR}/lib/std/math/float.zig"
448 "${CMAKE_SOURCE_DIR}/lib/std/math/floor.zig"
449448 "${CMAKE_SOURCE_DIR}/lib/std/math/frexp.zig"
450449 "${CMAKE_SOURCE_DIR}/lib/std/math/isinf.zig"
451450 "${CMAKE_SOURCE_DIR}/lib/std/math/isnan.zig"
......@@ -482,20 +481,40 @@ set(ZIG_STAGE2_SOURCES
482481 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig"
483482 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/absv.zig"
484483 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/addXf3.zig"
484 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/addo.zig"
485 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/arm.zig"
485486 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/atomics.zig"
487 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/aulldiv.zig"
488 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/aullrem.zig"
486489 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/bswap.zig"
490 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/ceil.zig"
487491 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/clear_cache.zig"
488492 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cmp.zig"
489493 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/compareXf2.zig"
494 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cos.zig"
490495 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/count0bits.zig"
491496 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divdf3.zig"
492497 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divsf3.zig"
493498 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divtf3.zig"
494499 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divti3.zig"
500 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divxf3.zig"
501 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/emutls.zig"
502 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/exp.zig"
503 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/exp2.zig"
495504 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extendXfYf2.zig"
505 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extend_f80.zig"
506 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fabs.zig"
496507 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixXfYi.zig"
497508 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXiYf.zig"
509 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floor.zig"
510 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fma.zig"
511 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmax.zig"
512 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmin.zig"
513 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmod.zig"
498514 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/int.zig"
515 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log.zig"
516 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log10.zig"
517 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log2.zig"
499518 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig"
500519 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig"
501520 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/muldi3.zig"
......@@ -507,9 +526,22 @@ set(ZIG_STAGE2_SOURCES
507526 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/os_version_check.zig"
508527 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/parity.zig"
509528 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/popcount.zig"
529 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2.zig"
530 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2_large.zig"
531 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2f.zig"
532 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/round.zig"
510533 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/shift.zig"
534 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sin.zig"
535 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sincos.zig"
536 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sparc.zig"
537 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sqrt.zig"
511538 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/stack_probe.zig"
539 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/subo.zig"
540 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/tan.zig"
541 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trig.zig"
542 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trunc.zig"
512543 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/truncXfYf2.zig"
544 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trunc_f80.zig"
513545 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmod.zig"
514546 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig"
515547 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig"
doc/langref.html.in+15-1
......@@ -8026,7 +8026,7 @@ fn func(y: *i32) void {
80268026 only rounds once, and is thus more accurate.
80278027 </p>
80288028 <p>
8029 Supports Floats and Vectors of floats.
8029 Supports {#link|Floats#} and {#link|Vectors#} of floats.
80308030 </p>
80318031 {#header_close#}
80328032
......@@ -9440,6 +9440,7 @@ fn doTheTest() !void {
94409440 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
94419441 </p>
94429442 {#header_close#}
9443
94439444 {#header_open|@cos#}
94449445 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>
94459446 <p>
......@@ -9451,6 +9452,19 @@ fn doTheTest() !void {
94519452 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
94529453 </p>
94539454 {#header_close#}
9455
9456 {#header_open|@tan#}
9457 <pre>{#syntax#}@tan(value: anytype) @TypeOf(value){#endsyntax#}</pre>
9458 <p>
9459 Tangent trigonometric function on a floating point number.
9460 Uses a dedicated hardware instruction when available.
9461 </p>
9462 <p>
9463 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9464 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9465 </p>
9466 {#header_close#}
9467
94549468 {#header_open|@exp#}
94559469 <pre>{#syntax#}@exp(value: anytype) @TypeOf(value){#endsyntax#}</pre>
94569470 <p>
lib/std/fmt/errol.zig+4-4
......@@ -113,7 +113,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {
113113 // normalize the midpoint
114114
115115 const e = math.frexp(val).exponent;
116 var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103));
116 var exp = @floatToInt(i16, @floor(307 + @intToFloat(f64, e) * 0.30103));
117117 if (exp < 20) {
118118 exp = 20;
119119 } else if (@intCast(usize, exp) >= lookup_table.len) {
......@@ -170,10 +170,10 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {
170170 // digit generation
171171 var buf_index: usize = 0;
172172 while (true) {
173 var hdig = @floatToInt(u8, math.floor(high.val));
173 var hdig = @floatToInt(u8, @floor(high.val));
174174 if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1;
175175
176 var ldig = @floatToInt(u8, math.floor(low.val));
176 var ldig = @floatToInt(u8, @floor(low.val));
177177 if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1;
178178
179179 if (ldig != hdig) break;
......@@ -187,7 +187,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {
187187 }
188188
189189 const tmp = (high.val + low.val) / 2.0;
190 var mdig = @floatToInt(u8, math.floor(tmp + 0.5));
190 var mdig = @floatToInt(u8, @floor(tmp + 0.5));
191191 if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1;
192192
193193 buffer[buf_index] = mdig + '0';
lib/std/json.zig+1-1
......@@ -1655,7 +1655,7 @@ fn parseInternal(
16551655 if (numberToken.is_integer)
16561656 return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10);
16571657 const float = try std.fmt.parseFloat(f128, numberToken.slice(tokens.slice, tokens.i - 1));
1658 if (std.math.round(float) != float) return error.InvalidNumber;
1658 if (@round(float) != float) return error.InvalidNumber;
16591659 if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow;
16601660 return @floatToInt(T, float);
16611661 },
lib/std/math.zig+2-29
......@@ -138,7 +138,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool {
138138 if (isNan(x) or isNan(y))
139139 return false;
140140
141 return fabs(x - y) <= tolerance;
141 return @fabs(x - y) <= tolerance;
142142}
143143
144144/// Performs an approximate comparison of two floating point values `x` and `y`.
......@@ -166,7 +166,7 @@ pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool {
166166 if (isNan(x) or isNan(y))
167167 return false;
168168
169 return fabs(x - y) <= max(fabs(x), fabs(y)) * tolerance;
169 return @fabs(x - y) <= max(@fabs(x), @fabs(y)) * tolerance;
170170}
171171
172172pub fn approxEq(comptime T: type, x: T, y: T, tolerance: T) bool {
......@@ -233,11 +233,6 @@ pub fn raiseDivByZero() void {
233233
234234pub const isNan = @import("math/isnan.zig").isNan;
235235pub const isSignalNan = @import("math/isnan.zig").isSignalNan;
236pub const fabs = @import("math/fabs.zig").fabs;
237pub const ceil = @import("math/ceil.zig").ceil;
238pub const floor = @import("math/floor.zig").floor;
239pub const trunc = @import("math/trunc.zig").trunc;
240pub const round = @import("math/round.zig").round;
241236pub const frexp = @import("math/frexp.zig").frexp;
242237pub const Frexp = @import("math/frexp.zig").Frexp;
243238pub const modf = @import("math/modf.zig").modf;
......@@ -261,8 +256,6 @@ pub const asin = @import("math/asin.zig").asin;
261256pub const atan = @import("math/atan.zig").atan;
262257pub const atan2 = @import("math/atan2.zig").atan2;
263258pub const hypot = @import("math/hypot.zig").hypot;
264pub const exp = @import("math/exp.zig").exp;
265pub const exp2 = @import("math/exp2.zig").exp2;
266259pub const expm1 = @import("math/expm1.zig").expm1;
267260pub const ilogb = @import("math/ilogb.zig").ilogb;
268261pub const ln = @import("math/ln.zig").ln;
......@@ -270,16 +263,12 @@ pub const log = @import("math/log.zig").log;
270263pub const log2 = @import("math/log2.zig").log2;
271264pub const log10 = @import("math/log10.zig").log10;
272265pub const log1p = @import("math/log1p.zig").log1p;
273pub const fma = @import("math/fma.zig").fma;
274266pub const asinh = @import("math/asinh.zig").asinh;
275267pub const acosh = @import("math/acosh.zig").acosh;
276268pub const atanh = @import("math/atanh.zig").atanh;
277269pub const sinh = @import("math/sinh.zig").sinh;
278270pub const cosh = @import("math/cosh.zig").cosh;
279271pub const tanh = @import("math/tanh.zig").tanh;
280pub const cos = @import("math/cos.zig").cos;
281pub const sin = @import("math/sin.zig").sin;
282pub const tan = @import("math/tan.zig").tan;
283272
284273pub const complex = @import("math/complex.zig");
285274pub const Complex = complex.Complex;
......@@ -716,17 +705,6 @@ fn testAbsInt() !void {
716705 try testing.expect((absInt(@as(i32, 10)) catch unreachable) == 10);
717706}
718707
719pub const absFloat = fabs;
720
721test "absFloat" {
722 try testAbsFloat();
723 comptime try testAbsFloat();
724}
725fn testAbsFloat() !void {
726 try testing.expect(absFloat(@as(f32, -10.05)) == 10.05);
727 try testing.expect(absFloat(@as(f32, 10.05)) == 10.05);
728}
729
730708/// Divide numerator by denominator, rounding toward zero. Returns an
731709/// error on overflow or when denominator is zero.
732710pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
......@@ -1400,11 +1378,6 @@ test "order.compare" {
14001378 try testing.expect(order(1, 0).compare(.neq));
14011379}
14021380
1403test "comptime sin and ln" {
1404 const v = comptime (sin(@as(f32, 1)) + ln(@as(f32, 5)));
1405 try testing.expect(v == sin(@as(f32, 1)) + ln(@as(f32, 5)));
1406}
1407
14081381/// Returns a mask of all ones if value is true,
14091382/// and a mask of all zeroes if value is false.
14101383/// Compiles to one instruction for register sized integers.
lib/std/math/__rem_pio2.zig deleted-198
......@@ -1,198 +0,0 @@
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/__rem_pio2.c
5
6const std = @import("../std.zig");
7const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large;
8const math = std.math;
9
10const toint = 1.5 / math.floatEps(f64);
11// pi/4
12const pio4 = 0x1.921fb54442d18p-1;
13// invpio2: 53 bits of 2/pi
14const invpio2 = 6.36619772367581382433e-01; // 0x3FE45F30, 0x6DC9C883
15// pio2_1: first 33 bit of pi/2
16const pio2_1 = 1.57079632673412561417e+00; // 0x3FF921FB, 0x54400000
17// pio2_1t: pi/2 - pio2_1
18const pio2_1t = 6.07710050650619224932e-11; // 0x3DD0B461, 0x1A626331
19// pio2_2: second 33 bit of pi/2
20const pio2_2 = 6.07710050630396597660e-11; // 0x3DD0B461, 0x1A600000
21// pio2_2t: pi/2 - (pio2_1+pio2_2)
22const pio2_2t = 2.02226624879595063154e-21; // 0x3BA3198A, 0x2E037073
23// pio2_3: third 33 bit of pi/2
24const pio2_3 = 2.02226624871116645580e-21; // 0x3BA3198A, 0x2E000000
25// pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3)
26const pio2_3t = 8.47842766036889956997e-32; // 0x397B839A, 0x252049C1
27
28fn U(x: anytype) usize {
29 return @intCast(usize, x);
30}
31
32fn medium(ix: u32, x: f64, y: *[2]f64) i32 {
33 var w: f64 = undefined;
34 var t: f64 = undefined;
35 var r: f64 = undefined;
36 var @"fn": f64 = undefined;
37 var n: i32 = undefined;
38 var ex: i32 = undefined;
39 var ey: i32 = undefined;
40 var ui: u64 = undefined;
41
42 // rint(x/(pi/2))
43 @"fn" = x * invpio2 + toint - toint;
44 n = @floatToInt(i32, @"fn");
45 r = x - @"fn" * pio2_1;
46 w = @"fn" * pio2_1t; // 1st round, good to 85 bits
47 // Matters with directed rounding.
48 if (r - w < -pio4) {
49 n -= 1;
50 @"fn" -= 1;
51 r = x - @"fn" * pio2_1;
52 w = @"fn" * pio2_1t;
53 } else if (r - w > pio4) {
54 n += 1;
55 @"fn" += 1;
56 r = x - @"fn" * pio2_1;
57 w = @"fn" * pio2_1t;
58 }
59 y[0] = r - w;
60 ui = @bitCast(u64, y[0]);
61 ey = @intCast(i32, (ui >> 52) & 0x7ff);
62 ex = @intCast(i32, ix >> 20);
63 if (ex - ey > 16) { // 2nd round, good to 118 bits
64 t = r;
65 w = @"fn" * pio2_2;
66 r = t - w;
67 w = @"fn" * pio2_2t - ((t - r) - w);
68 y[0] = r - w;
69 ui = @bitCast(u64, y[0]);
70 ey = @intCast(i32, (ui >> 52) & 0x7ff);
71 if (ex - ey > 49) { // 3rd round, good to 151 bits, covers all cases
72 t = r;
73 w = @"fn" * pio2_3;
74 r = t - w;
75 w = @"fn" * pio2_3t - ((t - r) - w);
76 y[0] = r - w;
77 }
78 }
79 y[1] = (r - y[0]) - w;
80 return n;
81}
82
83// Returns the remainder of x rem pi/2 in y[0]+y[1]
84//
85// use __rem_pio2_large() for large x
86//
87// caller must handle the case when reduction is not needed: |x| ~<= pi/4 */
88pub fn __rem_pio2(x: f64, y: *[2]f64) i32 {
89 var z: f64 = undefined;
90 var tx: [3]f64 = undefined;
91 var ty: [2]f64 = undefined;
92 var n: i32 = undefined;
93 var ix: u32 = undefined;
94 var sign: bool = undefined;
95 var i: i32 = undefined;
96 var ui: u64 = undefined;
97
98 ui = @bitCast(u64, x);
99 sign = ui >> 63 != 0;
100 ix = @truncate(u32, (ui >> 32) & 0x7fffffff);
101 if (ix <= 0x400f6a7a) { // |x| ~<= 5pi/4
102 if ((ix & 0xfffff) == 0x921fb) { // |x| ~= pi/2 or 2pi/2
103 return medium(ix, x, y);
104 }
105 if (ix <= 0x4002d97c) { // |x| ~<= 3pi/4
106 if (!sign) {
107 z = x - pio2_1; // one round good to 85 bits
108 y[0] = z - pio2_1t;
109 y[1] = (z - y[0]) - pio2_1t;
110 return 1;
111 } else {
112 z = x + pio2_1;
113 y[0] = z + pio2_1t;
114 y[1] = (z - y[0]) + pio2_1t;
115 return -1;
116 }
117 } else {
118 if (!sign) {
119 z = x - 2 * pio2_1;
120 y[0] = z - 2 * pio2_1t;
121 y[1] = (z - y[0]) - 2 * pio2_1t;
122 return 2;
123 } else {
124 z = x + 2 * pio2_1;
125 y[0] = z + 2 * pio2_1t;
126 y[1] = (z - y[0]) + 2 * pio2_1t;
127 return -2;
128 }
129 }
130 }
131 if (ix <= 0x401c463b) { // |x| ~<= 9pi/4
132 if (ix <= 0x4015fdbc) { // |x| ~<= 7pi/4
133 if (ix == 0x4012d97c) { // |x| ~= 3pi/2
134 return medium(ix, x, y);
135 }
136 if (!sign) {
137 z = x - 3 * pio2_1;
138 y[0] = z - 3 * pio2_1t;
139 y[1] = (z - y[0]) - 3 * pio2_1t;
140 return 3;
141 } else {
142 z = x + 3 * pio2_1;
143 y[0] = z + 3 * pio2_1t;
144 y[1] = (z - y[0]) + 3 * pio2_1t;
145 return -3;
146 }
147 } else {
148 if (ix == 0x401921fb) { // |x| ~= 4pi/2 */
149 return medium(ix, x, y);
150 }
151 if (!sign) {
152 z = x - 4 * pio2_1;
153 y[0] = z - 4 * pio2_1t;
154 y[1] = (z - y[0]) - 4 * pio2_1t;
155 return 4;
156 } else {
157 z = x + 4 * pio2_1;
158 y[0] = z + 4 * pio2_1t;
159 y[1] = (z - y[0]) + 4 * pio2_1t;
160 return -4;
161 }
162 }
163 }
164 if (ix < 0x413921fb) { // |x| ~< 2^20*(pi/2), medium size
165 return medium(ix, x, y);
166 }
167 // all other (large) arguments
168 if (ix >= 0x7ff00000) { // x is inf or NaN
169 y[0] = x - x;
170 y[1] = y[0];
171 return 0;
172 }
173 // set z = scalbn(|x|,-ilogb(x)+23)
174 ui = @bitCast(u64, x);
175 ui &= std.math.maxInt(u64) >> 12;
176 ui |= @as(u64, 0x3ff + 23) << 52;
177 z = @bitCast(f64, ui);
178
179 i = 0;
180 while (i < 2) : (i += 1) {
181 tx[U(i)] = @intToFloat(f64, @floatToInt(i32, z));
182 z = (z - tx[U(i)]) * 0x1p24;
183 }
184 tx[U(i)] = z;
185 // skip zero terms, first term is non-zero
186 while (tx[U(i)] == 0.0) {
187 i -= 1;
188 }
189 n = __rem_pio2_large(tx[0..], ty[0..], @intCast(i32, (ix >> 20)) - (0x3ff + 23), i + 1, 1);
190 if (sign) {
191 y[0] = -ty[0];
192 y[1] = -ty[1];
193 return -n;
194 }
195 y[0] = ty[0];
196 y[1] = ty[1];
197 return n;
198}
lib/std/math/__rem_pio2_large.zig deleted-510
......@@ -1,510 +0,0 @@
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/__rem_pio2_large.c
5
6const std = @import("../std.zig");
7const math = std.math;
8
9const init_jk = [_]i32{ 3, 4, 4, 6 }; // initial value for jk
10
11//
12// Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi
13//
14// integer array, contains the (24*i)-th to (24*i+23)-th
15// bit of 2/pi after binary point. The corresponding
16// floating value is
17//
18// ipio2[i] * 2^(-24(i+1)).
19//
20// NB: This table must have at least (e0-3)/24 + jk terms.
21// For quad precision (e0 <= 16360, jk = 6), this is 686.
22///
23const ipio2 = [_]i32{
24 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62,
25 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A,
26 0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129,
27 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41,
28 0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8,
29 0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF,
30 0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5,
31 0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08,
32 0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3,
33 0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
34 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B,
35
36 //#if LDBL_MAX_EXP > 1024
37 0x47C419, 0xC367CD, 0xDCE809, 0x2A8359, 0xC4768B, 0x961CA6,
38 0xDDAF44, 0xD15719, 0x053EA5, 0xFF0705, 0x3F7E33, 0xE832C2,
39 0xDE4F98, 0x327DBB, 0xC33D26, 0xEF6B1E, 0x5EF89F, 0x3A1F35,
40 0xCAF27F, 0x1D87F1, 0x21907C, 0x7C246A, 0xFA6ED5, 0x772D30,
41 0x433B15, 0xC614B5, 0x9D19C3, 0xC2C4AD, 0x414D2C, 0x5D000C,
42 0x467D86, 0x2D71E3, 0x9AC69B, 0x006233, 0x7CD2B4, 0x97A7B4,
43 0xD55537, 0xF63ED7, 0x1810A3, 0xFC764D, 0x2A9D64, 0xABD770,
44 0xF87C63, 0x57B07A, 0xE71517, 0x5649C0, 0xD9D63B, 0x3884A7,
45 0xCB2324, 0x778AD6, 0x23545A, 0xB91F00, 0x1B0AF1, 0xDFCE19,
46 0xFF319F, 0x6A1E66, 0x615799, 0x47FBAC, 0xD87F7E, 0xB76522,
47 0x89E832, 0x60BFE6, 0xCDC4EF, 0x09366C, 0xD43F5D, 0xD7DE16,
48 0xDE3B58, 0x929BDE, 0x2822D2, 0xE88628, 0x4D58E2, 0x32CAC6,
49 0x16E308, 0xCB7DE0, 0x50C017, 0xA71DF3, 0x5BE018, 0x34132E,
50 0x621283, 0x014883, 0x5B8EF5, 0x7FB0AD, 0xF2E91E, 0x434A48,
51 0xD36710, 0xD8DDAA, 0x425FAE, 0xCE616A, 0xA4280A, 0xB499D3,
52 0xF2A606, 0x7F775C, 0x83C2A3, 0x883C61, 0x78738A, 0x5A8CAF,
53 0xBDD76F, 0x63A62D, 0xCBBFF4, 0xEF818D, 0x67C126, 0x45CA55,
54 0x36D9CA, 0xD2A828, 0x8D61C2, 0x77C912, 0x142604, 0x9B4612,
55 0xC459C4, 0x44C5C8, 0x91B24D, 0xF31700, 0xAD43D4, 0xE54929,
56 0x10D5FD, 0xFCBE00, 0xCC941E, 0xEECE70, 0xF53E13, 0x80F1EC,
57 0xC3E7B3, 0x28F8C7, 0x940593, 0x3E71C1, 0xB3092E, 0xF3450B,
58 0x9C1288, 0x7B20AB, 0x9FB52E, 0xC29247, 0x2F327B, 0x6D550C,
59 0x90A772, 0x1FE76B, 0x96CB31, 0x4A1679, 0xE27941, 0x89DFF4,
60 0x9794E8, 0x84E6E2, 0x973199, 0x6BED88, 0x365F5F, 0x0EFDBB,
61 0xB49A48, 0x6CA467, 0x427271, 0x325D8D, 0xB8159F, 0x09E5BC,
62 0x25318D, 0x3974F7, 0x1C0530, 0x010C0D, 0x68084B, 0x58EE2C,
63 0x90AA47, 0x02E774, 0x24D6BD, 0xA67DF7, 0x72486E, 0xEF169F,
64 0xA6948E, 0xF691B4, 0x5153D1, 0xF20ACF, 0x339820, 0x7E4BF5,
65 0x6863B2, 0x5F3EDD, 0x035D40, 0x7F8985, 0x295255, 0xC06437,
66 0x10D86D, 0x324832, 0x754C5B, 0xD4714E, 0x6E5445, 0xC1090B,
67 0x69F52A, 0xD56614, 0x9D0727, 0x50045D, 0xDB3BB4, 0xC576EA,
68 0x17F987, 0x7D6B49, 0xBA271D, 0x296996, 0xACCCC6, 0x5414AD,
69 0x6AE290, 0x89D988, 0x50722C, 0xBEA404, 0x940777, 0x7030F3,
70 0x27FC00, 0xA871EA, 0x49C266, 0x3DE064, 0x83DD97, 0x973FA3,
71 0xFD9443, 0x8C860D, 0xDE4131, 0x9D3992, 0x8C70DD, 0xE7B717,
72 0x3BDF08, 0x2B3715, 0xA0805C, 0x93805A, 0x921110, 0xD8E80F,
73 0xAF806C, 0x4BFFDB, 0x0F9038, 0x761859, 0x15A562, 0xBBCB61,
74 0xB989C7, 0xBD4010, 0x04F2D2, 0x277549, 0xF6B6EB, 0xBB22DB,
75 0xAA140A, 0x2F2689, 0x768364, 0x333B09, 0x1A940E, 0xAA3A51,
76 0xC2A31D, 0xAEEDAF, 0x12265C, 0x4DC26D, 0x9C7A2D, 0x9756C0,
77 0x833F03, 0xF6F009, 0x8C402B, 0x99316D, 0x07B439, 0x15200C,
78 0x5BC3D8, 0xC492F5, 0x4BADC6, 0xA5CA4E, 0xCD37A7, 0x36A9E6,
79 0x9492AB, 0x6842DD, 0xDE6319, 0xEF8C76, 0x528B68, 0x37DBFC,
80 0xABA1AE, 0x3115DF, 0xA1AE00, 0xDAFB0C, 0x664D64, 0xB705ED,
81 0x306529, 0xBF5657, 0x3AFF47, 0xB9F96A, 0xF3BE75, 0xDF9328,
82 0x3080AB, 0xF68C66, 0x15CB04, 0x0622FA, 0x1DE4D9, 0xA4B33D,
83 0x8F1B57, 0x09CD36, 0xE9424E, 0xA4BE13, 0xB52333, 0x1AAAF0,
84 0xA8654F, 0xA5C1D2, 0x0F3F0B, 0xCD785B, 0x76F923, 0x048B7B,
85 0x721789, 0x53A6C6, 0xE26E6F, 0x00EBEF, 0x584A9B, 0xB7DAC4,
86 0xBA66AA, 0xCFCF76, 0x1D02D1, 0x2DF1B1, 0xC1998C, 0x77ADC3,
87 0xDA4886, 0xA05DF7, 0xF480C6, 0x2FF0AC, 0x9AECDD, 0xBC5C3F,
88 0x6DDED0, 0x1FC790, 0xB6DB2A, 0x3A25A3, 0x9AAF00, 0x9353AD,
89 0x0457B6, 0xB42D29, 0x7E804B, 0xA707DA, 0x0EAA76, 0xA1597B,
90 0x2A1216, 0x2DB7DC, 0xFDE5FA, 0xFEDB89, 0xFDBE89, 0x6C76E4,
91 0xFCA906, 0x70803E, 0x156E85, 0xFF87FD, 0x073E28, 0x336761,
92 0x86182A, 0xEABD4D, 0xAFE7B3, 0x6E6D8F, 0x396795, 0x5BBF31,
93 0x48D784, 0x16DF30, 0x432DC7, 0x356125, 0xCE70C9, 0xB8CB30,
94 0xFD6CBF, 0xA200A4, 0xE46C05, 0xA0DD5A, 0x476F21, 0xD21262,
95 0x845CB9, 0x496170, 0xE0566B, 0x015299, 0x375550, 0xB7D51E,
96 0xC4F133, 0x5F6E13, 0xE4305D, 0xA92E85, 0xC3B21D, 0x3632A1,
97 0xA4B708, 0xD4B1EA, 0x21F716, 0xE4698F, 0x77FF27, 0x80030C,
98 0x2D408D, 0xA0CD4F, 0x99A520, 0xD3A2B3, 0x0A5D2F, 0x42F9B4,
99 0xCBDA11, 0xD0BE7D, 0xC1DB9B, 0xBD17AB, 0x81A2CA, 0x5C6A08,
100 0x17552E, 0x550027, 0xF0147F, 0x8607E1, 0x640B14, 0x8D4196,
101 0xDEBE87, 0x2AFDDA, 0xB6256B, 0x34897B, 0xFEF305, 0x9EBFB9,
102 0x4F6A68, 0xA82A4A, 0x5AC44F, 0xBCF82D, 0x985AD7, 0x95C7F4,
103 0x8D4D0D, 0xA63A20, 0x5F57A4, 0xB13F14, 0x953880, 0x0120CC,
104 0x86DD71, 0xB6DEC9, 0xF560BF, 0x11654D, 0x6B0701, 0xACB08C,
105 0xD0C0B2, 0x485551, 0x0EFB1E, 0xC37295, 0x3B06A3, 0x3540C0,
106 0x7BDC06, 0xCC45E0, 0xFA294E, 0xC8CAD6, 0x41F3E8, 0xDE647C,
107 0xD8649B, 0x31BED9, 0xC397A4, 0xD45877, 0xC5E369, 0x13DAF0,
108 0x3C3ABA, 0x461846, 0x5F7555, 0xF5BDD2, 0xC6926E, 0x5D2EAC,
109 0xED440E, 0x423E1C, 0x87C461, 0xE9FD29, 0xF3D6E7, 0xCA7C22,
110 0x35916F, 0xC5E008, 0x8DD7FF, 0xE26A6E, 0xC6FDB0, 0xC10893,
111 0x745D7C, 0xB2AD6B, 0x9D6ECD, 0x7B723E, 0x6A11C6, 0xA9CFF7,
112 0xDF7329, 0xBAC9B5, 0x5100B7, 0x0DB2E2, 0x24BA74, 0x607DE5,
113 0x8AD874, 0x2C150D, 0x0C1881, 0x94667E, 0x162901, 0x767A9F,
114 0xBEFDFD, 0xEF4556, 0x367ED9, 0x13D9EC, 0xB9BA8B, 0xFC97C4,
115 0x27A831, 0xC36EF1, 0x36C594, 0x56A8D8, 0xB5A8B4, 0x0ECCCF,
116 0x2D8912, 0x34576F, 0x89562C, 0xE3CE99, 0xB920D6, 0xAA5E6B,
117 0x9C2A3E, 0xCC5F11, 0x4A0BFD, 0xFBF4E1, 0x6D3B8E, 0x2C86E2,
118 0x84D4E9, 0xA9B4FC, 0xD1EEEF, 0xC9352E, 0x61392F, 0x442138,
119 0xC8D91B, 0x0AFC81, 0x6A4AFB, 0xD81C2F, 0x84B453, 0x8C994E,
120 0xCC2254, 0xDC552A, 0xD6C6C0, 0x96190B, 0xB8701A, 0x649569,
121 0x605A26, 0xEE523F, 0x0F117F, 0x11B5F4, 0xF5CBFC, 0x2DBC34,
122 0xEEBC34, 0xCC5DE8, 0x605EDD, 0x9B8E67, 0xEF3392, 0xB817C9,
123 0x9B5861, 0xBC57E1, 0xC68351, 0x103ED8, 0x4871DD, 0xDD1C2D,
124 0xA118AF, 0x462C21, 0xD7F359, 0x987AD9, 0xC0549E, 0xFA864F,
125 0xFC0656, 0xAE79E5, 0x362289, 0x22AD38, 0xDC9367, 0xAAE855,
126 0x382682, 0x9BE7CA, 0xA40D51, 0xB13399, 0x0ED7A9, 0x480569,
127 0xF0B265, 0xA7887F, 0x974C88, 0x36D1F9, 0xB39221, 0x4A827B,
128 0x21CF98, 0xDC9F40, 0x5547DC, 0x3A74E1, 0x42EB67, 0xDF9DFE,
129 0x5FD45E, 0xA4677B, 0x7AACBA, 0xA2F655, 0x23882B, 0x55BA41,
130 0x086E59, 0x862A21, 0x834739, 0xE6E389, 0xD49EE5, 0x40FB49,
131 0xE956FF, 0xCA0F1C, 0x8A59C5, 0x2BFA94, 0xC5C1D3, 0xCFC50F,
132 0xAE5ADB, 0x86C547, 0x624385, 0x3B8621, 0x94792C, 0x876110,
133 0x7B4C2A, 0x1A2C80, 0x12BF43, 0x902688, 0x893C78, 0xE4C4A8,
134 0x7BDBE5, 0xC23AC4, 0xEAF426, 0x8A67F7, 0xBF920D, 0x2BA365,
135 0xB1933D, 0x0B7CBD, 0xDC51A4, 0x63DD27, 0xDDE169, 0x19949A,
136 0x9529A8, 0x28CE68, 0xB4ED09, 0x209F44, 0xCA984E, 0x638270,
137 0x237C7E, 0x32B90F, 0x8EF5A7, 0xE75614, 0x08F121, 0x2A9DB5,
138 0x4D7E6F, 0x5119A5, 0xABF9B5, 0xD6DF82, 0x61DD96, 0x023616,
139 0x9F3AC4, 0xA1A283, 0x6DED72, 0x7A8D39, 0xA9B882, 0x5C326B,
140 0x5B2746, 0xED3400, 0x7700D2, 0x55F4FC, 0x4D5901,
141 0x8071E0,
142 //#endif
143};
144
145const PIo2 = [_]f64{
146 1.57079625129699707031e+00, // 0x3FF921FB, 0x40000000
147 7.54978941586159635335e-08, // 0x3E74442D, 0x00000000
148 5.39030252995776476554e-15, // 0x3CF84698, 0x80000000
149 3.28200341580791294123e-22, // 0x3B78CC51, 0x60000000
150 1.27065575308067607349e-29, // 0x39F01B83, 0x80000000
151 1.22933308981111328932e-36, // 0x387A2520, 0x40000000
152 2.73370053816464559624e-44, // 0x36E38222, 0x80000000
153 2.16741683877804819444e-51, // 0x3569F31D, 0x00000000
154};
155
156fn U(x: anytype) usize {
157 return @intCast(usize, x);
158}
159
160// Returns the last three digits of N with y = x - N*pi/2 so that |y| < pi/2.
161//
162// The method is to compute the integer (mod 8) and fraction parts of
163// (2/pi)*x without doing the full multiplication. In general we
164// skip the part of the product that are known to be a huge integer (
165// more accurately, = 0 mod 8 ). Thus the number of operations are
166// independent of the exponent of the input.
167//
168// (2/pi) is represented by an array of 24-bit integers in ipio2[].
169//
170// Input parameters:
171// x[] The input value (must be positive) is broken into nx
172// pieces of 24-bit integers in double precision format.
173// x[i] will be the i-th 24 bit of x. The scaled exponent
174// of x[0] is given in input parameter e0 (i.e., x[0]*2^e0
175// match x's up to 24 bits.
176//
177// Example of breaking a double positive z into x[0]+x[1]+x[2]:
178// e0 = ilogb(z)-23
179// z = scalbn(z,-e0)
180// for i = 0,1,2
181// x[i] = floor(z)
182// z = (z-x[i])*2**24
183//
184//
185// y[] ouput result in an array of double precision numbers.
186// The dimension of y[] is:
187// 24-bit precision 1
188// 53-bit precision 2
189// 64-bit precision 2
190// 113-bit precision 3
191// The actual value is the sum of them. Thus for 113-bit
192// precison, one may have to do something like:
193//
194// long double t,w,r_head, r_tail;
195// t = (long double)y[2] + (long double)y[1];
196// w = (long double)y[0];
197// r_head = t+w;
198// r_tail = w - (r_head - t);
199//
200// e0 The exponent of x[0]. Must be <= 16360 or you need to
201// expand the ipio2 table.
202//
203// nx dimension of x[]
204//
205// prec an integer indicating the precision:
206// 0 24 bits (single)
207// 1 53 bits (double)
208// 2 64 bits (extended)
209// 3 113 bits (quad)
210//
211// Here is the description of some local variables:
212//
213// jk jk+1 is the initial number of terms of ipio2[] needed
214// in the computation. The minimum and recommended value
215// for jk is 3,4,4,6 for single, double, extended, and quad.
216// jk+1 must be 2 larger than you might expect so that our
217// recomputation test works. (Up to 24 bits in the integer
218// part (the 24 bits of it that we compute) and 23 bits in
219// the fraction part may be lost to cancelation before we
220// recompute.)
221//
222// jz local integer variable indicating the number of
223// terms of ipio2[] used.
224//
225// jx nx - 1
226//
227// jv index for pointing to the suitable ipio2[] for the
228// computation. In general, we want
229// ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8
230// is an integer. Thus
231// e0-3-24*jv >= 0 or (e0-3)/24 >= jv
232// Hence jv = max(0,(e0-3)/24).
233//
234// jp jp+1 is the number of terms in PIo2[] needed, jp = jk.
235//
236// q[] double array with integral value, representing the
237// 24-bits chunk of the product of x and 2/pi.
238//
239// q0 the corresponding exponent of q[0]. Note that the
240// exponent for q[i] would be q0-24*i.
241//
242// PIo2[] double precision array, obtained by cutting pi/2
243// into 24 bits chunks.
244//
245// f[] ipio2[] in floating point
246//
247// iq[] integer array by breaking up q[] in 24-bits chunk.
248//
249// fq[] final product of x*(2/pi) in fq[0],..,fq[jk]
250//
251// ih integer. If >0 it indicates q[] is >= 0.5, hence
252// it also indicates the *sign* of the result.
253//
254///
255//
256// Constants:
257// The hexadecimal values are the intended ones for the following
258// constants. The decimal values may be used, provided that the
259// compiler will convert from decimal to binary accurately enough
260// to produce the hexadecimal values shown.
261///
262pub fn __rem_pio2_large(x: []f64, y: []f64, e0: i32, nx: i32, prec: usize) i32 {
263 var jz: i32 = undefined;
264 var jx: i32 = undefined;
265 var jv: i32 = undefined;
266 var jp: i32 = undefined;
267 var jk: i32 = undefined;
268 var carry: i32 = undefined;
269 var n: i32 = undefined;
270 var iq: [20]i32 = undefined;
271 var i: i32 = undefined;
272 var j: i32 = undefined;
273 var k: i32 = undefined;
274 var m: i32 = undefined;
275 var q0: i32 = undefined;
276 var ih: i32 = undefined;
277
278 var z: f64 = undefined;
279 var fw: f64 = undefined;
280 var f: [20]f64 = undefined;
281 var fq: [20]f64 = undefined;
282 var q: [20]f64 = undefined;
283
284 // initialize jk
285 jk = init_jk[prec];
286 jp = jk;
287
288 // determine jx,jv,q0, note that 3>q0
289 jx = nx - 1;
290 jv = @divFloor(e0 - 3, 24);
291 if (jv < 0) jv = 0;
292 q0 = e0 - 24 * (jv + 1);
293
294 // set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk]
295 j = jv - jx;
296 m = jx + jk;
297 i = 0;
298 while (i <= m) : ({
299 i += 1;
300 j += 1;
301 }) {
302 f[U(i)] = if (j < 0) 0.0 else @intToFloat(f64, ipio2[U(j)]);
303 }
304
305 // compute q[0],q[1],...q[jk]
306 i = 0;
307 while (i <= jk) : (i += 1) {
308 j = 0;
309 fw = 0;
310 while (j <= jx) : (j += 1) {
311 fw += x[U(j)] * f[U(jx + i - j)];
312 }
313 q[U(i)] = fw;
314 }
315
316 jz = jk;
317
318 // This is to handle a non-trivial goto translation from C.
319 // An unconditional return statement is found at the end of this loop.
320 recompute: while (true) {
321 // distill q[] into iq[] reversingly
322 i = 0;
323 j = jz;
324 z = q[U(jz)];
325 while (j > 0) : ({
326 i += 1;
327 j -= 1;
328 }) {
329 fw = @intToFloat(f64, @floatToInt(i32, 0x1p-24 * z));
330 iq[U(i)] = @floatToInt(i32, z - 0x1p24 * fw);
331 z = q[U(j - 1)] + fw;
332 }
333
334 // compute n
335 z = math.scalbn(z, q0); // actual value of z
336 z -= 8.0 * math.floor(z * 0.125); // trim off integer >= 8
337 n = @floatToInt(i32, z);
338 z -= @intToFloat(f64, n);
339 ih = 0;
340 if (q0 > 0) { // need iq[jz-1] to determine n
341 i = iq[U(jz - 1)] >> @intCast(u5, 24 - q0);
342 n += i;
343 iq[U(jz - 1)] -= i << @intCast(u5, 24 - q0);
344 ih = iq[U(jz - 1)] >> @intCast(u5, 23 - q0);
345 } else if (q0 == 0) {
346 ih = iq[U(jz - 1)] >> 23;
347 } else if (z >= 0.5) {
348 ih = 2;
349 }
350
351 if (ih > 0) { // q > 0.5
352 n += 1;
353 carry = 0;
354 i = 0;
355 while (i < jz) : (i += 1) { // compute 1-q
356 j = iq[U(i)];
357 if (carry == 0) {
358 if (j != 0) {
359 carry = 1;
360 iq[U(i)] = 0x1000000 - j;
361 }
362 } else {
363 iq[U(i)] = 0xffffff - j;
364 }
365 }
366 if (q0 > 0) { // rare case: chance is 1 in 12
367 switch (q0) {
368 1 => iq[U(jz - 1)] &= 0x7fffff,
369 2 => iq[U(jz - 1)] &= 0x3fffff,
370 else => unreachable,
371 }
372 }
373 if (ih == 2) {
374 z = 1.0 - z;
375 if (carry != 0) {
376 z -= math.scalbn(@as(f64, 1.0), q0);
377 }
378 }
379 }
380
381 // check if recomputation is needed
382 if (z == 0.0) {
383 j = 0;
384 i = jz - 1;
385 while (i >= jk) : (i -= 1) {
386 j |= iq[U(i)];
387 }
388
389 if (j == 0) { // need recomputation
390 k = 1;
391 while (iq[U(jk - k)] == 0) : (k += 1) {
392 // k = no. of terms needed
393 }
394
395 i = jz + 1;
396 while (i <= jz + k) : (i += 1) { // add q[jz+1] to q[jz+k]
397 f[U(jx + i)] = @intToFloat(f64, ipio2[U(jv + i)]);
398 j = 0;
399 fw = 0;
400 while (j <= jx) : (j += 1) {
401 fw += x[U(j)] * f[U(jx + i - j)];
402 }
403 q[U(i)] = fw;
404 }
405 jz += k;
406 continue :recompute; // mimic goto recompute
407 }
408 }
409
410 // chop off zero terms
411 if (z == 0.0) {
412 jz -= 1;
413 q0 -= 24;
414 while (iq[U(jz)] == 0) {
415 jz -= 1;
416 q0 -= 24;
417 }
418 } else { // break z into 24-bit if necessary
419 z = math.scalbn(z, -q0);
420 if (z >= 0x1p24) {
421 fw = @intToFloat(f64, @floatToInt(i32, 0x1p-24 * z));
422 iq[U(jz)] = @floatToInt(i32, z - 0x1p24 * fw);
423 jz += 1;
424 q0 += 24;
425 iq[U(jz)] = @floatToInt(i32, fw);
426 } else {
427 iq[U(jz)] = @floatToInt(i32, z);
428 }
429 }
430
431 // convert integer "bit" chunk to floating-point value
432 fw = math.scalbn(@as(f64, 1.0), q0);
433 i = jz;
434 while (i >= 0) : (i -= 1) {
435 q[U(i)] = fw * @intToFloat(f64, iq[U(i)]);
436 fw *= 0x1p-24;
437 }
438
439 // compute PIo2[0,...,jp]*q[jz,...,0]
440 i = jz;
441 while (i >= 0) : (i -= 1) {
442 fw = 0;
443 k = 0;
444 while (k <= jp and k <= jz - i) : (k += 1) {
445 fw += PIo2[U(k)] * q[U(i + k)];
446 }
447 fq[U(jz - i)] = fw;
448 }
449
450 // compress fq[] into y[]
451 switch (prec) {
452 0 => {
453 fw = 0.0;
454 i = jz;
455 while (i >= 0) : (i -= 1) {
456 fw += fq[U(i)];
457 }
458 y[0] = if (ih == 0) fw else -fw;
459 },
460
461 1, 2 => {
462 fw = 0.0;
463 i = jz;
464 while (i >= 0) : (i -= 1) {
465 fw += fq[U(i)];
466 }
467 // TODO: drop excess precision here once double_t is used
468 fw = fw;
469 y[0] = if (ih == 0) fw else -fw;
470 fw = fq[0] - fw;
471 i = 1;
472 while (i <= jz) : (i += 1) {
473 fw += fq[U(i)];
474 }
475 y[1] = if (ih == 0) fw else -fw;
476 },
477 3 => { // painful
478 i = jz;
479 while (i > 0) : (i -= 1) {
480 fw = fq[U(i - 1)] + fq[U(i)];
481 fq[U(i)] += fq[U(i - 1)] - fw;
482 fq[U(i - 1)] = fw;
483 }
484 i = jz;
485 while (i > 1) : (i -= 1) {
486 fw = fq[U(i - 1)] + fq[U(i)];
487 fq[U(i)] += fq[U(i - 1)] - fw;
488 fq[U(i - 1)] = fw;
489 }
490 fw = 0;
491 i = jz;
492 while (i >= 2) : (i -= 1) {
493 fw += fq[U(i)];
494 }
495 if (ih == 0) {
496 y[0] = fq[0];
497 y[1] = fq[1];
498 y[2] = fw;
499 } else {
500 y[0] = -fq[0];
501 y[1] = -fq[1];
502 y[2] = -fw;
503 }
504 },
505 else => unreachable,
506 }
507
508 return n & 7;
509 }
510}
lib/std/math/__rem_pio2f.zig deleted-70
......@@ -1,70 +0,0 @@
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/__rem_pio2f.c
5
6const std = @import("../std.zig");
7const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large;
8const math = std.math;
9
10const toint = 1.5 / math.floatEps(f64);
11// pi/4
12const pio4 = 0x1.921fb6p-1;
13// invpio2: 53 bits of 2/pi
14const invpio2 = 6.36619772367581382433e-01; // 0x3FE45F30, 0x6DC9C883
15// pio2_1: first 25 bits of pi/2
16const pio2_1 = 1.57079631090164184570e+00; // 0x3FF921FB, 0x50000000
17// pio2_1t: pi/2 - pio2_1
18const pio2_1t = 1.58932547735281966916e-08; // 0x3E5110b4, 0x611A6263
19
20// Returns the remainder of x rem pi/2 in *y
21// use double precision for everything except passing x
22// use __rem_pio2_large() for large x
23pub fn __rem_pio2f(x: f32, y: *f64) i32 {
24 var tx: [1]f64 = undefined;
25 var ty: [1]f64 = undefined;
26 var @"fn": f64 = undefined;
27 var ix: u32 = undefined;
28 var n: i32 = undefined;
29 var sign: bool = undefined;
30 var e0: u32 = undefined;
31 var ui: u32 = undefined;
32
33 ui = @bitCast(u32, x);
34 ix = ui & 0x7fffffff;
35
36 // 25+53 bit pi is good enough for medium size
37 if (ix < 0x4dc90fdb) { // |x| ~< 2^28*(pi/2), medium size
38 // Use a specialized rint() to get fn.
39 @"fn" = @floatCast(f64, x) * invpio2 + toint - toint;
40 n = @floatToInt(i32, @"fn");
41 y.* = x - @"fn" * pio2_1 - @"fn" * pio2_1t;
42 // Matters with directed rounding.
43 if (y.* < -pio4) {
44 n -= 1;
45 @"fn" -= 1;
46 y.* = x - @"fn" * pio2_1 - @"fn" * pio2_1t;
47 } else if (y.* > pio4) {
48 n += 1;
49 @"fn" += 1;
50 y.* = x - @"fn" * pio2_1 - @"fn" * pio2_1t;
51 }
52 return n;
53 }
54 if (ix >= 0x7f800000) { // x is inf or NaN
55 y.* = x - x;
56 return 0;
57 }
58 // scale x into [2^23, 2^24-1]
59 sign = ui >> 31 != 0;
60 e0 = (ix >> 23) - (0x7f + 23); // e0 = ilogb(|x|)-23, positive
61 ui = ix - (e0 << 23);
62 tx[0] = @bitCast(f32, ui);
63 n = __rem_pio2_large(&tx, &ty, @intCast(i32, e0), 1, 0);
64 if (sign) {
65 y.* = -ty[0];
66 return -n;
67 }
68 y.* = ty[0];
69 return n;
70}
lib/std/math/__trig.zig deleted-273
......@@ -1,273 +0,0 @@
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/__cos.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/__cosdf.c
6// https://git.musl-libc.org/cgit/musl/tree/src/math/__sin.c
7// https://git.musl-libc.org/cgit/musl/tree/src/math/__sindf.c
8// https://git.musl-libc.org/cgit/musl/tree/src/math/__tand.c
9// https://git.musl-libc.org/cgit/musl/tree/src/math/__tandf.c
10
11// kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164
12// Input x is assumed to be bounded by ~pi/4 in magnitude.
13// Input y is the tail of x.
14//
15// Algorithm
16// 1. Since cos(-x) = cos(x), we need only to consider positive x.
17// 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0.
18// 3. cos(x) is approximated by a polynomial of degree 14 on
19// [0,pi/4]
20// 4 14
21// cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x
22// where the remez error is
23//
24// | 2 4 6 8 10 12 14 | -58
25// |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2
26// | |
27//
28// 4 6 8 10 12 14
29// 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then
30// cos(x) ~ 1 - x*x/2 + r
31// since cos(x+y) ~ cos(x) - sin(x)*y
32// ~ cos(x) - x*y,
33// a correction term is necessary in cos(x) and hence
34// cos(x+y) = 1 - (x*x/2 - (r - x*y))
35// For better accuracy, rearrange to
36// cos(x+y) ~ w + (tmp + (r-x*y))
37// where w = 1 - x*x/2 and tmp is a tiny correction term
38// (1 - x*x/2 == w + tmp exactly in infinite precision).
39// The exactness of w + tmp in infinite precision depends on w
40// and tmp having the same precision as x. If they have extra
41// precision due to compiler bugs, then the extra precision is
42// only good provided it is retained in all terms of the final
43// expression for cos(). Retention happens in all cases tested
44// under FreeBSD, so don't pessimize things by forcibly clipping
45// any extra precision in w.
46pub fn __cos(x: f64, y: f64) f64 {
47 const C1 = 4.16666666666666019037e-02; // 0x3FA55555, 0x5555554C
48 const C2 = -1.38888888888741095749e-03; // 0xBF56C16C, 0x16C15177
49 const C3 = 2.48015872894767294178e-05; // 0x3EFA01A0, 0x19CB1590
50 const C4 = -2.75573143513906633035e-07; // 0xBE927E4F, 0x809C52AD
51 const C5 = 2.08757232129817482790e-09; // 0x3E21EE9E, 0xBDB4B1C4
52 const C6 = -1.13596475577881948265e-11; // 0xBDA8FAE9, 0xBE8838D4
53
54 const z = x * x;
55 const zs = z * z;
56 const r = z * (C1 + z * (C2 + z * C3)) + zs * zs * (C4 + z * (C5 + z * C6));
57 const hz = 0.5 * z;
58 const w = 1.0 - hz;
59 return w + (((1.0 - w) - hz) + (z * r - x * y));
60}
61
62pub fn __cosdf(x: f64) f32 {
63 // |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]).
64 const C0 = -0x1ffffffd0c5e81.0p-54; // -0.499999997251031003120
65 const C1 = 0x155553e1053a42.0p-57; // 0.0416666233237390631894
66 const C2 = -0x16c087e80f1e27.0p-62; // -0.00138867637746099294692
67 const C3 = 0x199342e0ee5069.0p-68; // 0.0000243904487962774090654
68
69 // Try to optimize for parallel evaluation as in __tandf.c.
70 const z = x * x;
71 const w = z * z;
72 const r = C2 + z * C3;
73 return @floatCast(f32, ((1.0 + z * C0) + w * C1) + (w * z) * r);
74}
75
76// kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854
77// Input x is assumed to be bounded by ~pi/4 in magnitude.
78// Input y is the tail of x.
79// Input iy indicates whether y is 0. (if iy=0, y assume to be 0).
80//
81// Algorithm
82// 1. Since sin(-x) = -sin(x), we need only to consider positive x.
83// 2. Callers must return sin(-0) = -0 without calling here since our
84// odd polynomial is not evaluated in a way that preserves -0.
85// Callers may do the optimization sin(x) ~ x for tiny x.
86// 3. sin(x) is approximated by a polynomial of degree 13 on
87// [0,pi/4]
88// 3 13
89// sin(x) ~ x + S1*x + ... + S6*x
90// where
91//
92// |sin(x) 2 4 6 8 10 12 | -58
93// |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2
94// | x |
95//
96// 4. sin(x+y) = sin(x) + sin'(x')*y
97// ~ sin(x) + (1-x*x/2)*y
98// For better accuracy, let
99// 3 2 2 2 2
100// r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6))))
101// then 3 2
102// sin(x) = x + (S1*x + (x *(r-y/2)+y))
103pub fn __sin(x: f64, y: f64, iy: i32) f64 {
104 const S1 = -1.66666666666666324348e-01; // 0xBFC55555, 0x55555549
105 const S2 = 8.33333333332248946124e-03; // 0x3F811111, 0x1110F8A6
106 const S3 = -1.98412698298579493134e-04; // 0xBF2A01A0, 0x19C161D5
107 const S4 = 2.75573137070700676789e-06; // 0x3EC71DE3, 0x57B1FE7D
108 const S5 = -2.50507602534068634195e-08; // 0xBE5AE5E6, 0x8A2B9CEB
109 const S6 = 1.58969099521155010221e-10; // 0x3DE5D93A, 0x5ACFD57C
110
111 const z = x * x;
112 const w = z * z;
113 const r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6);
114 const v = z * x;
115 if (iy == 0) {
116 return x + v * (S1 + z * r);
117 } else {
118 return x - ((z * (0.5 * y - v * r) - y) - v * S1);
119 }
120}
121
122pub fn __sindf(x: f64) f32 {
123 // |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]).
124 const S1 = -0x15555554cbac77.0p-55; // -0.166666666416265235595
125 const S2 = 0x111110896efbb2.0p-59; // 0.0083333293858894631756
126 const S3 = -0x1a00f9e2cae774.0p-65; // -0.000198393348360966317347
127 const S4 = 0x16cd878c3b46a7.0p-71; // 0.0000027183114939898219064
128
129 // Try to optimize for parallel evaluation as in __tandf.c.
130 const z = x * x;
131 const w = z * z;
132 const r = S3 + z * S4;
133 const s = z * x;
134 return @floatCast(f32, (x + s * (S1 + z * S2)) + s * w * r);
135}
136
137// kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854
138// Input x is assumed to be bounded by ~pi/4 in magnitude.
139// Input y is the tail of x.
140// Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned.
141//
142// Algorithm
143// 1. Since tan(-x) = -tan(x), we need only to consider positive x.
144// 2. Callers must return tan(-0) = -0 without calling here since our
145// odd polynomial is not evaluated in a way that preserves -0.
146// Callers may do the optimization tan(x) ~ x for tiny x.
147// 3. tan(x) is approximated by a odd polynomial of degree 27 on
148// [0,0.67434]
149// 3 27
150// tan(x) ~ x + T1*x + ... + T13*x
151// where
152//
153// |tan(x) 2 4 26 | -59.2
154// |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2
155// | x |
156//
157// Note: tan(x+y) = tan(x) + tan'(x)*y
158// ~ tan(x) + (1+x*x)*y
159// Therefore, for better accuracy in computing tan(x+y), let
160// 3 2 2 2 2
161// r = x *(T2+x *(T3+x *(...+x *(T12+x *T13))))
162// then
163// 3 2
164// tan(x+y) = x + (T1*x + (x *(r+y)+y))
165//
166// 4. For x in [0.67434,pi/4], let y = pi/4 - x, then
167// tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y))
168// = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y)))
169pub fn __tan(x_: f64, y_: f64, odd: bool) f64 {
170 var x = x_;
171 var y = y_;
172
173 const T = [_]f64{
174 3.33333333333334091986e-01, // 3FD55555, 55555563
175 1.33333333333201242699e-01, // 3FC11111, 1110FE7A
176 5.39682539762260521377e-02, // 3FABA1BA, 1BB341FE
177 2.18694882948595424599e-02, // 3F9664F4, 8406D637
178 8.86323982359930005737e-03, // 3F8226E3, E96E8493
179 3.59207910759131235356e-03, // 3F6D6D22, C9560328
180 1.45620945432529025516e-03, // 3F57DBC8, FEE08315
181 5.88041240820264096874e-04, // 3F4344D8, F2F26501
182 2.46463134818469906812e-04, // 3F3026F7, 1A8D1068
183 7.81794442939557092300e-05, // 3F147E88, A03792A6
184 7.14072491382608190305e-05, // 3F12B80F, 32F0A7E9
185 -1.85586374855275456654e-05, // BEF375CB, DB605373
186 2.59073051863633712884e-05, // 3EFB2A70, 74BF7AD4
187 };
188 const pio4 = 7.85398163397448278999e-01; // 3FE921FB, 54442D18
189 const pio4lo = 3.06161699786838301793e-17; // 3C81A626, 33145C07
190
191 var z: f64 = undefined;
192 var r: f64 = undefined;
193 var v: f64 = undefined;
194 var w: f64 = undefined;
195 var s: f64 = undefined;
196 var a: f64 = undefined;
197 var w0: f64 = undefined;
198 var a0: f64 = undefined;
199 var hx: u32 = undefined;
200 var sign: bool = undefined;
201
202 hx = @intCast(u32, @bitCast(u64, x) >> 32);
203 const big = (hx & 0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744
204 if (big) {
205 sign = hx >> 31 != 0;
206 if (sign) {
207 x = -x;
208 y = -y;
209 }
210 x = (pio4 - x) + (pio4lo - y);
211 y = 0.0;
212 }
213 z = x * x;
214 w = z * z;
215
216 // Break x^5*(T[1]+x^2*T[2]+...) into
217 // x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
218 // x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
219 r = T[1] + w * (T[3] + w * (T[5] + w * (T[7] + w * (T[9] + w * T[11]))));
220 v = z * (T[2] + w * (T[4] + w * (T[6] + w * (T[8] + w * (T[10] + w * T[12])))));
221 s = z * x;
222 r = y + z * (s * (r + v) + y) + s * T[0];
223 w = x + r;
224 if (big) {
225 s = 1 - 2 * @intToFloat(f64, @boolToInt(odd));
226 v = s - 2.0 * (x + (r - w * w / (w + s)));
227 return if (sign) -v else v;
228 }
229 if (!odd) {
230 return w;
231 }
232 // -1.0/(x+r) has up to 2ulp error, so compute it accurately
233 w0 = w;
234 w0 = @bitCast(f64, @bitCast(u64, w0) & 0xffffffff00000000);
235 v = r - (w0 - x); // w0+v = r+x
236 a = -1.0 / w;
237 a0 = a;
238 a0 = @bitCast(f64, @bitCast(u64, a0) & 0xffffffff00000000);
239 return a0 + a * (1.0 + a0 * w0 + a0 * v);
240}
241
242pub fn __tandf(x: f64, odd: bool) f32 {
243 // |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]).
244 const T = [_]f64{
245 0x15554d3418c99f.0p-54, // 0.333331395030791399758
246 0x1112fd38999f72.0p-55, // 0.133392002712976742718
247 0x1b54c91d865afe.0p-57, // 0.0533812378445670393523
248 0x191df3908c33ce.0p-58, // 0.0245283181166547278873
249 0x185dadfcecf44e.0p-61, // 0.00297435743359967304927
250 0x1362b9bf971bcd.0p-59, // 0.00946564784943673166728
251 };
252
253 const z = x * x;
254 // Split up the polynomial into small independent terms to give
255 // opportunities for parallel evaluation. The chosen splitting is
256 // micro-optimized for Athlons (XP, X64). It costs 2 multiplications
257 // relative to Horner's method on sequential machines.
258 //
259 // We add the small terms from lowest degree up for efficiency on
260 // non-sequential machines (the lowest degree terms tend to be ready
261 // earlier). Apart from this, we don't care about order of
262 // operations, and don't need to to care since we have precision to
263 // spare. However, the chosen splitting is good for accuracy too,
264 // and would give results as accurate as Horner's method if the
265 // small terms were added from highest degree down.
266 const r = T[4] + z * T[5];
267 const t = T[2] + z * T[3];
268 const w = z * z;
269 const s = z * x;
270 const u = T[0] + z * T[1];
271 const r0 = (x + s * u) + (s * w) * (t + w * r);
272 return @floatCast(f32, if (odd) -1.0 / r0 else r0);
273}
lib/std/math/acos.zig+4-4
......@@ -64,14 +64,14 @@ fn acos32(x: f32) f32 {
6464 // x < -0.5
6565 if (hx >> 31 != 0) {
6666 const z = (1 + x) * 0.5;
67 const s = math.sqrt(z);
67 const s = @sqrt(z);
6868 const w = r32(z) * s - pio2_lo;
6969 return 2 * (pio2_hi - (s + w));
7070 }
7171
7272 // x > 0.5
7373 const z = (1.0 - x) * 0.5;
74 const s = math.sqrt(z);
74 const s = @sqrt(z);
7575 const jx = @bitCast(u32, s);
7676 const df = @bitCast(f32, jx & 0xFFFFF000);
7777 const c = (z - df * df) / (s + df);
......@@ -133,14 +133,14 @@ fn acos64(x: f64) f64 {
133133 // x < -0.5
134134 if (hx >> 31 != 0) {
135135 const z = (1.0 + x) * 0.5;
136 const s = math.sqrt(z);
136 const s = @sqrt(z);
137137 const w = r64(z) * s - pio2_lo;
138138 return 2 * (pio2_hi - (s + w));
139139 }
140140
141141 // x > 0.5
142142 const z = (1.0 - x) * 0.5;
143 const s = math.sqrt(z);
143 const s = @sqrt(z);
144144 const jx = @bitCast(u64, s);
145145 const df = @bitCast(f64, jx & 0xFFFFFFFF00000000);
146146 const c = (z - df * df) / (s + df);
lib/std/math/acosh.zig+6-6
......@@ -29,15 +29,15 @@ fn acosh32(x: f32) f32 {
2929
3030 // |x| < 2, invalid if x < 1 or nan
3131 if (i < 0x3F800000 + (1 << 23)) {
32 return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
32 return math.log1p(x - 1 + @sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
3333 }
3434 // |x| < 0x1p12
3535 else if (i < 0x3F800000 + (12 << 23)) {
36 return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)));
36 return @log(2 * x - 1 / (x + @sqrt(x * x - 1)));
3737 }
3838 // |x| >= 0x1p12
3939 else {
40 return math.ln(x) + 0.693147180559945309417232121458176568;
40 return @log(x) + 0.693147180559945309417232121458176568;
4141 }
4242}
4343
......@@ -47,15 +47,15 @@ fn acosh64(x: f64) f64 {
4747
4848 // |x| < 2, invalid if x < 1 or nan
4949 if (e < 0x3FF + 1) {
50 return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
50 return math.log1p(x - 1 + @sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
5151 }
5252 // |x| < 0x1p26
5353 else if (e < 0x3FF + 26) {
54 return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)));
54 return @log(2 * x - 1 / (x + @sqrt(x * x - 1)));
5555 }
5656 // |x| >= 0x1p26 or nan
5757 else {
58 return math.ln(x) + 0.693147180559945309417232121458176568;
58 return @log(x) + 0.693147180559945309417232121458176568;
5959 }
6060}
6161
lib/std/math/asin.zig+4-4
......@@ -60,8 +60,8 @@ fn asin32(x: f32) f32 {
6060 }
6161
6262 // 1 > |x| >= 0.5
63 const z = (1 - math.fabs(x)) * 0.5;
64 const s = math.sqrt(z);
63 const z = (1 - @fabs(x)) * 0.5;
64 const s = @sqrt(z);
6565 const fx = pio2 - 2 * (s + s * r32(z));
6666
6767 if (hx >> 31 != 0) {
......@@ -119,8 +119,8 @@ fn asin64(x: f64) f64 {
119119 }
120120
121121 // 1 > |x| >= 0.5
122 const z = (1 - math.fabs(x)) * 0.5;
123 const s = math.sqrt(z);
122 const z = (1 - @fabs(x)) * 0.5;
123 const s = @sqrt(z);
124124 const r = r64(z);
125125 var fx: f64 = undefined;
126126
lib/std/math/asinh.zig+6-6
......@@ -39,15 +39,15 @@ fn asinh32(x: f32) f32 {
3939
4040 // |x| >= 0x1p12 or inf or nan
4141 if (i >= 0x3F800000 + (12 << 23)) {
42 rx = math.ln(rx) + 0.69314718055994530941723212145817656;
42 rx = @log(rx) + 0.69314718055994530941723212145817656;
4343 }
4444 // |x| >= 2
4545 else if (i >= 0x3F800000 + (1 << 23)) {
46 rx = math.ln(2 * x + 1 / (math.sqrt(x * x + 1) + x));
46 rx = @log(2 * x + 1 / (@sqrt(x * x + 1) + x));
4747 }
4848 // |x| >= 0x1p-12, up to 1.6ulp error
4949 else if (i >= 0x3F800000 - (12 << 23)) {
50 rx = math.log1p(x + x * x / (math.sqrt(x * x + 1) + 1));
50 rx = math.log1p(x + x * x / (@sqrt(x * x + 1) + 1));
5151 }
5252 // |x| < 0x1p-12, inexact if x != 0
5353 else {
......@@ -70,15 +70,15 @@ fn asinh64(x: f64) f64 {
7070
7171 // |x| >= 0x1p26 or inf or nan
7272 if (e >= 0x3FF + 26) {
73 rx = math.ln(rx) + 0.693147180559945309417232121458176568;
73 rx = @log(rx) + 0.693147180559945309417232121458176568;
7474 }
7575 // |x| >= 2
7676 else if (e >= 0x3FF + 1) {
77 rx = math.ln(2 * x + 1 / (math.sqrt(x * x + 1) + x));
77 rx = @log(2 * x + 1 / (@sqrt(x * x + 1) + x));
7878 }
7979 // |x| >= 0x1p-12, up to 1.6ulp error
8080 else if (e >= 0x3FF - 26) {
81 rx = math.log1p(x + x * x / (math.sqrt(x * x + 1) + 1));
81 rx = math.log1p(x + x * x / (@sqrt(x * x + 1) + 1));
8282 }
8383 // |x| < 0x1p-12, inexact if x != 0
8484 else {
lib/std/math/atan.zig+2-2
......@@ -73,7 +73,7 @@ fn atan32(x_: f32) f32 {
7373 }
7474 id = null;
7575 } else {
76 x = math.fabs(x);
76 x = @fabs(x);
7777 // |x| < 1.1875
7878 if (ix < 0x3F980000) {
7979 // 7/16 <= |x| < 11/16
......@@ -171,7 +171,7 @@ fn atan64(x_: f64) f64 {
171171 }
172172 id = null;
173173 } else {
174 x = math.fabs(x);
174 x = @fabs(x);
175175 // |x| < 1.1875
176176 if (ix < 0x3FF30000) {
177177 // 7/16 <= |x| < 11/16
lib/std/math/atan2.zig+2-2
......@@ -108,7 +108,7 @@ fn atan2_32(y: f32, x: f32) f32 {
108108 if ((m & 2) != 0 and iy + (26 << 23) < ix) {
109109 break :z 0.0;
110110 } else {
111 break :z math.atan(math.fabs(y / x));
111 break :z math.atan(@fabs(y / x));
112112 }
113113 };
114114
......@@ -198,7 +198,7 @@ fn atan2_64(y: f64, x: f64) f64 {
198198 if ((m & 2) != 0 and iy +% (64 << 20) < ix) {
199199 break :z 0.0;
200200 } else {
201 break :z math.atan(math.fabs(y / x));
201 break :z math.atan(@fabs(y / x));
202202 }
203203 };
204204
lib/std/math/ceil.zig deleted-170
......@@ -1,170 +0,0 @@
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/ceilf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c
6
7const std = @import("../std.zig");
8const math = std.math;
9const expect = std.testing.expect;
10
11/// Returns the least integer value greater than of equal to x.
12///
13/// Special Cases:
14/// - ceil(+-0) = +-0
15/// - ceil(+-inf) = +-inf
16/// - ceil(nan) = nan
17pub fn ceil(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);
19 return switch (T) {
20 f32 => ceil32(x),
21 f64 => ceil64(x),
22 f128 => ceil128(x),
23
24 // TODO this is not correct for some targets
25 c_longdouble => @floatCast(c_longdouble, ceil128(x)),
26
27 else => @compileError("ceil not implemented for " ++ @typeName(T)),
28 };
29}
30
31fn ceil32(x: f32) f32 {
32 var u = @bitCast(u32, x);
33 var e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F;
34 var m: u32 = undefined;
35
36 // TODO: Shouldn't need this explicit check.
37 if (x == 0.0) {
38 return x;
39 }
40
41 if (e >= 23) {
42 return x;
43 } else if (e >= 0) {
44 m = @as(u32, 0x007FFFFF) >> @intCast(u5, e);
45 if (u & m == 0) {
46 return x;
47 }
48 math.doNotOptimizeAway(x + 0x1.0p120);
49 if (u >> 31 == 0) {
50 u += m;
51 }
52 u &= ~m;
53 return @bitCast(f32, u);
54 } else {
55 math.doNotOptimizeAway(x + 0x1.0p120);
56 if (u >> 31 != 0) {
57 return -0.0;
58 } else {
59 return 1.0;
60 }
61 }
62}
63
64fn ceil64(x: f64) f64 {
65 const f64_toint = 1.0 / math.floatEps(f64);
66
67 const u = @bitCast(u64, x);
68 const e = (u >> 52) & 0x7FF;
69 var y: f64 = undefined;
70
71 if (e >= 0x3FF + 52 or x == 0) {
72 return x;
73 }
74
75 if (u >> 63 != 0) {
76 y = x - f64_toint + f64_toint - x;
77 } else {
78 y = x + f64_toint - f64_toint - x;
79 }
80
81 if (e <= 0x3FF - 1) {
82 math.doNotOptimizeAway(y);
83 if (u >> 63 != 0) {
84 return -0.0;
85 } else {
86 return 1.0;
87 }
88 } else if (y < 0) {
89 return x + y + 1;
90 } else {
91 return x + y;
92 }
93}
94
95fn ceil128(x: f128) f128 {
96 const f128_toint = 1.0 / math.floatEps(f128);
97
98 const u = @bitCast(u128, x);
99 const e = (u >> 112) & 0x7FFF;
100 var y: f128 = undefined;
101
102 if (e >= 0x3FFF + 112 or x == 0) return x;
103
104 if (u >> 127 != 0) {
105 y = x - f128_toint + f128_toint - x;
106 } else {
107 y = x + f128_toint - f128_toint - x;
108 }
109
110 if (e <= 0x3FFF - 1) {
111 math.doNotOptimizeAway(y);
112 if (u >> 127 != 0) {
113 return -0.0;
114 } else {
115 return 1.0;
116 }
117 } else if (y < 0) {
118 return x + y + 1;
119 } else {
120 return x + y;
121 }
122}
123
124test "math.ceil" {
125 try expect(ceil(@as(f32, 0.0)) == ceil32(0.0));
126 try expect(ceil(@as(f64, 0.0)) == ceil64(0.0));
127 try expect(ceil(@as(f128, 0.0)) == ceil128(0.0));
128}
129
130test "math.ceil32" {
131 try expect(ceil32(1.3) == 2.0);
132 try expect(ceil32(-1.3) == -1.0);
133 try expect(ceil32(0.2) == 1.0);
134}
135
136test "math.ceil64" {
137 try expect(ceil64(1.3) == 2.0);
138 try expect(ceil64(-1.3) == -1.0);
139 try expect(ceil64(0.2) == 1.0);
140}
141
142test "math.ceil128" {
143 try expect(ceil128(1.3) == 2.0);
144 try expect(ceil128(-1.3) == -1.0);
145 try expect(ceil128(0.2) == 1.0);
146}
147
148test "math.ceil32.special" {
149 try expect(ceil32(0.0) == 0.0);
150 try expect(ceil32(-0.0) == -0.0);
151 try expect(math.isPositiveInf(ceil32(math.inf(f32))));
152 try expect(math.isNegativeInf(ceil32(-math.inf(f32))));
153 try expect(math.isNan(ceil32(math.nan(f32))));
154}
155
156test "math.ceil64.special" {
157 try expect(ceil64(0.0) == 0.0);
158 try expect(ceil64(-0.0) == -0.0);
159 try expect(math.isPositiveInf(ceil64(math.inf(f64))));
160 try expect(math.isNegativeInf(ceil64(-math.inf(f64))));
161 try expect(math.isNan(ceil64(math.nan(f64))));
162}
163
164test "math.ceil128.special" {
165 try expect(ceil128(0.0) == 0.0);
166 try expect(ceil128(-0.0) == -0.0);
167 try expect(math.isPositiveInf(ceil128(math.inf(f128))));
168 try expect(math.isNegativeInf(ceil128(-math.inf(f128))));
169 try expect(math.isNan(ceil128(math.nan(f128))));
170}
lib/std/math/complex.zig+1-1
......@@ -115,7 +115,7 @@ pub fn Complex(comptime T: type) type {
115115
116116 /// Returns the magnitude of a complex number.
117117 pub fn magnitude(self: Self) T {
118 return math.sqrt(self.re * self.re + self.im * self.im);
118 return @sqrt(self.re * self.re + self.im * self.im);
119119 }
120120 };
121121}
lib/std/math/complex/atan.zig+2-2
......@@ -66,7 +66,7 @@ fn atan32(z: Complex(f32)) Complex(f32) {
6666
6767 t = y + 1.0;
6868 a = (x2 + (t * t)) / a;
69 return Complex(f32).init(w, 0.25 * math.ln(a));
69 return Complex(f32).init(w, 0.25 * @log(a));
7070}
7171
7272fn redupif64(x: f64) f64 {
......@@ -115,7 +115,7 @@ fn atan64(z: Complex(f64)) Complex(f64) {
115115
116116 t = y + 1.0;
117117 a = (x2 + (t * t)) / a;
118 return Complex(f64).init(w, 0.25 * math.ln(a));
118 return Complex(f64).init(w, 0.25 * @log(a));
119119}
120120
121121const epsilon = 0.0001;
lib/std/math/complex/cosh.zig+12-12
......@@ -38,25 +38,25 @@ fn cosh32(z: Complex(f32)) Complex(f32) {
3838 }
3939 // small x: normal case
4040 if (ix < 0x41100000) {
41 return Complex(f32).init(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
41 return Complex(f32).init(math.cosh(x) * @cos(y), math.sinh(x) * @sin(y));
4242 }
4343
4444 // |x|>= 9, so cosh(x) ~= exp(|x|)
4545 if (ix < 0x42b17218) {
4646 // x < 88.7: exp(|x|) won't overflow
47 const h = math.exp(math.fabs(x)) * 0.5;
48 return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
47 const h = @exp(@fabs(x)) * 0.5;
48 return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y));
4949 }
5050 // x < 192.7: scale to avoid overflow
5151 else if (ix < 0x4340b1e7) {
52 const v = Complex(f32).init(math.fabs(x), y);
52 const v = Complex(f32).init(@fabs(x), y);
5353 const r = ldexp_cexp(v, -1);
5454 return Complex(f32).init(r.re, r.im * math.copysign(f32, 1, x));
5555 }
5656 // x >= 192.7: result always overflows
5757 else {
5858 const h = 0x1p127 * x;
59 return Complex(f32).init(h * h * math.cos(y), h * math.sin(y));
59 return Complex(f32).init(h * h * @cos(y), h * @sin(y));
6060 }
6161 }
6262
......@@ -79,7 +79,7 @@ fn cosh32(z: Complex(f32)) Complex(f32) {
7979 if (iy >= 0x7f800000) {
8080 return Complex(f32).init(x * x, x * (y - y));
8181 }
82 return Complex(f32).init((x * x) * math.cos(y), x * math.sin(y));
82 return Complex(f32).init((x * x) * @cos(y), x * @sin(y));
8383 }
8484
8585 return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y));
......@@ -106,25 +106,25 @@ fn cosh64(z: Complex(f64)) Complex(f64) {
106106 }
107107 // small x: normal case
108108 if (ix < 0x40360000) {
109 return Complex(f64).init(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
109 return Complex(f64).init(math.cosh(x) * @cos(y), math.sinh(x) * @sin(y));
110110 }
111111
112112 // |x|>= 22, so cosh(x) ~= exp(|x|)
113113 if (ix < 0x40862e42) {
114114 // x < 710: exp(|x|) won't overflow
115 const h = math.exp(math.fabs(x)) * 0.5;
116 return Complex(f64).init(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y));
115 const h = @exp(@fabs(x)) * 0.5;
116 return Complex(f64).init(h * @cos(y), math.copysign(f64, h, x) * @sin(y));
117117 }
118118 // x < 1455: scale to avoid overflow
119119 else if (ix < 0x4096bbaa) {
120 const v = Complex(f64).init(math.fabs(x), y);
120 const v = Complex(f64).init(@fabs(x), y);
121121 const r = ldexp_cexp(v, -1);
122122 return Complex(f64).init(r.re, r.im * math.copysign(f64, 1, x));
123123 }
124124 // x >= 1455: result always overflows
125125 else {
126126 const h = 0x1p1023;
127 return Complex(f64).init(h * h * math.cos(y), h * math.sin(y));
127 return Complex(f64).init(h * h * @cos(y), h * @sin(y));
128128 }
129129 }
130130
......@@ -147,7 +147,7 @@ fn cosh64(z: Complex(f64)) Complex(f64) {
147147 if (iy >= 0x7ff00000) {
148148 return Complex(f64).init(x * x, x * (y - y));
149149 }
150 return Complex(f64).init(x * x * math.cos(y), x * math.sin(y));
150 return Complex(f64).init(x * x * @cos(y), x * @sin(y));
151151 }
152152
153153 return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y));
lib/std/math/complex/exp.zig+10-10
......@@ -33,13 +33,13 @@ fn exp32(z: Complex(f32)) Complex(f32) {
3333 const hy = @bitCast(u32, y) & 0x7fffffff;
3434 // cexp(x + i0) = exp(x) + i0
3535 if (hy == 0) {
36 return Complex(f32).init(math.exp(x), y);
36 return Complex(f32).init(@exp(x), y);
3737 }
3838
3939 const hx = @bitCast(u32, x);
4040 // cexp(0 + iy) = cos(y) + isin(y)
4141 if ((hx & 0x7fffffff) == 0) {
42 return Complex(f32).init(math.cos(y), math.sin(y));
42 return Complex(f32).init(@cos(y), @sin(y));
4343 }
4444
4545 if (hy >= 0x7f800000) {
......@@ -63,8 +63,8 @@ fn exp32(z: Complex(f32)) Complex(f32) {
6363 // - x = +-inf
6464 // - x = nan
6565 else {
66 const exp_x = math.exp(x);
67 return Complex(f32).init(exp_x * math.cos(y), exp_x * math.sin(y));
66 const exp_x = @exp(x);
67 return Complex(f32).init(exp_x * @cos(y), exp_x * @sin(y));
6868 }
6969}
7070
......@@ -81,7 +81,7 @@ fn exp64(z: Complex(f64)) Complex(f64) {
8181
8282 // cexp(x + i0) = exp(x) + i0
8383 if (hy | ly == 0) {
84 return Complex(f64).init(math.exp(x), y);
84 return Complex(f64).init(@exp(x), y);
8585 }
8686
8787 const fx = @bitCast(u64, x);
......@@ -90,7 +90,7 @@ fn exp64(z: Complex(f64)) Complex(f64) {
9090
9191 // cexp(0 + iy) = cos(y) + isin(y)
9292 if ((hx & 0x7fffffff) | lx == 0) {
93 return Complex(f64).init(math.cos(y), math.sin(y));
93 return Complex(f64).init(@cos(y), @sin(y));
9494 }
9595
9696 if (hy >= 0x7ff00000) {
......@@ -114,13 +114,13 @@ fn exp64(z: Complex(f64)) Complex(f64) {
114114 // - x = +-inf
115115 // - x = nan
116116 else {
117 const exp_x = math.exp(x);
118 return Complex(f64).init(exp_x * math.cos(y), exp_x * math.sin(y));
117 const exp_x = @exp(x);
118 return Complex(f64).init(exp_x * @cos(y), exp_x * @sin(y));
119119 }
120120}
121121
122122test "complex.cexp32" {
123 const tolerance_f32 = math.sqrt(math.floatEps(f32));
123 const tolerance_f32 = @sqrt(math.floatEps(f32));
124124
125125 {
126126 const a = Complex(f32).init(5, 3);
......@@ -140,7 +140,7 @@ test "complex.cexp32" {
140140}
141141
142142test "complex.cexp64" {
143 const tolerance_f64 = math.sqrt(math.floatEps(f64));
143 const tolerance_f64 = @sqrt(math.floatEps(f64));
144144
145145 {
146146 const a = Complex(f64).init(5, 3);
lib/std/math/complex/ldexp.zig+6-6
......@@ -26,7 +26,7 @@ fn frexp_exp32(x: f32, expt: *i32) f32 {
2626 const k = 235; // reduction constant
2727 const kln2 = 162.88958740; // k * ln2
2828
29 const exp_x = math.exp(x - kln2);
29 const exp_x = @exp(x - kln2);
3030 const hx = @bitCast(u32, exp_x);
3131 // TODO zig should allow this cast implicitly because it should know the value is in range
3232 expt.* = @intCast(i32, hx >> 23) - (0x7f + 127) + k;
......@@ -45,8 +45,8 @@ fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) {
4545 const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);
4646
4747 return Complex(f32).init(
48 math.cos(z.im) * exp_x * scale1 * scale2,
49 math.sin(z.im) * exp_x * scale1 * scale2,
48 @cos(z.im) * exp_x * scale1 * scale2,
49 @sin(z.im) * exp_x * scale1 * scale2,
5050 );
5151}
5252
......@@ -54,7 +54,7 @@ fn frexp_exp64(x: f64, expt: *i32) f64 {
5454 const k = 1799; // reduction constant
5555 const kln2 = 1246.97177782734161156; // k * ln2
5656
57 const exp_x = math.exp(x - kln2);
57 const exp_x = @exp(x - kln2);
5858
5959 const fx = @bitCast(u64, exp_x);
6060 const hx = @intCast(u32, fx >> 32);
......@@ -78,7 +78,7 @@ fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) {
7878 const scale2 = @bitCast(f64, (0x3ff + half_expt2) << (20 + 32));
7979
8080 return Complex(f64).init(
81 math.cos(z.im) * exp_x * scale1 * scale2,
82 math.sin(z.im) * exp_x * scale1 * scale2,
81 @cos(z.im) * exp_x * scale1 * scale2,
82 @sin(z.im) * exp_x * scale1 * scale2,
8383 );
8484}
lib/std/math/complex/log.zig+1-1
......@@ -10,7 +10,7 @@ pub fn log(z: anytype) Complex(@TypeOf(z.re)) {
1010 const r = cmath.abs(z);
1111 const phi = cmath.arg(z);
1212
13 return Complex(T).init(math.ln(r), phi);
13 return Complex(T).init(@log(r), phi);
1414}
1515
1616const epsilon = 0.0001;
lib/std/math/complex/sinh.zig+12-12
......@@ -38,25 +38,25 @@ fn sinh32(z: Complex(f32)) Complex(f32) {
3838 }
3939 // small x: normal case
4040 if (ix < 0x41100000) {
41 return Complex(f32).init(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
41 return Complex(f32).init(math.sinh(x) * @cos(y), math.cosh(x) * @sin(y));
4242 }
4343
4444 // |x|>= 9, so cosh(x) ~= exp(|x|)
4545 if (ix < 0x42b17218) {
4646 // x < 88.7: exp(|x|) won't overflow
47 const h = math.exp(math.fabs(x)) * 0.5;
48 return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
47 const h = @exp(@fabs(x)) * 0.5;
48 return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y));
4949 }
5050 // x < 192.7: scale to avoid overflow
5151 else if (ix < 0x4340b1e7) {
52 const v = Complex(f32).init(math.fabs(x), y);
52 const v = Complex(f32).init(@fabs(x), y);
5353 const r = ldexp_cexp(v, -1);
5454 return Complex(f32).init(r.re * math.copysign(f32, 1, x), r.im);
5555 }
5656 // x >= 192.7: result always overflows
5757 else {
5858 const h = 0x1p127 * x;
59 return Complex(f32).init(h * math.cos(y), h * h * math.sin(y));
59 return Complex(f32).init(h * @cos(y), h * h * @sin(y));
6060 }
6161 }
6262
......@@ -79,7 +79,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) {
7979 if (iy >= 0x7f800000) {
8080 return Complex(f32).init(x * x, x * (y - y));
8181 }
82 return Complex(f32).init(x * math.cos(y), math.inf(f32) * math.sin(y));
82 return Complex(f32).init(x * @cos(y), math.inf(f32) * @sin(y));
8383 }
8484
8585 return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y));
......@@ -105,25 +105,25 @@ fn sinh64(z: Complex(f64)) Complex(f64) {
105105 }
106106 // small x: normal case
107107 if (ix < 0x40360000) {
108 return Complex(f64).init(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
108 return Complex(f64).init(math.sinh(x) * @cos(y), math.cosh(x) * @sin(y));
109109 }
110110
111111 // |x|>= 22, so cosh(x) ~= exp(|x|)
112112 if (ix < 0x40862e42) {
113113 // x < 710: exp(|x|) won't overflow
114 const h = math.exp(math.fabs(x)) * 0.5;
115 return Complex(f64).init(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));
114 const h = @exp(@fabs(x)) * 0.5;
115 return Complex(f64).init(math.copysign(f64, h, x) * @cos(y), h * @sin(y));
116116 }
117117 // x < 1455: scale to avoid overflow
118118 else if (ix < 0x4096bbaa) {
119 const v = Complex(f64).init(math.fabs(x), y);
119 const v = Complex(f64).init(@fabs(x), y);
120120 const r = ldexp_cexp(v, -1);
121121 return Complex(f64).init(r.re * math.copysign(f64, 1, x), r.im);
122122 }
123123 // x >= 1455: result always overflows
124124 else {
125125 const h = 0x1p1023 * x;
126 return Complex(f64).init(h * math.cos(y), h * h * math.sin(y));
126 return Complex(f64).init(h * @cos(y), h * h * @sin(y));
127127 }
128128 }
129129
......@@ -146,7 +146,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) {
146146 if (iy >= 0x7ff00000) {
147147 return Complex(f64).init(x * x, x * (y - y));
148148 }
149 return Complex(f64).init(x * math.cos(y), math.inf(f64) * math.sin(y));
149 return Complex(f64).init(x * @cos(y), math.inf(f64) * @sin(y));
150150 }
151151
152152 return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y));
lib/std/math/complex/sqrt.zig+9-9
......@@ -43,7 +43,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) {
4343 // sqrt(-inf + i nan) = nan +- inf i
4444 // sqrt(-inf + iy) = 0 + inf i
4545 if (math.signbit(x)) {
46 return Complex(f32).init(math.fabs(x - y), math.copysign(f32, x, y));
46 return Complex(f32).init(@fabs(x - y), math.copysign(f32, x, y));
4747 } else {
4848 return Complex(f32).init(x, math.copysign(f32, y - y, y));
4949 }
......@@ -56,15 +56,15 @@ fn sqrt32(z: Complex(f32)) Complex(f32) {
5656 const dy = @as(f64, y);
5757
5858 if (dx >= 0) {
59 const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5);
59 const t = @sqrt((dx + math.hypot(f64, dx, dy)) * 0.5);
6060 return Complex(f32).init(
6161 @floatCast(f32, t),
6262 @floatCast(f32, dy / (2.0 * t)),
6363 );
6464 } else {
65 const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
65 const t = @sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
6666 return Complex(f32).init(
67 @floatCast(f32, math.fabs(y) / (2.0 * t)),
67 @floatCast(f32, @fabs(y) / (2.0 * t)),
6868 @floatCast(f32, math.copysign(f64, t, y)),
6969 );
7070 }
......@@ -94,7 +94,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
9494 // sqrt(-inf + i nan) = nan +- inf i
9595 // sqrt(-inf + iy) = 0 + inf i
9696 if (math.signbit(x)) {
97 return Complex(f64).init(math.fabs(x - y), math.copysign(f64, x, y));
97 return Complex(f64).init(@fabs(x - y), math.copysign(f64, x, y));
9898 } else {
9999 return Complex(f64).init(x, math.copysign(f64, y - y, y));
100100 }
......@@ -104,7 +104,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
104104
105105 // scale to avoid overflow
106106 var scale = false;
107 if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) {
107 if (@fabs(x) >= threshold or @fabs(y) >= threshold) {
108108 x *= 0.25;
109109 y *= 0.25;
110110 scale = true;
......@@ -112,11 +112,11 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
112112
113113 var result: Complex(f64) = undefined;
114114 if (x >= 0) {
115 const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5);
115 const t = @sqrt((x + math.hypot(f64, x, y)) * 0.5);
116116 result = Complex(f64).init(t, y / (2.0 * t));
117117 } else {
118 const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5);
119 result = Complex(f64).init(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y));
118 const t = @sqrt((-x + math.hypot(f64, x, y)) * 0.5);
119 result = Complex(f64).init(@fabs(y) / (2.0 * t), math.copysign(f64, t, y));
120120 }
121121
122122 if (scale) {
lib/std/math/complex/tanh.zig+10-10
......@@ -33,7 +33,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) {
3333 return Complex(f32).init(x, r);
3434 }
3535 const xx = @bitCast(f32, hx - 0x40000000);
36 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
36 const r = if (math.isInf(y)) y else @sin(y) * @cos(y);
3737 return Complex(f32).init(xx, math.copysign(f32, 0, r));
3838 }
3939
......@@ -44,15 +44,15 @@ fn tanh32(z: Complex(f32)) Complex(f32) {
4444
4545 // x >= 11
4646 if (ix >= 0x41300000) {
47 const exp_mx = math.exp(-math.fabs(x));
48 return Complex(f32).init(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
47 const exp_mx = @exp(-@fabs(x));
48 return Complex(f32).init(math.copysign(f32, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx);
4949 }
5050
5151 // Kahan's algorithm
52 const t = math.tan(y);
52 const t = @tan(y);
5353 const beta = 1.0 + t * t;
5454 const s = math.sinh(x);
55 const rho = math.sqrt(1 + s * s);
55 const rho = @sqrt(1 + s * s);
5656 const den = 1 + beta * s * s;
5757
5858 return Complex(f32).init((beta * rho * s) / den, t / den);
......@@ -76,7 +76,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) {
7676 }
7777
7878 const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx);
79 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
79 const r = if (math.isInf(y)) y else @sin(y) * @cos(y);
8080 return Complex(f64).init(xx, math.copysign(f64, 0, r));
8181 }
8282
......@@ -87,15 +87,15 @@ fn tanh64(z: Complex(f64)) Complex(f64) {
8787
8888 // x >= 22
8989 if (ix >= 0x40360000) {
90 const exp_mx = math.exp(-math.fabs(x));
91 return Complex(f64).init(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
90 const exp_mx = @exp(-@fabs(x));
91 return Complex(f64).init(math.copysign(f64, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx);
9292 }
9393
9494 // Kahan's algorithm
95 const t = math.tan(y);
95 const t = @tan(y);
9696 const beta = 1.0 + t * t;
9797 const s = math.sinh(x);
98 const rho = math.sqrt(1 + s * s);
98 const rho = @sqrt(1 + s * s);
9999 const den = 1 + beta * s * s;
100100
101101 return Complex(f64).init((beta * rho * s) / den, t / den);
lib/std/math/cos.zig deleted-154
......@@ -1,154 +0,0 @@
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/cosf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/cos.c
6
7const std = @import("../std.zig");
8const math = std.math;
9const expect = std.testing.expect;
10
11const kernel = @import("__trig.zig");
12const __rem_pio2 = @import("__rem_pio2.zig").__rem_pio2;
13const __rem_pio2f = @import("__rem_pio2f.zig").__rem_pio2f;
14
15/// Returns the cosine of the radian value x.
16///
17/// Special Cases:
18/// - cos(+-inf) = nan
19/// - cos(nan) = nan
20pub fn cos(x: anytype) @TypeOf(x) {
21 const T = @TypeOf(x);
22 return switch (T) {
23 f32 => cos32(x),
24 f64 => cos64(x),
25 else => @compileError("cos not implemented for " ++ @typeName(T)),
26 };
27}
28
29fn cos32(x: f32) f32 {
30 // Small multiples of pi/2 rounded to double precision.
31 const c1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
32 const c2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
33 const c3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
34 const c4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
35
36 var ix = @bitCast(u32, x);
37 const sign = ix >> 31 != 0;
38 ix &= 0x7fffffff;
39
40 if (ix <= 0x3f490fda) { // |x| ~<= pi/4
41 if (ix < 0x39800000) { // |x| < 2**-12
42 // raise inexact if x != 0
43 math.doNotOptimizeAway(x + 0x1p120);
44 return 1.0;
45 }
46 return kernel.__cosdf(x);
47 }
48 if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4
49 if (ix > 0x4016cbe3) { // |x| ~> 3*pi/4
50 return -kernel.__cosdf(if (sign) x + c2pio2 else x - c2pio2);
51 } else {
52 if (sign) {
53 return kernel.__sindf(x + c1pio2);
54 } else {
55 return kernel.__sindf(c1pio2 - x);
56 }
57 }
58 }
59 if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4
60 if (ix > 0x40afeddf) { // |x| ~> 7*pi/4
61 return kernel.__cosdf(if (sign) x + c4pio2 else x - c4pio2);
62 } else {
63 if (sign) {
64 return kernel.__sindf(-x - c3pio2);
65 } else {
66 return kernel.__sindf(x - c3pio2);
67 }
68 }
69 }
70
71 // cos(Inf or NaN) is NaN
72 if (ix >= 0x7f800000) {
73 return x - x;
74 }
75
76 var y: f64 = undefined;
77 const n = __rem_pio2f(x, &y);
78 return switch (n & 3) {
79 0 => kernel.__cosdf(y),
80 1 => kernel.__sindf(-y),
81 2 => -kernel.__cosdf(y),
82 else => kernel.__sindf(y),
83 };
84}
85
86fn cos64(x: f64) f64 {
87 var ix = @bitCast(u64, x) >> 32;
88 ix &= 0x7fffffff;
89
90 // |x| ~< pi/4
91 if (ix <= 0x3fe921fb) {
92 if (ix < 0x3e46a09e) { // |x| < 2**-27 * sqrt(2)
93 // raise inexact if x!=0
94 math.doNotOptimizeAway(x + 0x1p120);
95 return 1.0;
96 }
97 return kernel.__cos(x, 0);
98 }
99
100 // cos(Inf or NaN) is NaN
101 if (ix >= 0x7ff00000) {
102 return x - x;
103 }
104
105 var y: [2]f64 = undefined;
106 const n = __rem_pio2(x, &y);
107 return switch (n & 3) {
108 0 => kernel.__cos(y[0], y[1]),
109 1 => -kernel.__sin(y[0], y[1], 1),
110 2 => -kernel.__cos(y[0], y[1]),
111 else => kernel.__sin(y[0], y[1], 1),
112 };
113}
114
115test "math.cos" {
116 try expect(cos(@as(f32, 0.0)) == cos32(0.0));
117 try expect(cos(@as(f64, 0.0)) == cos64(0.0));
118}
119
120test "math.cos32" {
121 const epsilon = 0.00001;
122
123 try expect(math.approxEqAbs(f32, cos32(0.0), 1.0, epsilon));
124 try expect(math.approxEqAbs(f32, cos32(0.2), 0.980067, epsilon));
125 try expect(math.approxEqAbs(f32, cos32(0.8923), 0.627623, epsilon));
126 try expect(math.approxEqAbs(f32, cos32(1.5), 0.070737, epsilon));
127 try expect(math.approxEqAbs(f32, cos32(-1.5), 0.070737, epsilon));
128 try expect(math.approxEqAbs(f32, cos32(37.45), 0.969132, epsilon));
129 try expect(math.approxEqAbs(f32, cos32(89.123), 0.400798, epsilon));
130}
131
132test "math.cos64" {
133 const epsilon = 0.000001;
134
135 try expect(math.approxEqAbs(f64, cos64(0.0), 1.0, epsilon));
136 try expect(math.approxEqAbs(f64, cos64(0.2), 0.980067, epsilon));
137 try expect(math.approxEqAbs(f64, cos64(0.8923), 0.627623, epsilon));
138 try expect(math.approxEqAbs(f64, cos64(1.5), 0.070737, epsilon));
139 try expect(math.approxEqAbs(f64, cos64(-1.5), 0.070737, epsilon));
140 try expect(math.approxEqAbs(f64, cos64(37.45), 0.969132, epsilon));
141 try expect(math.approxEqAbs(f64, cos64(89.123), 0.40080, epsilon));
142}
143
144test "math.cos32.special" {
145 try expect(math.isNan(cos32(math.inf(f32))));
146 try expect(math.isNan(cos32(-math.inf(f32))));
147 try expect(math.isNan(cos32(math.nan(f32))));
148}
149
150test "math.cos64.special" {
151 try expect(math.isNan(cos64(math.inf(f64))));
152 try expect(math.isNan(cos64(-math.inf(f64))));
153 try expect(math.isNan(cos64(math.nan(f64))));
154}
lib/std/math/cosh.zig+2-2
......@@ -45,7 +45,7 @@ fn cosh32(x: f32) f32 {
4545
4646 // |x| < log(FLT_MAX)
4747 if (ux < 0x42B17217) {
48 const t = math.exp(ax);
48 const t = @exp(ax);
4949 return 0.5 * (t + 1 / t);
5050 }
5151
......@@ -77,7 +77,7 @@ fn cosh64(x: f64) f64 {
7777
7878 // |x| < log(DBL_MAX)
7979 if (w < 0x40862E42) {
80 const t = math.exp(ax);
80 const t = @exp(ax);
8181 // NOTE: If x > log(0x1p26) then 1/t is not required.
8282 return 0.5 * (t + 1 / t);
8383 }
lib/std/math/exp.zig deleted-217
......@@ -1,217 +0,0 @@
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/expf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/exp.c
6
7const std = @import("../std.zig");
8const math = std.math;
9const expect = std.testing.expect;
10
11/// Returns e raised to the power of x (e^x).
12///
13/// Special Cases:
14/// - exp(+inf) = +inf
15/// - exp(nan) = nan
16pub fn exp(x: anytype) @TypeOf(x) {
17 const T = @TypeOf(x);
18 return switch (T) {
19 f32 => exp32(x),
20 f64 => exp64(x),
21 else => @compileError("exp not implemented for " ++ @typeName(T)),
22 };
23}
24
25fn exp32(x_: f32) f32 {
26 const half = [_]f32{ 0.5, -0.5 };
27 const ln2hi = 6.9314575195e-1;
28 const ln2lo = 1.4286067653e-6;
29 const invln2 = 1.4426950216e+0;
30 const P1 = 1.6666625440e-1;
31 const P2 = -2.7667332906e-3;
32
33 var x = x_;
34 var hx = @bitCast(u32, x);
35 const sign = @intCast(i32, hx >> 31);
36 hx &= 0x7FFFFFFF;
37
38 if (math.isNan(x)) {
39 return x;
40 }
41
42 // |x| >= -87.33655 or nan
43 if (hx >= 0x42AEAC50) {
44 // nan
45 if (hx > 0x7F800000) {
46 return x;
47 }
48 // x >= 88.722839
49 if (hx >= 0x42b17218 and sign == 0) {
50 return x * 0x1.0p127;
51 }
52 if (sign != 0) {
53 math.doNotOptimizeAway(-0x1.0p-149 / x); // overflow
54 // x <= -103.972084
55 if (hx >= 0x42CFF1B5) {
56 return 0;
57 }
58 }
59 }
60
61 var k: i32 = undefined;
62 var hi: f32 = undefined;
63 var lo: f32 = undefined;
64
65 // |x| > 0.5 * ln2
66 if (hx > 0x3EB17218) {
67 // |x| > 1.5 * ln2
68 if (hx > 0x3F851592) {
69 k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]);
70 } else {
71 k = 1 - sign - sign;
72 }
73
74 const fk = @intToFloat(f32, k);
75 hi = x - fk * ln2hi;
76 lo = fk * ln2lo;
77 x = hi - lo;
78 }
79 // |x| > 2^(-14)
80 else if (hx > 0x39000000) {
81 k = 0;
82 hi = x;
83 lo = 0;
84 } else {
85 math.doNotOptimizeAway(0x1.0p127 + x); // inexact
86 return 1 + x;
87 }
88
89 const xx = x * x;
90 const c = x - xx * (P1 + xx * P2);
91 const y = 1 + (x * c / (2 - c) - lo + hi);
92
93 if (k == 0) {
94 return y;
95 } else {
96 return math.scalbn(y, k);
97 }
98}
99
100fn exp64(x_: f64) f64 {
101 const half = [_]f64{ 0.5, -0.5 };
102 const ln2hi: f64 = 6.93147180369123816490e-01;
103 const ln2lo: f64 = 1.90821492927058770002e-10;
104 const invln2: f64 = 1.44269504088896338700e+00;
105 const P1: f64 = 1.66666666666666019037e-01;
106 const P2: f64 = -2.77777777770155933842e-03;
107 const P3: f64 = 6.61375632143793436117e-05;
108 const P4: f64 = -1.65339022054652515390e-06;
109 const P5: f64 = 4.13813679705723846039e-08;
110
111 var x = x_;
112 var ux = @bitCast(u64, x);
113 var hx = ux >> 32;
114 const sign = @intCast(i32, hx >> 31);
115 hx &= 0x7FFFFFFF;
116
117 if (math.isNan(x)) {
118 return x;
119 }
120
121 // |x| >= 708.39 or nan
122 if (hx >= 0x4086232B) {
123 // nan
124 if (hx > 0x7FF00000) {
125 return x;
126 }
127 if (x > 709.782712893383973096) {
128 // overflow if x != inf
129 if (!math.isInf(x)) {
130 math.raiseOverflow();
131 }
132 return math.inf(f64);
133 }
134 if (x < -708.39641853226410622) {
135 // underflow if x != -inf
136 // math.doNotOptimizeAway(@as(f32, -0x1.0p-149 / x));
137 if (x < -745.13321910194110842) {
138 return 0;
139 }
140 }
141 }
142
143 // argument reduction
144 var k: i32 = undefined;
145 var hi: f64 = undefined;
146 var lo: f64 = undefined;
147
148 // |x| > 0.5 * ln2
149 if (hx > 0x3FD62E42) {
150 // |x| >= 1.5 * ln2
151 if (hx > 0x3FF0A2B2) {
152 k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]);
153 } else {
154 k = 1 - sign - sign;
155 }
156
157 const dk = @intToFloat(f64, k);
158 hi = x - dk * ln2hi;
159 lo = dk * ln2lo;
160 x = hi - lo;
161 }
162 // |x| > 2^(-28)
163 else if (hx > 0x3E300000) {
164 k = 0;
165 hi = x;
166 lo = 0;
167 } else {
168 // inexact if x != 0
169 // math.doNotOptimizeAway(0x1.0p1023 + x);
170 return 1 + x;
171 }
172
173 const xx = x * x;
174 const c = x - xx * (P1 + xx * (P2 + xx * (P3 + xx * (P4 + xx * P5))));
175 const y = 1 + (x * c / (2 - c) - lo + hi);
176
177 if (k == 0) {
178 return y;
179 } else {
180 return math.scalbn(y, k);
181 }
182}
183
184test "math.exp" {
185 try expect(exp(@as(f32, 0.0)) == exp32(0.0));
186 try expect(exp(@as(f64, 0.0)) == exp64(0.0));
187}
188
189test "math.exp32" {
190 const epsilon = 0.000001;
191
192 try expect(exp32(0.0) == 1.0);
193 try expect(math.approxEqAbs(f32, exp32(0.0), 1.0, epsilon));
194 try expect(math.approxEqAbs(f32, exp32(0.2), 1.221403, epsilon));
195 try expect(math.approxEqAbs(f32, exp32(0.8923), 2.440737, epsilon));
196 try expect(math.approxEqAbs(f32, exp32(1.5), 4.481689, epsilon));
197}
198
199test "math.exp64" {
200 const epsilon = 0.000001;
201
202 try expect(exp64(0.0) == 1.0);
203 try expect(math.approxEqAbs(f64, exp64(0.0), 1.0, epsilon));
204 try expect(math.approxEqAbs(f64, exp64(0.2), 1.221403, epsilon));
205 try expect(math.approxEqAbs(f64, exp64(0.8923), 2.440737, epsilon));
206 try expect(math.approxEqAbs(f64, exp64(1.5), 4.481689, epsilon));
207}
208
209test "math.exp32.special" {
210 try expect(math.isPositiveInf(exp32(math.inf(f32))));
211 try expect(math.isNan(exp32(math.nan(f32))));
212}
213
214test "math.exp64.special" {
215 try expect(math.isPositiveInf(exp64(math.inf(f64))));
216 try expect(math.isNan(exp64(math.nan(f64))));
217}
lib/std/math/exp2.zig deleted-465
......@@ -1,465 +0,0 @@
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/exp2f.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/exp2.c
6
7const std = @import("../std.zig");
8const math = std.math;
9const expect = std.testing.expect;
10
11/// Returns 2 raised to the power of x (2^x).
12///
13/// Special Cases:
14/// - exp2(+inf) = +inf
15/// - exp2(nan) = nan
16pub fn exp2(x: anytype) @TypeOf(x) {
17 const T = @TypeOf(x);
18 return switch (T) {
19 f32 => exp2_32(x),
20 f64 => exp2_64(x),
21 else => @compileError("exp2 not implemented for " ++ @typeName(T)),
22 };
23}
24
25const exp2ft = [_]f64{
26 0x1.6a09e667f3bcdp-1,
27 0x1.7a11473eb0187p-1,
28 0x1.8ace5422aa0dbp-1,
29 0x1.9c49182a3f090p-1,
30 0x1.ae89f995ad3adp-1,
31 0x1.c199bdd85529cp-1,
32 0x1.d5818dcfba487p-1,
33 0x1.ea4afa2a490dap-1,
34 0x1.0000000000000p+0,
35 0x1.0b5586cf9890fp+0,
36 0x1.172b83c7d517bp+0,
37 0x1.2387a6e756238p+0,
38 0x1.306fe0a31b715p+0,
39 0x1.3dea64c123422p+0,
40 0x1.4bfdad5362a27p+0,
41 0x1.5ab07dd485429p+0,
42};
43
44fn exp2_32(x: f32) f32 {
45 const tblsiz = @intCast(u32, exp2ft.len);
46 const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz);
47 const P1: f32 = 0x1.62e430p-1;
48 const P2: f32 = 0x1.ebfbe0p-3;
49 const P3: f32 = 0x1.c6b348p-5;
50 const P4: f32 = 0x1.3b2c9cp-7;
51
52 var u = @bitCast(u32, x);
53 const ix = u & 0x7FFFFFFF;
54
55 // |x| > 126
56 if (ix > 0x42FC0000) {
57 // nan
58 if (ix > 0x7F800000) {
59 return x;
60 }
61 // x >= 128
62 if (u >= 0x43000000 and u < 0x80000000) {
63 return x * 0x1.0p127;
64 }
65 // x < -126
66 if (u >= 0x80000000) {
67 if (u >= 0xC3160000 or u & 0x000FFFF != 0) {
68 math.doNotOptimizeAway(-0x1.0p-149 / x);
69 }
70 // x <= -150
71 if (u >= 0x3160000) {
72 return 0;
73 }
74 }
75 }
76 // |x| <= 0x1p-25
77 else if (ix <= 0x33000000) {
78 return 1.0 + x;
79 }
80
81 // NOTE: musl relies on unsafe behaviours which are replicated below
82 // (addition/bit-shift overflow). Appears that this produces the
83 // intended result but should confirm how GCC/Clang handle this to ensure.
84
85 var uf = x + redux;
86 var i_0 = @bitCast(u32, uf);
87 i_0 +%= tblsiz / 2;
88
89 const k = i_0 / tblsiz;
90 const uk = @bitCast(f64, @as(u64, 0x3FF + k) << 52);
91 i_0 &= tblsiz - 1;
92 uf -= redux;
93
94 const z: f64 = x - uf;
95 var r: f64 = exp2ft[@intCast(usize, i_0)];
96 const t: f64 = r * z;
97 r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4);
98 return @floatCast(f32, r * uk);
99}
100
101const exp2dt = [_]f64{
102 // exp2(z + eps) eps
103 0x1.6a09e667f3d5dp-1, 0x1.9880p-44,
104 0x1.6b052fa751744p-1, 0x1.8000p-50,
105 0x1.6c012750bd9fep-1, -0x1.8780p-45,
106 0x1.6cfdcddd476bfp-1, 0x1.ec00p-46,
107 0x1.6dfb23c651a29p-1, -0x1.8000p-50,
108 0x1.6ef9298593ae3p-1, -0x1.c000p-52,
109 0x1.6ff7df9519386p-1, -0x1.fd80p-45,
110 0x1.70f7466f42da3p-1, -0x1.c880p-45,
111 0x1.71f75e8ec5fc3p-1, 0x1.3c00p-46,
112 0x1.72f8286eacf05p-1, -0x1.8300p-44,
113 0x1.73f9a48a58152p-1, -0x1.0c00p-47,
114 0x1.74fbd35d7ccfcp-1, 0x1.f880p-45,
115 0x1.75feb564267f1p-1, 0x1.3e00p-47,
116 0x1.77024b1ab6d48p-1, -0x1.7d00p-45,
117 0x1.780694fde5d38p-1, -0x1.d000p-50,
118 0x1.790b938ac1d00p-1, 0x1.3000p-49,
119 0x1.7a11473eb0178p-1, -0x1.d000p-49,
120 0x1.7b17b0976d060p-1, 0x1.0400p-45,
121 0x1.7c1ed0130c133p-1, 0x1.0000p-53,
122 0x1.7d26a62ff8636p-1, -0x1.6900p-45,
123 0x1.7e2f336cf4e3bp-1, -0x1.2e00p-47,
124 0x1.7f3878491c3e8p-1, -0x1.4580p-45,
125 0x1.80427543e1b4ep-1, 0x1.3000p-44,
126 0x1.814d2add1071ap-1, 0x1.f000p-47,
127 0x1.82589994ccd7ep-1, -0x1.1c00p-45,
128 0x1.8364c1eb942d0p-1, 0x1.9d00p-45,
129 0x1.8471a4623cab5p-1, 0x1.7100p-43,
130 0x1.857f4179f5bbcp-1, 0x1.2600p-45,
131 0x1.868d99b4491afp-1, -0x1.2c40p-44,
132 0x1.879cad931a395p-1, -0x1.3000p-45,
133 0x1.88ac7d98a65b8p-1, -0x1.a800p-45,
134 0x1.89bd0a4785800p-1, -0x1.d000p-49,
135 0x1.8ace5422aa223p-1, 0x1.3280p-44,
136 0x1.8be05bad619fap-1, 0x1.2b40p-43,
137 0x1.8cf3216b54383p-1, -0x1.ed00p-45,
138 0x1.8e06a5e08664cp-1, -0x1.0500p-45,
139 0x1.8f1ae99157807p-1, 0x1.8280p-45,
140 0x1.902fed0282c0ep-1, -0x1.cb00p-46,
141 0x1.9145b0b91ff96p-1, -0x1.5e00p-47,
142 0x1.925c353aa2ff9p-1, 0x1.5400p-48,
143 0x1.93737b0cdc64ap-1, 0x1.7200p-46,
144 0x1.948b82b5f98aep-1, -0x1.9000p-47,
145 0x1.95a44cbc852cbp-1, 0x1.5680p-45,
146 0x1.96bdd9a766f21p-1, -0x1.6d00p-44,
147 0x1.97d829fde4e2ap-1, -0x1.1000p-47,
148 0x1.98f33e47a23a3p-1, 0x1.d000p-45,
149 0x1.9a0f170ca0604p-1, -0x1.8a40p-44,
150 0x1.9b2bb4d53ff89p-1, 0x1.55c0p-44,
151 0x1.9c49182a3f15bp-1, 0x1.6b80p-45,
152 0x1.9d674194bb8c5p-1, -0x1.c000p-49,
153 0x1.9e86319e3238ep-1, 0x1.7d00p-46,
154 0x1.9fa5e8d07f302p-1, 0x1.6400p-46,
155 0x1.a0c667b5de54dp-1, -0x1.5000p-48,
156 0x1.a1e7aed8eb8f6p-1, 0x1.9e00p-47,
157 0x1.a309bec4a2e27p-1, 0x1.ad80p-45,
158 0x1.a42c980460a5dp-1, -0x1.af00p-46,
159 0x1.a5503b23e259bp-1, 0x1.b600p-47,
160 0x1.a674a8af46213p-1, 0x1.8880p-44,
161 0x1.a799e1330b3a7p-1, 0x1.1200p-46,
162 0x1.a8bfe53c12e8dp-1, 0x1.6c00p-47,
163 0x1.a9e6b5579fcd2p-1, -0x1.9b80p-45,
164 0x1.ab0e521356fb8p-1, 0x1.b700p-45,
165 0x1.ac36bbfd3f381p-1, 0x1.9000p-50,
166 0x1.ad5ff3a3c2780p-1, 0x1.4000p-49,
167 0x1.ae89f995ad2a3p-1, -0x1.c900p-45,
168 0x1.afb4ce622f367p-1, 0x1.6500p-46,
169 0x1.b0e07298db790p-1, 0x1.fd40p-45,
170 0x1.b20ce6c9a89a9p-1, 0x1.2700p-46,
171 0x1.b33a2b84f1a4bp-1, 0x1.d470p-43,
172 0x1.b468415b747e7p-1, -0x1.8380p-44,
173 0x1.b59728de5593ap-1, 0x1.8000p-54,
174 0x1.b6c6e29f1c56ap-1, 0x1.ad00p-47,
175 0x1.b7f76f2fb5e50p-1, 0x1.e800p-50,
176 0x1.b928cf22749b2p-1, -0x1.4c00p-47,
177 0x1.ba5b030a10603p-1, -0x1.d700p-47,
178 0x1.bb8e0b79a6f66p-1, 0x1.d900p-47,
179 0x1.bcc1e904bc1ffp-1, 0x1.2a00p-47,
180 0x1.bdf69c3f3a16fp-1, -0x1.f780p-46,
181 0x1.bf2c25bd71db8p-1, -0x1.0a00p-46,
182 0x1.c06286141b2e9p-1, -0x1.1400p-46,
183 0x1.c199bdd8552e0p-1, 0x1.be00p-47,
184 0x1.c2d1cd9fa64eep-1, -0x1.9400p-47,
185 0x1.c40ab5fffd02fp-1, -0x1.ed00p-47,
186 0x1.c544778fafd15p-1, 0x1.9660p-44,
187 0x1.c67f12e57d0cbp-1, -0x1.a100p-46,
188 0x1.c7ba88988c1b6p-1, -0x1.8458p-42,
189 0x1.c8f6d9406e733p-1, -0x1.a480p-46,
190 0x1.ca3405751c4dfp-1, 0x1.b000p-51,
191 0x1.cb720dcef9094p-1, 0x1.1400p-47,
192 0x1.ccb0f2e6d1689p-1, 0x1.0200p-48,
193 0x1.cdf0b555dc412p-1, 0x1.3600p-48,
194 0x1.cf3155b5bab3bp-1, -0x1.6900p-47,
195 0x1.d072d4a0789bcp-1, 0x1.9a00p-47,
196 0x1.d1b532b08c8fap-1, -0x1.5e00p-46,
197 0x1.d2f87080d8a85p-1, 0x1.d280p-46,
198 0x1.d43c8eacaa203p-1, 0x1.1a00p-47,
199 0x1.d5818dcfba491p-1, 0x1.f000p-50,
200 0x1.d6c76e862e6a1p-1, -0x1.3a00p-47,
201 0x1.d80e316c9834ep-1, -0x1.cd80p-47,
202 0x1.d955d71ff6090p-1, 0x1.4c00p-48,
203 0x1.da9e603db32aep-1, 0x1.f900p-48,
204 0x1.dbe7cd63a8325p-1, 0x1.9800p-49,
205 0x1.dd321f301b445p-1, -0x1.5200p-48,
206 0x1.de7d5641c05bfp-1, -0x1.d700p-46,
207 0x1.dfc97337b9aecp-1, -0x1.6140p-46,
208 0x1.e11676b197d5ep-1, 0x1.b480p-47,
209 0x1.e264614f5a3e7p-1, 0x1.0ce0p-43,
210 0x1.e3b333b16ee5cp-1, 0x1.c680p-47,
211 0x1.e502ee78b3fb4p-1, -0x1.9300p-47,
212 0x1.e653924676d68p-1, -0x1.5000p-49,
213 0x1.e7a51fbc74c44p-1, -0x1.7f80p-47,
214 0x1.e8f7977cdb726p-1, -0x1.3700p-48,
215 0x1.ea4afa2a490e8p-1, 0x1.5d00p-49,
216 0x1.eb9f4867ccae4p-1, 0x1.61a0p-46,
217 0x1.ecf482d8e680dp-1, 0x1.5500p-48,
218 0x1.ee4aaa2188514p-1, 0x1.6400p-51,
219 0x1.efa1bee615a13p-1, -0x1.e800p-49,
220 0x1.f0f9c1cb64106p-1, -0x1.a880p-48,
221 0x1.f252b376bb963p-1, -0x1.c900p-45,
222 0x1.f3ac948dd7275p-1, 0x1.a000p-53,
223 0x1.f50765b6e4524p-1, -0x1.4f00p-48,
224 0x1.f6632798844fdp-1, 0x1.a800p-51,
225 0x1.f7bfdad9cbe38p-1, 0x1.abc0p-48,
226 0x1.f91d802243c82p-1, -0x1.4600p-50,
227 0x1.fa7c1819e908ep-1, -0x1.b0c0p-47,
228 0x1.fbdba3692d511p-1, -0x1.0e00p-51,
229 0x1.fd3c22b8f7194p-1, -0x1.0de8p-46,
230 0x1.fe9d96b2a23eep-1, 0x1.e430p-49,
231 0x1.0000000000000p+0, 0x0.0000p+0,
232 0x1.00b1afa5abcbep+0, -0x1.3400p-52,
233 0x1.0163da9fb3303p+0, -0x1.2170p-46,
234 0x1.02168143b0282p+0, 0x1.a400p-52,
235 0x1.02c9a3e77806cp+0, 0x1.f980p-49,
236 0x1.037d42e11bbcap+0, -0x1.7400p-51,
237 0x1.04315e86e7f89p+0, 0x1.8300p-50,
238 0x1.04e5f72f65467p+0, -0x1.a3f0p-46,
239 0x1.059b0d315855ap+0, -0x1.2840p-47,
240 0x1.0650a0e3c1f95p+0, 0x1.1600p-48,
241 0x1.0706b29ddf71ap+0, 0x1.5240p-46,
242 0x1.07bd42b72a82dp+0, -0x1.9a00p-49,
243 0x1.0874518759bd0p+0, 0x1.6400p-49,
244 0x1.092bdf66607c8p+0, -0x1.0780p-47,
245 0x1.09e3ecac6f383p+0, -0x1.8000p-54,
246 0x1.0a9c79b1f3930p+0, 0x1.fa00p-48,
247 0x1.0b5586cf988fcp+0, -0x1.ac80p-48,
248 0x1.0c0f145e46c8ap+0, 0x1.9c00p-50,
249 0x1.0cc922b724816p+0, 0x1.5200p-47,
250 0x1.0d83b23395dd8p+0, -0x1.ad00p-48,
251 0x1.0e3ec32d3d1f3p+0, 0x1.bac0p-46,
252 0x1.0efa55fdfa9a6p+0, -0x1.4e80p-47,
253 0x1.0fb66affed2f0p+0, -0x1.d300p-47,
254 0x1.1073028d7234bp+0, 0x1.1500p-48,
255 0x1.11301d0125b5bp+0, 0x1.c000p-49,
256 0x1.11edbab5e2af9p+0, 0x1.6bc0p-46,
257 0x1.12abdc06c31d5p+0, 0x1.8400p-49,
258 0x1.136a814f2047dp+0, -0x1.ed00p-47,
259 0x1.1429aaea92de9p+0, 0x1.8e00p-49,
260 0x1.14e95934f3138p+0, 0x1.b400p-49,
261 0x1.15a98c8a58e71p+0, 0x1.5300p-47,
262 0x1.166a45471c3dfp+0, 0x1.3380p-47,
263 0x1.172b83c7d5211p+0, 0x1.8d40p-45,
264 0x1.17ed48695bb9fp+0, -0x1.5d00p-47,
265 0x1.18af9388c8d93p+0, -0x1.c880p-46,
266 0x1.1972658375d66p+0, 0x1.1f00p-46,
267 0x1.1a35beb6fcba7p+0, 0x1.0480p-46,
268 0x1.1af99f81387e3p+0, -0x1.7390p-43,
269 0x1.1bbe084045d54p+0, 0x1.4e40p-45,
270 0x1.1c82f95281c43p+0, -0x1.a200p-47,
271 0x1.1d4873168b9b2p+0, 0x1.3800p-49,
272 0x1.1e0e75eb44031p+0, 0x1.ac00p-49,
273 0x1.1ed5022fcd938p+0, 0x1.1900p-47,
274 0x1.1f9c18438cdf7p+0, -0x1.b780p-46,
275 0x1.2063b88628d8fp+0, 0x1.d940p-45,
276 0x1.212be3578a81ep+0, 0x1.8000p-50,
277 0x1.21f49917ddd41p+0, 0x1.b340p-45,
278 0x1.22bdda2791323p+0, 0x1.9f80p-46,
279 0x1.2387a6e7561e7p+0, -0x1.9c80p-46,
280 0x1.2451ffb821427p+0, 0x1.2300p-47,
281 0x1.251ce4fb2a602p+0, -0x1.3480p-46,
282 0x1.25e85711eceb0p+0, 0x1.2700p-46,
283 0x1.26b4565e27d16p+0, 0x1.1d00p-46,
284 0x1.2780e341de00fp+0, 0x1.1ee0p-44,
285 0x1.284dfe1f5633ep+0, -0x1.4c00p-46,
286 0x1.291ba7591bb30p+0, -0x1.3d80p-46,
287 0x1.29e9df51fdf09p+0, 0x1.8b00p-47,
288 0x1.2ab8a66d10e9bp+0, -0x1.27c0p-45,
289 0x1.2b87fd0dada3ap+0, 0x1.a340p-45,
290 0x1.2c57e39771af9p+0, -0x1.0800p-46,
291 0x1.2d285a6e402d9p+0, -0x1.ed00p-47,
292 0x1.2df961f641579p+0, -0x1.4200p-48,
293 0x1.2ecafa93e2ecfp+0, -0x1.4980p-45,
294 0x1.2f9d24abd8822p+0, -0x1.6300p-46,
295 0x1.306fe0a31b625p+0, -0x1.2360p-44,
296 0x1.31432edeea50bp+0, -0x1.0df8p-40,
297 0x1.32170fc4cd7b8p+0, -0x1.2480p-45,
298 0x1.32eb83ba8e9a2p+0, -0x1.5980p-45,
299 0x1.33c08b2641766p+0, 0x1.ed00p-46,
300 0x1.3496266e3fa27p+0, -0x1.c000p-50,
301 0x1.356c55f929f0fp+0, -0x1.0d80p-44,
302 0x1.36431a2de88b9p+0, 0x1.2c80p-45,
303 0x1.371a7373aaa39p+0, 0x1.0600p-45,
304 0x1.37f26231e74fep+0, -0x1.6600p-46,
305 0x1.38cae6d05d838p+0, -0x1.ae00p-47,
306 0x1.39a401b713ec3p+0, -0x1.4720p-43,
307 0x1.3a7db34e5a020p+0, 0x1.8200p-47,
308 0x1.3b57fbfec6e95p+0, 0x1.e800p-44,
309 0x1.3c32dc313a8f2p+0, 0x1.f800p-49,
310 0x1.3d0e544ede122p+0, -0x1.7a00p-46,
311 0x1.3dea64c1234bbp+0, 0x1.6300p-45,
312 0x1.3ec70df1c4eccp+0, -0x1.8a60p-43,
313 0x1.3fa4504ac7e8cp+0, -0x1.cdc0p-44,
314 0x1.40822c367a0bbp+0, 0x1.5b80p-45,
315 0x1.4160a21f72e95p+0, 0x1.ec00p-46,
316 0x1.423fb27094646p+0, -0x1.3600p-46,
317 0x1.431f5d950a920p+0, 0x1.3980p-45,
318 0x1.43ffa3f84b9ebp+0, 0x1.a000p-48,
319 0x1.44e0860618919p+0, -0x1.6c00p-48,
320 0x1.45c2042a7d201p+0, -0x1.bc00p-47,
321 0x1.46a41ed1d0016p+0, -0x1.2800p-46,
322 0x1.4786d668b3326p+0, 0x1.0e00p-44,
323 0x1.486a2b5c13c00p+0, -0x1.d400p-45,
324 0x1.494e1e192af04p+0, 0x1.c200p-47,
325 0x1.4a32af0d7d372p+0, -0x1.e500p-46,
326 0x1.4b17dea6db801p+0, 0x1.7800p-47,
327 0x1.4bfdad53629e1p+0, -0x1.3800p-46,
328 0x1.4ce41b817c132p+0, 0x1.0800p-47,
329 0x1.4dcb299fddddbp+0, 0x1.c700p-45,
330 0x1.4eb2d81d8ab96p+0, -0x1.ce00p-46,
331 0x1.4f9b2769d2d02p+0, 0x1.9200p-46,
332 0x1.508417f4531c1p+0, -0x1.8c00p-47,
333 0x1.516daa2cf662ap+0, -0x1.a000p-48,
334 0x1.5257de83f51eap+0, 0x1.a080p-43,
335 0x1.5342b569d4edap+0, -0x1.6d80p-45,
336 0x1.542e2f4f6ac1ap+0, -0x1.2440p-44,
337 0x1.551a4ca5d94dbp+0, 0x1.83c0p-43,
338 0x1.56070dde9116bp+0, 0x1.4b00p-45,
339 0x1.56f4736b529dep+0, 0x1.15a0p-43,
340 0x1.57e27dbe2c40ep+0, -0x1.9e00p-45,
341 0x1.58d12d497c76fp+0, -0x1.3080p-45,
342 0x1.59c0827ff0b4cp+0, 0x1.dec0p-43,
343 0x1.5ab07dd485427p+0, -0x1.4000p-51,
344 0x1.5ba11fba87af4p+0, 0x1.0080p-44,
345 0x1.5c9268a59460bp+0, -0x1.6c80p-45,
346 0x1.5d84590998e3fp+0, 0x1.69a0p-43,
347 0x1.5e76f15ad20e1p+0, -0x1.b400p-46,
348 0x1.5f6a320dcebcap+0, 0x1.7700p-46,
349 0x1.605e1b976dcb8p+0, 0x1.6f80p-45,
350 0x1.6152ae6cdf715p+0, 0x1.1000p-47,
351 0x1.6247eb03a5531p+0, -0x1.5d00p-46,
352 0x1.633dd1d1929b5p+0, -0x1.2d00p-46,
353 0x1.6434634ccc313p+0, -0x1.a800p-49,
354 0x1.652b9febc8efap+0, -0x1.8600p-45,
355 0x1.6623882553397p+0, 0x1.1fe0p-40,
356 0x1.671c1c708328ep+0, -0x1.7200p-44,
357 0x1.68155d44ca97ep+0, 0x1.6800p-49,
358 0x1.690f4b19e9471p+0, -0x1.9780p-45,
359};
360
361fn exp2_64(x: f64) f64 {
362 const tblsiz: u32 = @intCast(u32, exp2dt.len / 2);
363 const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz);
364 const P1: f64 = 0x1.62e42fefa39efp-1;
365 const P2: f64 = 0x1.ebfbdff82c575p-3;
366 const P3: f64 = 0x1.c6b08d704a0a6p-5;
367 const P4: f64 = 0x1.3b2ab88f70400p-7;
368 const P5: f64 = 0x1.5d88003875c74p-10;
369
370 const ux = @bitCast(u64, x);
371 const ix = @intCast(u32, ux >> 32) & 0x7FFFFFFF;
372
373 // TODO: This should be handled beneath.
374 if (math.isNan(x)) {
375 return math.nan(f64);
376 }
377
378 // |x| >= 1022 or nan
379 if (ix >= 0x408FF000) {
380 // x >= 1024 or nan
381 if (ix >= 0x40900000 and ux >> 63 == 0) {
382 math.raiseOverflow();
383 return math.inf(f64);
384 }
385 // -inf or -nan
386 if (ix >= 0x7FF00000) {
387 return -1 / x;
388 }
389 // x <= -1022
390 if (ux >> 63 != 0) {
391 // underflow
392 if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) {
393 math.doNotOptimizeAway(@floatCast(f32, -0x1.0p-149 / x));
394 }
395 if (x <= -1075) {
396 return 0;
397 }
398 }
399 }
400 // |x| < 0x1p-54
401 else if (ix < 0x3C900000) {
402 return 1.0 + x;
403 }
404
405 // NOTE: musl relies on unsafe behaviours which are replicated below
406 // (addition overflow, division truncation, casting). Appears that this
407 // produces the intended result but should confirm how GCC/Clang handle this
408 // to ensure.
409
410 // reduce x
411 var uf: f64 = x + redux;
412 // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here
413 var i_0: u32 = @truncate(u32, @bitCast(u64, uf));
414 i_0 +%= tblsiz / 2;
415
416 const k: u32 = i_0 / tblsiz * tblsiz;
417 const ik: i32 = @divTrunc(@bitCast(i32, k), tblsiz);
418 i_0 %= tblsiz;
419 uf -= redux;
420
421 // r = exp2(y) = exp2t[i_0] * p(z - eps[i])
422 var z: f64 = x - uf;
423 const t: f64 = exp2dt[@intCast(usize, 2 * i_0)];
424 z -= exp2dt[@intCast(usize, 2 * i_0 + 1)];
425 const r: f64 = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5))));
426
427 return math.scalbn(r, ik);
428}
429
430test "math.exp2" {
431 try expect(exp2(@as(f32, 0.8923)) == exp2_32(0.8923));
432 try expect(exp2(@as(f64, 0.8923)) == exp2_64(0.8923));
433}
434
435test "math.exp2_32" {
436 const epsilon = 0.000001;
437
438 try expect(exp2_32(0.0) == 1.0);
439 try expect(math.approxEqAbs(f32, exp2_32(0.2), 1.148698, epsilon));
440 try expect(math.approxEqAbs(f32, exp2_32(0.8923), 1.856133, epsilon));
441 try expect(math.approxEqAbs(f32, exp2_32(1.5), 2.828427, epsilon));
442 try expect(math.approxEqAbs(f32, exp2_32(37.45), 187747237888, epsilon));
443 try expect(math.approxEqAbs(f32, exp2_32(-1), 0.5, epsilon));
444}
445
446test "math.exp2_64" {
447 const epsilon = 0.000001;
448
449 try expect(exp2_64(0.0) == 1.0);
450 try expect(math.approxEqAbs(f64, exp2_64(0.2), 1.148698, epsilon));
451 try expect(math.approxEqAbs(f64, exp2_64(0.8923), 1.856133, epsilon));
452 try expect(math.approxEqAbs(f64, exp2_64(1.5), 2.828427, epsilon));
453 try expect(math.approxEqAbs(f64, exp2_64(-1), 0.5, epsilon));
454 try expect(math.approxEqAbs(f64, exp2_64(-0x1.a05cc754481d1p-2), 0x1.824056efc687cp-1, epsilon));
455}
456
457test "math.exp2_32.special" {
458 try expect(math.isPositiveInf(exp2_32(math.inf(f32))));
459 try expect(math.isNan(exp2_32(math.nan(f32))));
460}
461
462test "math.exp2_64.special" {
463 try expect(math.isPositiveInf(exp2_64(math.inf(f64))));
464 try expect(math.isNan(exp2_64(math.nan(f64))));
465}
lib/std/math/expo2.zig+2-2
......@@ -22,7 +22,7 @@ fn expo2f(x: f32) f32 {
2222
2323 const u = (0x7F + k / 2) << 23;
2424 const scale = @bitCast(f32, u);
25 return math.exp(x - kln2) * scale * scale;
25 return @exp(x - kln2) * scale * scale;
2626}
2727
2828fn expo2d(x: f64) f64 {
......@@ -31,5 +31,5 @@ fn expo2d(x: f64) f64 {
3131
3232 const u = (0x3FF + k / 2) << 20;
3333 const scale = @bitCast(f64, @as(u64, u) << 32);
34 return math.exp(x - kln2) * scale * scale;
34 return @exp(x - kln2) * scale * scale;
3535}
lib/std/math/fabs.zig deleted-45
......@@ -1,45 +0,0 @@
1const std = @import("../std.zig");
2const math = std.math;
3const expect = std.testing.expect;
4
5/// Returns the absolute value of x.
6///
7/// Special Cases:
8/// - fabs(+-inf) = +inf
9/// - fabs(nan) = nan
10pub fn fabs(x: anytype) @TypeOf(x) {
11 const T = @TypeOf(x);
12 const TBits = std.meta.Int(.unsigned, @bitSizeOf(T));
13 if (@typeInfo(T) != .Float) {
14 @compileError("fabs not implemented for " ++ @typeName(T));
15 }
16
17 const float_bits = @bitCast(TBits, x);
18 const remove_sign = ~@as(TBits, 0) >> 1;
19
20 return @bitCast(T, float_bits & remove_sign);
21}
22
23test "math.fabs" {
24 // TODO add support for c_longdouble here
25 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
26 // normals
27 try expect(fabs(@as(T, 1.0)) == 1.0);
28 try expect(fabs(@as(T, -1.0)) == 1.0);
29 try expect(fabs(math.floatMin(T)) == math.floatMin(T));
30 try expect(fabs(-math.floatMin(T)) == math.floatMin(T));
31 try expect(fabs(math.floatMax(T)) == math.floatMax(T));
32 try expect(fabs(-math.floatMax(T)) == math.floatMax(T));
33
34 // subnormals
35 try expect(fabs(@as(T, 0.0)) == 0.0);
36 try expect(fabs(@as(T, -0.0)) == 0.0);
37 try expect(fabs(math.floatTrueMin(T)) == math.floatTrueMin(T));
38 try expect(fabs(-math.floatTrueMin(T)) == math.floatTrueMin(T));
39
40 // non-finite numbers
41 try expect(math.isPositiveInf(fabs(math.inf(T))));
42 try expect(math.isPositiveInf(fabs(-math.inf(T))));
43 try expect(math.isNan(fabs(math.nan(T))));
44 }
45}
lib/std/math/floor.zig deleted-221
......@@ -1,221 +0,0 @@
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/floorf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c
6
7const expect = std.testing.expect;
8const std = @import("../std.zig");
9const math = std.math;
10
11/// Returns the greatest integer value less than or equal to x.
12///
13/// Special Cases:
14/// - floor(+-0) = +-0
15/// - floor(+-inf) = +-inf
16/// - floor(nan) = nan
17pub fn floor(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);
19 return switch (T) {
20 f16 => floor16(x),
21 f32 => floor32(x),
22 f64 => floor64(x),
23 f128 => floor128(x),
24
25 // TODO this is not correct for some targets
26 c_longdouble => @floatCast(c_longdouble, floor128(x)),
27
28 else => @compileError("floor not implemented for " ++ @typeName(T)),
29 };
30}
31
32fn floor16(x: f16) f16 {
33 var u = @bitCast(u16, x);
34 const e = @intCast(i16, (u >> 10) & 31) - 15;
35 var m: u16 = undefined;
36
37 // TODO: Shouldn't need this explicit check.
38 if (x == 0.0) {
39 return x;
40 }
41
42 if (e >= 10) {
43 return x;
44 }
45
46 if (e >= 0) {
47 m = @as(u16, 1023) >> @intCast(u4, e);
48 if (u & m == 0) {
49 return x;
50 }
51 math.doNotOptimizeAway(x + 0x1.0p120);
52 if (u >> 15 != 0) {
53 u += m;
54 }
55 return @bitCast(f16, u & ~m);
56 } else {
57 math.doNotOptimizeAway(x + 0x1.0p120);
58 if (u >> 15 == 0) {
59 return 0.0;
60 } else {
61 return -1.0;
62 }
63 }
64}
65
66fn floor32(x: f32) f32 {
67 var u = @bitCast(u32, x);
68 const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F;
69 var m: u32 = undefined;
70
71 // TODO: Shouldn't need this explicit check.
72 if (x == 0.0) {
73 return x;
74 }
75
76 if (e >= 23) {
77 return x;
78 }
79
80 if (e >= 0) {
81 m = @as(u32, 0x007FFFFF) >> @intCast(u5, e);
82 if (u & m == 0) {
83 return x;
84 }
85 math.doNotOptimizeAway(x + 0x1.0p120);
86 if (u >> 31 != 0) {
87 u += m;
88 }
89 return @bitCast(f32, u & ~m);
90 } else {
91 math.doNotOptimizeAway(x + 0x1.0p120);
92 if (u >> 31 == 0) {
93 return 0.0;
94 } else {
95 return -1.0;
96 }
97 }
98}
99
100fn floor64(x: f64) f64 {
101 const f64_toint = 1.0 / math.floatEps(f64);
102
103 const u = @bitCast(u64, x);
104 const e = (u >> 52) & 0x7FF;
105 var y: f64 = undefined;
106
107 if (e >= 0x3FF + 52 or x == 0) {
108 return x;
109 }
110
111 if (u >> 63 != 0) {
112 y = x - f64_toint + f64_toint - x;
113 } else {
114 y = x + f64_toint - f64_toint - x;
115 }
116
117 if (e <= 0x3FF - 1) {
118 math.doNotOptimizeAway(y);
119 if (u >> 63 != 0) {
120 return -1.0;
121 } else {
122 return 0.0;
123 }
124 } else if (y > 0) {
125 return x + y - 1;
126 } else {
127 return x + y;
128 }
129}
130
131fn floor128(x: f128) f128 {
132 const f128_toint = 1.0 / math.floatEps(f128);
133
134 const u = @bitCast(u128, x);
135 const e = (u >> 112) & 0x7FFF;
136 var y: f128 = undefined;
137
138 if (e >= 0x3FFF + 112 or x == 0) return x;
139
140 if (u >> 127 != 0) {
141 y = x - f128_toint + f128_toint - x;
142 } else {
143 y = x + f128_toint - f128_toint - x;
144 }
145
146 if (e <= 0x3FFF - 1) {
147 math.doNotOptimizeAway(y);
148 if (u >> 127 != 0) {
149 return -1.0;
150 } else {
151 return 0.0;
152 }
153 } else if (y > 0) {
154 return x + y - 1;
155 } else {
156 return x + y;
157 }
158}
159
160test "math.floor" {
161 try expect(floor(@as(f16, 1.3)) == floor16(1.3));
162 try expect(floor(@as(f32, 1.3)) == floor32(1.3));
163 try expect(floor(@as(f64, 1.3)) == floor64(1.3));
164 try expect(floor(@as(f128, 1.3)) == floor128(1.3));
165}
166
167test "math.floor16" {
168 try expect(floor16(1.3) == 1.0);
169 try expect(floor16(-1.3) == -2.0);
170 try expect(floor16(0.2) == 0.0);
171}
172
173test "math.floor32" {
174 try expect(floor32(1.3) == 1.0);
175 try expect(floor32(-1.3) == -2.0);
176 try expect(floor32(0.2) == 0.0);
177}
178
179test "math.floor64" {
180 try expect(floor64(1.3) == 1.0);
181 try expect(floor64(-1.3) == -2.0);
182 try expect(floor64(0.2) == 0.0);
183}
184
185test "math.floor128" {
186 try expect(floor128(1.3) == 1.0);
187 try expect(floor128(-1.3) == -2.0);
188 try expect(floor128(0.2) == 0.0);
189}
190
191test "math.floor16.special" {
192 try expect(floor16(0.0) == 0.0);
193 try expect(floor16(-0.0) == -0.0);
194 try expect(math.isPositiveInf(floor16(math.inf(f16))));
195 try expect(math.isNegativeInf(floor16(-math.inf(f16))));
196 try expect(math.isNan(floor16(math.nan(f16))));
197}
198
199test "math.floor32.special" {
200 try expect(floor32(0.0) == 0.0);
201 try expect(floor32(-0.0) == -0.0);
202 try expect(math.isPositiveInf(floor32(math.inf(f32))));
203 try expect(math.isNegativeInf(floor32(-math.inf(f32))));
204 try expect(math.isNan(floor32(math.nan(f32))));
205}
206
207test "math.floor64.special" {
208 try expect(floor64(0.0) == 0.0);
209 try expect(floor64(-0.0) == -0.0);
210 try expect(math.isPositiveInf(floor64(math.inf(f64))));
211 try expect(math.isNegativeInf(floor64(-math.inf(f64))));
212 try expect(math.isNan(floor64(math.nan(f64))));
213}
214
215test "math.floor128.special" {
216 try expect(floor128(0.0) == 0.0);
217 try expect(floor128(-0.0) == -0.0);
218 try expect(math.isPositiveInf(floor128(math.inf(f128))));
219 try expect(math.isNegativeInf(floor128(-math.inf(f128))));
220 try expect(math.isNan(floor128(math.nan(f128))));
221}
lib/std/math/fma.zig deleted-339
......@@ -1,339 +0,0 @@
1// Ported from musl, which is MIT licensed:
2// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
3//
4// https://git.musl-libc.org/cgit/musl/tree/src/math/fmal.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c
6// https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c
7
8const std = @import("../std.zig");
9const math = std.math;
10const expect = std.testing.expect;
11
12/// Returns x * y + z with a single rounding error.
13pub fn fma(comptime T: type, x: T, y: T, z: T) T {
14 return switch (T) {
15 f32 => fma32(x, y, z),
16 f64 => fma64(x, y, z),
17 f128 => fma128(x, y, z),
18
19 // TODO this is not correct for some targets
20 c_longdouble => @floatCast(c_longdouble, fma128(x, y, z)),
21
22 f80 => @floatCast(f80, fma128(x, y, z)),
23
24 else => @compileError("fma not implemented for " ++ @typeName(T)),
25 };
26}
27
28fn fma32(x: f32, y: f32, z: f32) f32 {
29 const xy = @as(f64, x) * y;
30 const xy_z = xy + z;
31 const u = @bitCast(u64, xy_z);
32 const e = (u >> 52) & 0x7FF;
33
34 if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or (xy_z - xy == z and xy_z - z == xy)) {
35 return @floatCast(f32, xy_z);
36 } else {
37 // TODO: Handle inexact case with double-rounding
38 return @floatCast(f32, xy_z);
39 }
40}
41
42// NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately.
43fn fma64(x: f64, y: f64, z: f64) f64 {
44 if (!math.isFinite(x) or !math.isFinite(y)) {
45 return x * y + z;
46 }
47 if (!math.isFinite(z)) {
48 return z;
49 }
50 if (x == 0.0 or y == 0.0) {
51 return x * y + z;
52 }
53 if (z == 0.0) {
54 return x * y;
55 }
56
57 const x1 = math.frexp(x);
58 var ex = x1.exponent;
59 var xs = x1.significand;
60 const x2 = math.frexp(y);
61 var ey = x2.exponent;
62 var ys = x2.significand;
63 const x3 = math.frexp(z);
64 var ez = x3.exponent;
65 var zs = x3.significand;
66
67 var spread = ex + ey - ez;
68 if (spread <= 53 * 2) {
69 zs = math.scalbn(zs, -spread);
70 } else {
71 zs = math.copysign(f64, math.floatMin(f64), zs);
72 }
73
74 const xy = dd_mul(xs, ys);
75 const r = dd_add(xy.hi, zs);
76 spread = ex + ey;
77
78 if (r.hi == 0.0) {
79 return xy.hi + zs + math.scalbn(xy.lo, spread);
80 }
81
82 const adj = add_adjusted(r.lo, xy.lo);
83 if (spread + math.ilogb(r.hi) > -1023) {
84 return math.scalbn(r.hi + adj, spread);
85 } else {
86 return add_and_denorm(r.hi, adj, spread);
87 }
88}
89
90const dd = struct {
91 hi: f64,
92 lo: f64,
93};
94
95fn dd_add(a: f64, b: f64) dd {
96 var ret: dd = undefined;
97 ret.hi = a + b;
98 const s = ret.hi - a;
99 ret.lo = (a - (ret.hi - s)) + (b - s);
100 return ret;
101}
102
103fn dd_mul(a: f64, b: f64) dd {
104 var ret: dd = undefined;
105 const split: f64 = 0x1.0p27 + 1.0;
106
107 var p = a * split;
108 var ha = a - p;
109 ha += p;
110 var la = a - ha;
111
112 p = b * split;
113 var hb = b - p;
114 hb += p;
115 var lb = b - hb;
116
117 p = ha * hb;
118 var q = ha * lb + la * hb;
119
120 ret.hi = p + q;
121 ret.lo = p - ret.hi + q + la * lb;
122 return ret;
123}
124
125fn add_adjusted(a: f64, b: f64) f64 {
126 var sum = dd_add(a, b);
127 if (sum.lo != 0) {
128 var uhii = @bitCast(u64, sum.hi);
129 if (uhii & 1 == 0) {
130 // hibits += copysign(1.0, sum.hi, sum.lo)
131 const uloi = @bitCast(u64, sum.lo);
132 uhii += 1 - ((uhii ^ uloi) >> 62);
133 sum.hi = @bitCast(f64, uhii);
134 }
135 }
136 return sum.hi;
137}
138
139fn add_and_denorm(a: f64, b: f64, scale: i32) f64 {
140 var sum = dd_add(a, b);
141 if (sum.lo != 0) {
142 var uhii = @bitCast(u64, sum.hi);
143 const bits_lost = -@intCast(i32, (uhii >> 52) & 0x7FF) - scale + 1;
144 if ((bits_lost != 1) == (uhii & 1 != 0)) {
145 const uloi = @bitCast(u64, sum.lo);
146 uhii += 1 - (((uhii ^ uloi) >> 62) & 2);
147 sum.hi = @bitCast(f64, uhii);
148 }
149 }
150 return math.scalbn(sum.hi, scale);
151}
152
153/// A struct that represents a floating-point number with twice the precision
154/// of f128. We maintain the invariant that "hi" stores the high-order
155/// bits of the result.
156const dd128 = struct {
157 hi: f128,
158 lo: f128,
159};
160
161/// Compute a+b exactly, returning the exact result in a struct dd. We assume
162/// that both a and b are finite, but make no assumptions about their relative
163/// magnitudes.
164fn dd_add128(a: f128, b: f128) dd128 {
165 var ret: dd128 = undefined;
166 ret.hi = a + b;
167 const s = ret.hi - a;
168 ret.lo = (a - (ret.hi - s)) + (b - s);
169 return ret;
170}
171
172/// Compute a+b, with a small tweak: The least significant bit of the
173/// result is adjusted into a sticky bit summarizing all the bits that
174/// were lost to rounding. This adjustment negates the effects of double
175/// rounding when the result is added to another number with a higher
176/// exponent. For an explanation of round and sticky bits, see any reference
177/// on FPU design, e.g.,
178///
179/// J. Coonen. An Implementation Guide to a Proposed Standard for
180/// Floating-Point Arithmetic. Computer, vol. 13, no. 1, Jan 1980.
181fn add_adjusted128(a: f128, b: f128) f128 {
182 var sum = dd_add128(a, b);
183 if (sum.lo != 0) {
184 var uhii = @bitCast(u128, sum.hi);
185 if (uhii & 1 == 0) {
186 // hibits += copysign(1.0, sum.hi, sum.lo)
187 const uloi = @bitCast(u128, sum.lo);
188 uhii += 1 - ((uhii ^ uloi) >> 126);
189 sum.hi = @bitCast(f128, uhii);
190 }
191 }
192 return sum.hi;
193}
194
195/// Compute ldexp(a+b, scale) with a single rounding error. It is assumed
196/// that the result will be subnormal, and care is taken to ensure that
197/// double rounding does not occur.
198fn add_and_denorm128(a: f128, b: f128, scale: i32) f128 {
199 var sum = dd_add128(a, b);
200 // If we are losing at least two bits of accuracy to denormalization,
201 // then the first lost bit becomes a round bit, and we adjust the
202 // lowest bit of sum.hi to make it a sticky bit summarizing all the
203 // bits in sum.lo. With the sticky bit adjusted, the hardware will
204 // break any ties in the correct direction.
205 //
206 // If we are losing only one bit to denormalization, however, we must
207 // break the ties manually.
208 if (sum.lo != 0) {
209 var uhii = @bitCast(u128, sum.hi);
210 const bits_lost = -@intCast(i32, (uhii >> 112) & 0x7FFF) - scale + 1;
211 if ((bits_lost != 1) == (uhii & 1 != 0)) {
212 const uloi = @bitCast(u128, sum.lo);
213 uhii += 1 - (((uhii ^ uloi) >> 126) & 2);
214 sum.hi = @bitCast(f128, uhii);
215 }
216 }
217 return math.scalbn(sum.hi, scale);
218}
219
220/// Compute a*b exactly, returning the exact result in a struct dd. We assume
221/// that both a and b are normalized, so no underflow or overflow will occur.
222/// The current rounding mode must be round-to-nearest.
223fn dd_mul128(a: f128, b: f128) dd128 {
224 var ret: dd128 = undefined;
225 const split: f128 = 0x1.0p57 + 1.0;
226
227 var p = a * split;
228 var ha = a - p;
229 ha += p;
230 var la = a - ha;
231
232 p = b * split;
233 var hb = b - p;
234 hb += p;
235 var lb = b - hb;
236
237 p = ha * hb;
238 var q = ha * lb + la * hb;
239
240 ret.hi = p + q;
241 ret.lo = p - ret.hi + q + la * lb;
242 return ret;
243}
244
245/// Fused multiply-add: Compute x * y + z with a single rounding error.
246///
247/// We use scaling to avoid overflow/underflow, along with the
248/// canonical precision-doubling technique adapted from:
249///
250/// Dekker, T. A Floating-Point Technique for Extending the
251/// Available Precision. Numer. Math. 18, 224-242 (1971).
252fn fma128(x: f128, y: f128, z: f128) f128 {
253 if (!math.isFinite(x) or !math.isFinite(y)) {
254 return x * y + z;
255 }
256 if (!math.isFinite(z)) {
257 return z;
258 }
259 if (x == 0.0 or y == 0.0) {
260 return x * y + z;
261 }
262 if (z == 0.0) {
263 return x * y;
264 }
265
266 const x1 = math.frexp(x);
267 var ex = x1.exponent;
268 var xs = x1.significand;
269 const x2 = math.frexp(y);
270 var ey = x2.exponent;
271 var ys = x2.significand;
272 const x3 = math.frexp(z);
273 var ez = x3.exponent;
274 var zs = x3.significand;
275
276 var spread = ex + ey - ez;
277 if (spread <= 113 * 2) {
278 zs = math.scalbn(zs, -spread);
279 } else {
280 zs = math.copysign(f128, math.floatMin(f128), zs);
281 }
282
283 const xy = dd_mul128(xs, ys);
284 const r = dd_add128(xy.hi, zs);
285 spread = ex + ey;
286
287 if (r.hi == 0.0) {
288 return xy.hi + zs + math.scalbn(xy.lo, spread);
289 }
290
291 const adj = add_adjusted128(r.lo, xy.lo);
292 if (spread + math.ilogb(r.hi) > -16383) {
293 return math.scalbn(r.hi + adj, spread);
294 } else {
295 return add_and_denorm128(r.hi, adj, spread);
296 }
297}
298
299test "type dispatch" {
300 try expect(fma(f32, 0.0, 1.0, 1.0) == fma32(0.0, 1.0, 1.0));
301 try expect(fma(f64, 0.0, 1.0, 1.0) == fma64(0.0, 1.0, 1.0));
302 try expect(fma(f128, 0.0, 1.0, 1.0) == fma128(0.0, 1.0, 1.0));
303}
304
305test "32" {
306 const epsilon = 0.000001;
307
308 try expect(math.approxEqAbs(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon));
309 try expect(math.approxEqAbs(f32, fma32(0.2, 5.0, 9.124), 10.124, epsilon));
310 try expect(math.approxEqAbs(f32, fma32(0.8923, 5.0, 9.124), 13.5855, epsilon));
311 try expect(math.approxEqAbs(f32, fma32(1.5, 5.0, 9.124), 16.624, epsilon));
312 try expect(math.approxEqAbs(f32, fma32(37.45, 5.0, 9.124), 196.374004, epsilon));
313 try expect(math.approxEqAbs(f32, fma32(89.123, 5.0, 9.124), 454.739005, epsilon));
314 try expect(math.approxEqAbs(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
315}
316
317test "64" {
318 const epsilon = 0.000001;
319
320 try expect(math.approxEqAbs(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon));
321 try expect(math.approxEqAbs(f64, fma64(0.2, 5.0, 9.124), 10.124, epsilon));
322 try expect(math.approxEqAbs(f64, fma64(0.8923, 5.0, 9.124), 13.5855, epsilon));
323 try expect(math.approxEqAbs(f64, fma64(1.5, 5.0, 9.124), 16.624, epsilon));
324 try expect(math.approxEqAbs(f64, fma64(37.45, 5.0, 9.124), 196.374, epsilon));
325 try expect(math.approxEqAbs(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon));
326 try expect(math.approxEqAbs(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
327}
328
329test "128" {
330 const epsilon = 0.000001;
331
332 try expect(math.approxEqAbs(f128, fma128(0.0, 5.0, 9.124), 9.124, epsilon));
333 try expect(math.approxEqAbs(f128, fma128(0.2, 5.0, 9.124), 10.124, epsilon));
334 try expect(math.approxEqAbs(f128, fma128(0.8923, 5.0, 9.124), 13.5855, epsilon));
335 try expect(math.approxEqAbs(f128, fma128(1.5, 5.0, 9.124), 16.624, epsilon));
336 try expect(math.approxEqAbs(f128, fma128(37.45, 5.0, 9.124), 196.374, epsilon));
337 try expect(math.approxEqAbs(f128, fma128(89.123, 5.0, 9.124), 454.739, epsilon));
338 try expect(math.approxEqAbs(f128, fma128(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
339}
lib/std/math/hypot.zig+2-2
......@@ -56,7 +56,7 @@ fn hypot32(x: f32, y: f32) f32 {
5656 yy *= 0x1.0p-90;
5757 }
5858
59 return z * math.sqrt(@floatCast(f32, @as(f64, x) * x + @as(f64, y) * y));
59 return z * @sqrt(@floatCast(f32, @as(f64, x) * x + @as(f64, y) * y));
6060}
6161
6262fn sq(hi: *f64, lo: *f64, x: f64) void {
......@@ -117,7 +117,7 @@ fn hypot64(x: f64, y: f64) f64 {
117117 sq(&hx, &lx, x);
118118 sq(&hy, &ly, y);
119119
120 return z * math.sqrt(ly + lx + hy + hx);
120 return z * @sqrt(ly + lx + hy + hx);
121121}
122122
123123test "math.hypot" {
lib/std/math/ln.zig+8-163
......@@ -1,12 +1,6 @@
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/lnf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c
6
71const std = @import("../std.zig");
82const math = std.math;
9const expect = std.testing.expect;
3const testing = std.testing;
104
115/// Returns the natural logarithm of x.
126///
......@@ -15,175 +9,26 @@ const expect = std.testing.expect;
159/// - ln(0) = -inf
1610/// - ln(x) = nan if x < 0
1711/// - ln(nan) = nan
12/// TODO remove this in favor of `@log`.
1813pub fn ln(x: anytype) @TypeOf(x) {
1914 const T = @TypeOf(x);
2015 switch (@typeInfo(T)) {
2116 .ComptimeFloat => {
22 return @as(comptime_float, ln_64(x));
23 },
24 .Float => {
25 return switch (T) {
26 f32 => ln_32(x),
27 f64 => ln_64(x),
28 else => @compileError("ln not implemented for " ++ @typeName(T)),
29 };
17 return @as(comptime_float, @log(x));
3018 },
19 .Float => return @log(x),
3120 .ComptimeInt => {
32 return @as(comptime_int, math.floor(ln_64(@as(f64, x))));
21 return @as(comptime_int, @floor(@log(@as(f64, x))));
3322 },
3423 .Int => |IntType| switch (IntType.signedness) {
3524 .signed => @compileError("ln not implemented for signed integers"),
36 .unsigned => return @as(T, math.floor(ln_64(@as(f64, x)))),
25 .unsigned => return @as(T, @floor(@log(@as(f64, x)))),
3726 },
3827 else => @compileError("ln not implemented for " ++ @typeName(T)),
3928 }
4029}
4130
42pub fn ln_32(x_: f32) f32 {
43 const ln2_hi: f32 = 6.9313812256e-01;
44 const ln2_lo: f32 = 9.0580006145e-06;
45 const Lg1: f32 = 0xaaaaaa.0p-24;
46 const Lg2: f32 = 0xccce13.0p-25;
47 const Lg3: f32 = 0x91e9ee.0p-25;
48 const Lg4: f32 = 0xf89e26.0p-26;
49
50 var x = x_;
51 var ix = @bitCast(u32, x);
52 var k: i32 = 0;
53
54 // x < 2^(-126)
55 if (ix < 0x00800000 or ix >> 31 != 0) {
56 // log(+-0) = -inf
57 if (ix << 1 == 0) {
58 return -math.inf(f32);
59 }
60 // log(-#) = nan
61 if (ix >> 31 != 0) {
62 return math.nan(f32);
63 }
64
65 // subnormal, scale x
66 k -= 25;
67 x *= 0x1.0p25;
68 ix = @bitCast(u32, x);
69 } else if (ix >= 0x7F800000) {
70 return x;
71 } else if (ix == 0x3F800000) {
72 return 0;
73 }
74
75 // x into [sqrt(2) / 2, sqrt(2)]
76 ix += 0x3F800000 - 0x3F3504F3;
77 k += @intCast(i32, ix >> 23) - 0x7F;
78 ix = (ix & 0x007FFFFF) + 0x3F3504F3;
79 x = @bitCast(f32, ix);
80
81 const f = x - 1.0;
82 const s = f / (2.0 + f);
83 const z = s * s;
84 const w = z * z;
85 const t1 = w * (Lg2 + w * Lg4);
86 const t2 = z * (Lg1 + w * Lg3);
87 const R = t2 + t1;
88 const hfsq = 0.5 * f * f;
89 const dk = @intToFloat(f32, k);
90
91 return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
92}
93
94pub fn ln_64(x_: f64) f64 {
95 const ln2_hi: f64 = 6.93147180369123816490e-01;
96 const ln2_lo: f64 = 1.90821492927058770002e-10;
97 const Lg1: f64 = 6.666666666666735130e-01;
98 const Lg2: f64 = 3.999999999940941908e-01;
99 const Lg3: f64 = 2.857142874366239149e-01;
100 const Lg4: f64 = 2.222219843214978396e-01;
101 const Lg5: f64 = 1.818357216161805012e-01;
102 const Lg6: f64 = 1.531383769920937332e-01;
103 const Lg7: f64 = 1.479819860511658591e-01;
104
105 var x = x_;
106 var ix = @bitCast(u64, x);
107 var hx = @intCast(u32, ix >> 32);
108 var k: i32 = 0;
109
110 if (hx < 0x00100000 or hx >> 31 != 0) {
111 // log(+-0) = -inf
112 if (ix << 1 == 0) {
113 return -math.inf(f64);
114 }
115 // log(-#) = nan
116 if (hx >> 31 != 0) {
117 return math.nan(f64);
118 }
119
120 // subnormal, scale x
121 k -= 54;
122 x *= 0x1.0p54;
123 hx = @intCast(u32, @bitCast(u64, ix) >> 32);
124 } else if (hx >= 0x7FF00000) {
125 return x;
126 } else if (hx == 0x3FF00000 and ix << 32 == 0) {
127 return 0;
128 }
129
130 // x into [sqrt(2) / 2, sqrt(2)]
131 hx += 0x3FF00000 - 0x3FE6A09E;
132 k += @intCast(i32, hx >> 20) - 0x3FF;
133 hx = (hx & 0x000FFFFF) + 0x3FE6A09E;
134 ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF);
135 x = @bitCast(f64, ix);
136
137 const f = x - 1.0;
138 const hfsq = 0.5 * f * f;
139 const s = f / (2.0 + f);
140 const z = s * s;
141 const w = z * z;
142 const t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
143 const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
144 const R = t2 + t1;
145 const dk = @intToFloat(f64, k);
146
147 return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
148}
149
15031test "math.ln" {
151 try expect(ln(@as(f32, 0.2)) == ln_32(0.2));
152 try expect(ln(@as(f64, 0.2)) == ln_64(0.2));
153}
154
155test "math.ln32" {
156 const epsilon = 0.000001;
157
158 try expect(math.approxEqAbs(f32, ln_32(0.2), -1.609438, epsilon));
159 try expect(math.approxEqAbs(f32, ln_32(0.8923), -0.113953, epsilon));
160 try expect(math.approxEqAbs(f32, ln_32(1.5), 0.405465, epsilon));
161 try expect(math.approxEqAbs(f32, ln_32(37.45), 3.623007, epsilon));
162 try expect(math.approxEqAbs(f32, ln_32(89.123), 4.490017, epsilon));
163 try expect(math.approxEqAbs(f32, ln_32(123123.234375), 11.720941, epsilon));
164}
165
166test "math.ln64" {
167 const epsilon = 0.000001;
168
169 try expect(math.approxEqAbs(f64, ln_64(0.2), -1.609438, epsilon));
170 try expect(math.approxEqAbs(f64, ln_64(0.8923), -0.113953, epsilon));
171 try expect(math.approxEqAbs(f64, ln_64(1.5), 0.405465, epsilon));
172 try expect(math.approxEqAbs(f64, ln_64(37.45), 3.623007, epsilon));
173 try expect(math.approxEqAbs(f64, ln_64(89.123), 4.490017, epsilon));
174 try expect(math.approxEqAbs(f64, ln_64(123123.234375), 11.720941, epsilon));
175}
176
177test "math.ln32.special" {
178 try expect(math.isPositiveInf(ln_32(math.inf(f32))));
179 try expect(math.isNegativeInf(ln_32(0.0)));
180 try expect(math.isNan(ln_32(-1.0)));
181 try expect(math.isNan(ln_32(math.nan(f32))));
182}
183
184test "math.ln64.special" {
185 try expect(math.isPositiveInf(ln_64(math.inf(f64))));
186 try expect(math.isNegativeInf(ln_64(0.0)));
187 try expect(math.isNan(ln_64(-1.0)));
188 try expect(math.isNan(ln_64(math.nan(f64))));
32 try testing.expect(ln(@as(f32, 0.2)) == @log(0.2));
33 try testing.expect(ln(@as(f64, 0.2)) == @log(0.2));
18934}
lib/std/math/log.zig+6-6
......@@ -15,28 +15,28 @@ pub fn log(comptime T: type, base: T, x: T) T {
1515 } else if (base == 10) {
1616 return math.log10(x);
1717 } else if ((@typeInfo(T) == .Float or @typeInfo(T) == .ComptimeFloat) and base == math.e) {
18 return math.ln(x);
18 return @log(x);
1919 }
2020
2121 const float_base = math.lossyCast(f64, base);
2222 switch (@typeInfo(T)) {
2323 .ComptimeFloat => {
24 return @as(comptime_float, math.ln(@as(f64, x)) / math.ln(float_base));
24 return @as(comptime_float, @log(@as(f64, x)) / @log(float_base));
2525 },
2626 .ComptimeInt => {
27 return @as(comptime_int, math.floor(math.ln(@as(f64, x)) / math.ln(float_base)));
27 return @as(comptime_int, @floor(@log(@as(f64, x)) / @log(float_base)));
2828 },
2929
3030 // TODO implement integer log without using float math
3131 .Int => |IntType| switch (IntType.signedness) {
3232 .signed => @compileError("log not implemented for signed integers"),
33 .unsigned => return @floatToInt(T, math.floor(math.ln(@intToFloat(f64, x)) / math.ln(float_base))),
33 .unsigned => return @floatToInt(T, @floor(@log(@intToFloat(f64, x)) / @log(float_base))),
3434 },
3535
3636 .Float => {
3737 switch (T) {
38 f32 => return @floatCast(f32, math.ln(@as(f64, x)) / math.ln(float_base)),
39 f64 => return math.ln(x) / math.ln(float_base),
38 f32 => return @floatCast(f32, @log(@as(f64, x)) / @log(float_base)),
39 f64 => return @log(x) / @log(float_base),
4040 else => @compileError("log not implemented for " ++ @typeName(T)),
4141 }
4242 },
lib/std/math/log10.zig+4-192
......@@ -1,9 +1,3 @@
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/log10f.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c
6
71const std = @import("../std.zig");
82const math = std.math;
93const testing = std.testing;
......@@ -20,198 +14,16 @@ pub fn log10(x: anytype) @TypeOf(x) {
2014 const T = @TypeOf(x);
2115 switch (@typeInfo(T)) {
2216 .ComptimeFloat => {
23 return @as(comptime_float, log10_64(x));
24 },
25 .Float => {
26 return switch (T) {
27 f32 => log10_32(x),
28 f64 => log10_64(x),
29 else => @compileError("log10 not implemented for " ++ @typeName(T)),
30 };
17 return @as(comptime_float, @log10(x));
3118 },
19 .Float => return @log10(x),
3220 .ComptimeInt => {
33 return @as(comptime_int, math.floor(log10_64(@as(f64, x))));
21 return @as(comptime_int, @floor(@log10(@as(f64, x))));
3422 },
3523 .Int => |IntType| switch (IntType.signedness) {
3624 .signed => @compileError("log10 not implemented for signed integers"),
37 .unsigned => return @floatToInt(T, math.floor(log10_64(@intToFloat(f64, x)))),
25 .unsigned => return @floatToInt(T, @floor(@log10(@intToFloat(f64, x)))),
3826 },
3927 else => @compileError("log10 not implemented for " ++ @typeName(T)),
4028 }
4129}
42
43pub fn log10_32(x_: f32) f32 {
44 const ivln10hi: f32 = 4.3432617188e-01;
45 const ivln10lo: f32 = -3.1689971365e-05;
46 const log10_2hi: f32 = 3.0102920532e-01;
47 const log10_2lo: f32 = 7.9034151668e-07;
48 const Lg1: f32 = 0xaaaaaa.0p-24;
49 const Lg2: f32 = 0xccce13.0p-25;
50 const Lg3: f32 = 0x91e9ee.0p-25;
51 const Lg4: f32 = 0xf89e26.0p-26;
52
53 var x = x_;
54 var u = @bitCast(u32, x);
55 var ix = u;
56 var k: i32 = 0;
57
58 // x < 2^(-126)
59 if (ix < 0x00800000 or ix >> 31 != 0) {
60 // log(+-0) = -inf
61 if (ix << 1 == 0) {
62 return -math.inf(f32);
63 }
64 // log(-#) = nan
65 if (ix >> 31 != 0) {
66 return math.nan(f32);
67 }
68
69 k -= 25;
70 x *= 0x1.0p25;
71 ix = @bitCast(u32, x);
72 } else if (ix >= 0x7F800000) {
73 return x;
74 } else if (ix == 0x3F800000) {
75 return 0;
76 }
77
78 // x into [sqrt(2) / 2, sqrt(2)]
79 ix += 0x3F800000 - 0x3F3504F3;
80 k += @intCast(i32, ix >> 23) - 0x7F;
81 ix = (ix & 0x007FFFFF) + 0x3F3504F3;
82 x = @bitCast(f32, ix);
83
84 const f = x - 1.0;
85 const s = f / (2.0 + f);
86 const z = s * s;
87 const w = z * z;
88 const t1 = w * (Lg2 + w * Lg4);
89 const t2 = z * (Lg1 + w * Lg3);
90 const R = t2 + t1;
91 const hfsq = 0.5 * f * f;
92
93 var hi = f - hfsq;
94 u = @bitCast(u32, hi);
95 u &= 0xFFFFF000;
96 hi = @bitCast(f32, u);
97 const lo = f - hi - hfsq + s * (hfsq + R);
98 const dk = @intToFloat(f32, k);
99
100 return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi;
101}
102
103pub fn log10_64(x_: f64) f64 {
104 const ivln10hi: f64 = 4.34294481878168880939e-01;
105 const ivln10lo: f64 = 2.50829467116452752298e-11;
106 const log10_2hi: f64 = 3.01029995663611771306e-01;
107 const log10_2lo: f64 = 3.69423907715893078616e-13;
108 const Lg1: f64 = 6.666666666666735130e-01;
109 const Lg2: f64 = 3.999999999940941908e-01;
110 const Lg3: f64 = 2.857142874366239149e-01;
111 const Lg4: f64 = 2.222219843214978396e-01;
112 const Lg5: f64 = 1.818357216161805012e-01;
113 const Lg6: f64 = 1.531383769920937332e-01;
114 const Lg7: f64 = 1.479819860511658591e-01;
115
116 var x = x_;
117 var ix = @bitCast(u64, x);
118 var hx = @intCast(u32, ix >> 32);
119 var k: i32 = 0;
120
121 if (hx < 0x00100000 or hx >> 31 != 0) {
122 // log(+-0) = -inf
123 if (ix << 1 == 0) {
124 return -math.inf(f32);
125 }
126 // log(-#) = nan
127 if (hx >> 31 != 0) {
128 return math.nan(f32);
129 }
130
131 // subnormal, scale x
132 k -= 54;
133 x *= 0x1.0p54;
134 hx = @intCast(u32, @bitCast(u64, x) >> 32);
135 } else if (hx >= 0x7FF00000) {
136 return x;
137 } else if (hx == 0x3FF00000 and ix << 32 == 0) {
138 return 0;
139 }
140
141 // x into [sqrt(2) / 2, sqrt(2)]
142 hx += 0x3FF00000 - 0x3FE6A09E;
143 k += @intCast(i32, hx >> 20) - 0x3FF;
144 hx = (hx & 0x000FFFFF) + 0x3FE6A09E;
145 ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF);
146 x = @bitCast(f64, ix);
147
148 const f = x - 1.0;
149 const hfsq = 0.5 * f * f;
150 const s = f / (2.0 + f);
151 const z = s * s;
152 const w = z * z;
153 const t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
154 const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
155 const R = t2 + t1;
156
157 // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
158 var hi = f - hfsq;
159 var hii = @bitCast(u64, hi);
160 hii &= @as(u64, maxInt(u64)) << 32;
161 hi = @bitCast(f64, hii);
162 const lo = f - hi - hfsq + s * (hfsq + R);
163
164 // val_hi + val_lo ~ log10(1 + f) + k * log10(2)
165 var val_hi = hi * ivln10hi;
166 const dk = @intToFloat(f64, k);
167 const y = dk * log10_2hi;
168 var val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi;
169
170 // Extra precision multiplication
171 const ww = y + val_hi;
172 val_lo += (y - ww) + val_hi;
173 val_hi = ww;
174
175 return val_lo + val_hi;
176}
177
178test "math.log10" {
179 try testing.expect(log10(@as(f32, 0.2)) == log10_32(0.2));
180 try testing.expect(log10(@as(f64, 0.2)) == log10_64(0.2));
181}
182
183test "math.log10_32" {
184 const epsilon = 0.000001;
185
186 try testing.expect(math.approxEqAbs(f32, log10_32(0.2), -0.698970, epsilon));
187 try testing.expect(math.approxEqAbs(f32, log10_32(0.8923), -0.049489, epsilon));
188 try testing.expect(math.approxEqAbs(f32, log10_32(1.5), 0.176091, epsilon));
189 try testing.expect(math.approxEqAbs(f32, log10_32(37.45), 1.573452, epsilon));
190 try testing.expect(math.approxEqAbs(f32, log10_32(89.123), 1.94999, epsilon));
191 try testing.expect(math.approxEqAbs(f32, log10_32(123123.234375), 5.09034, epsilon));
192}
193
194test "math.log10_64" {
195 const epsilon = 0.000001;
196
197 try testing.expect(math.approxEqAbs(f64, log10_64(0.2), -0.698970, epsilon));
198 try testing.expect(math.approxEqAbs(f64, log10_64(0.8923), -0.049489, epsilon));
199 try testing.expect(math.approxEqAbs(f64, log10_64(1.5), 0.176091, epsilon));
200 try testing.expect(math.approxEqAbs(f64, log10_64(37.45), 1.573452, epsilon));
201 try testing.expect(math.approxEqAbs(f64, log10_64(89.123), 1.94999, epsilon));
202 try testing.expect(math.approxEqAbs(f64, log10_64(123123.234375), 5.09034, epsilon));
203}
204
205test "math.log10_32.special" {
206 try testing.expect(math.isPositiveInf(log10_32(math.inf(f32))));
207 try testing.expect(math.isNegativeInf(log10_32(0.0)));
208 try testing.expect(math.isNan(log10_32(-1.0)));
209 try testing.expect(math.isNan(log10_32(math.nan(f32))));
210}
211
212test "math.log10_64.special" {
213 try testing.expect(math.isPositiveInf(log10_64(math.inf(f64))));
214 try testing.expect(math.isNegativeInf(log10_64(0.0)));
215 try testing.expect(math.isNan(log10_64(-1.0)));
216 try testing.expect(math.isNan(log10_64(math.nan(f64))));
217}
lib/std/math/log2.zig+5-179
......@@ -1,13 +1,6 @@
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/log2f.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c
6
71const std = @import("../std.zig");
82const math = std.math;
93const expect = std.testing.expect;
10const maxInt = std.math.maxInt;
114
125/// Returns the base-2 logarithm of x.
136///
......@@ -20,15 +13,9 @@ pub fn log2(x: anytype) @TypeOf(x) {
2013 const T = @TypeOf(x);
2114 switch (@typeInfo(T)) {
2215 .ComptimeFloat => {
23 return @as(comptime_float, log2_64(x));
24 },
25 .Float => {
26 return switch (T) {
27 f32 => log2_32(x),
28 f64 => log2_64(x),
29 else => @compileError("log2 not implemented for " ++ @typeName(T)),
30 };
16 return @as(comptime_float, @log2(x));
3117 },
18 .Float => return @log2(x),
3219 .ComptimeInt => comptime {
3320 var result = 0;
3421 var x_shifted = x;
......@@ -46,168 +33,7 @@ pub fn log2(x: anytype) @TypeOf(x) {
4633 }
4734}
4835
49pub fn log2_32(x_: f32) f32 {
50 const ivln2hi: f32 = 1.4428710938e+00;
51 const ivln2lo: f32 = -1.7605285393e-04;
52 const Lg1: f32 = 0xaaaaaa.0p-24;
53 const Lg2: f32 = 0xccce13.0p-25;
54 const Lg3: f32 = 0x91e9ee.0p-25;
55 const Lg4: f32 = 0xf89e26.0p-26;
56
57 var x = x_;
58 var u = @bitCast(u32, x);
59 var ix = u;
60 var k: i32 = 0;
61
62 // x < 2^(-126)
63 if (ix < 0x00800000 or ix >> 31 != 0) {
64 // log(+-0) = -inf
65 if (ix << 1 == 0) {
66 return -math.inf(f32);
67 }
68 // log(-#) = nan
69 if (ix >> 31 != 0) {
70 return math.nan(f32);
71 }
72
73 k -= 25;
74 x *= 0x1.0p25;
75 ix = @bitCast(u32, x);
76 } else if (ix >= 0x7F800000) {
77 return x;
78 } else if (ix == 0x3F800000) {
79 return 0;
80 }
81
82 // x into [sqrt(2) / 2, sqrt(2)]
83 ix += 0x3F800000 - 0x3F3504F3;
84 k += @intCast(i32, ix >> 23) - 0x7F;
85 ix = (ix & 0x007FFFFF) + 0x3F3504F3;
86 x = @bitCast(f32, ix);
87
88 const f = x - 1.0;
89 const s = f / (2.0 + f);
90 const z = s * s;
91 const w = z * z;
92 const t1 = w * (Lg2 + w * Lg4);
93 const t2 = z * (Lg1 + w * Lg3);
94 const R = t2 + t1;
95 const hfsq = 0.5 * f * f;
96
97 var hi = f - hfsq;
98 u = @bitCast(u32, hi);
99 u &= 0xFFFFF000;
100 hi = @bitCast(f32, u);
101 const lo = f - hi - hfsq + s * (hfsq + R);
102 return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + @intToFloat(f32, k);
103}
104
105pub fn log2_64(x_: f64) f64 {
106 const ivln2hi: f64 = 1.44269504072144627571e+00;
107 const ivln2lo: f64 = 1.67517131648865118353e-10;
108 const Lg1: f64 = 6.666666666666735130e-01;
109 const Lg2: f64 = 3.999999999940941908e-01;
110 const Lg3: f64 = 2.857142874366239149e-01;
111 const Lg4: f64 = 2.222219843214978396e-01;
112 const Lg5: f64 = 1.818357216161805012e-01;
113 const Lg6: f64 = 1.531383769920937332e-01;
114 const Lg7: f64 = 1.479819860511658591e-01;
115
116 var x = x_;
117 var ix = @bitCast(u64, x);
118 var hx = @intCast(u32, ix >> 32);
119 var k: i32 = 0;
120
121 if (hx < 0x00100000 or hx >> 31 != 0) {
122 // log(+-0) = -inf
123 if (ix << 1 == 0) {
124 return -math.inf(f64);
125 }
126 // log(-#) = nan
127 if (hx >> 31 != 0) {
128 return math.nan(f64);
129 }
130
131 // subnormal, scale x
132 k -= 54;
133 x *= 0x1.0p54;
134 hx = @intCast(u32, @bitCast(u64, x) >> 32);
135 } else if (hx >= 0x7FF00000) {
136 return x;
137 } else if (hx == 0x3FF00000 and ix << 32 == 0) {
138 return 0;
139 }
140
141 // x into [sqrt(2) / 2, sqrt(2)]
142 hx += 0x3FF00000 - 0x3FE6A09E;
143 k += @intCast(i32, hx >> 20) - 0x3FF;
144 hx = (hx & 0x000FFFFF) + 0x3FE6A09E;
145 ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF);
146 x = @bitCast(f64, ix);
147
148 const f = x - 1.0;
149 const hfsq = 0.5 * f * f;
150 const s = f / (2.0 + f);
151 const z = s * s;
152 const w = z * z;
153 const t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
154 const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
155 const R = t2 + t1;
156
157 // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
158 var hi = f - hfsq;
159 var hii = @bitCast(u64, hi);
160 hii &= @as(u64, maxInt(u64)) << 32;
161 hi = @bitCast(f64, hii);
162 const lo = f - hi - hfsq + s * (hfsq + R);
163
164 var val_hi = hi * ivln2hi;
165 var val_lo = (lo + hi) * ivln2lo + lo * ivln2hi;
166
167 // spadd(val_hi, val_lo, y)
168 const y = @intToFloat(f64, k);
169 const ww = y + val_hi;
170 val_lo += (y - ww) + val_hi;
171 val_hi = ww;
172
173 return val_lo + val_hi;
174}
175
176test "math.log2" {
177 try expect(log2(@as(f32, 0.2)) == log2_32(0.2));
178 try expect(log2(@as(f64, 0.2)) == log2_64(0.2));
179}
180
181test "math.log2_32" {
182 const epsilon = 0.000001;
183
184 try expect(math.approxEqAbs(f32, log2_32(0.2), -2.321928, epsilon));
185 try expect(math.approxEqAbs(f32, log2_32(0.8923), -0.164399, epsilon));
186 try expect(math.approxEqAbs(f32, log2_32(1.5), 0.584962, epsilon));
187 try expect(math.approxEqAbs(f32, log2_32(37.45), 5.226894, epsilon));
188 try expect(math.approxEqAbs(f32, log2_32(123123.234375), 16.909744, epsilon));
189}
190
191test "math.log2_64" {
192 const epsilon = 0.000001;
193
194 try expect(math.approxEqAbs(f64, log2_64(0.2), -2.321928, epsilon));
195 try expect(math.approxEqAbs(f64, log2_64(0.8923), -0.164399, epsilon));
196 try expect(math.approxEqAbs(f64, log2_64(1.5), 0.584962, epsilon));
197 try expect(math.approxEqAbs(f64, log2_64(37.45), 5.226894, epsilon));
198 try expect(math.approxEqAbs(f64, log2_64(123123.234375), 16.909744, epsilon));
199}
200
201test "math.log2_32.special" {
202 try expect(math.isPositiveInf(log2_32(math.inf(f32))));
203 try expect(math.isNegativeInf(log2_32(0.0)));
204 try expect(math.isNan(log2_32(-1.0)));
205 try expect(math.isNan(log2_32(math.nan(f32))));
206}
207
208test "math.log2_64.special" {
209 try expect(math.isPositiveInf(log2_64(math.inf(f64))));
210 try expect(math.isNegativeInf(log2_64(0.0)));
211 try expect(math.isNan(log2_64(-1.0)));
212 try expect(math.isNan(log2_64(math.nan(f64))));
36test "log2" {
37 try expect(log2(@as(f32, 0.2)) == @log2(0.2));
38 try expect(log2(@as(f64, 0.2)) == @log2(0.2));
21339}
lib/std/math/nan.zig+12-19
......@@ -1,27 +1,20 @@
11const math = @import("../math.zig");
22
33/// Returns the nan representation for type T.
4pub fn nan(comptime T: type) T {
5 return switch (T) {
6 f16 => math.nan_f16,
7 f32 => math.nan_f32,
8 f64 => math.nan_f64,
9 f80 => math.nan_f80,
10 f128 => math.nan_f128,
11 else => @compileError("nan not implemented for " ++ @typeName(T)),
4pub inline fn nan(comptime T: type) T {
5 return switch (@typeInfo(T).Float.bits) {
6 16 => math.nan_f16,
7 32 => math.nan_f32,
8 64 => math.nan_f64,
9 80 => math.nan_f80,
10 128 => math.nan_f128,
11 else => @compileError("unreachable"),
1212 };
1313}
1414
1515/// Returns the signalling nan representation for type T.
16pub fn snan(comptime T: type) T {
17 // Note: A signalling nan is identical to a standard right now by may have a different bit
18 // representation in the future when required.
19 return switch (T) {
20 f16 => @bitCast(f16, math.nan_u16),
21 f32 => @bitCast(f32, math.nan_u32),
22 f64 => @bitCast(f64, math.nan_u64),
23 f80 => @bitCast(f80, math.nan_u80),
24 f128 => @bitCast(f128, math.nan_u128),
25 else => @compileError("snan not implemented for " ++ @typeName(T)),
26 };
16/// Note: A signalling nan is identical to a standard right now by may have a different bit
17/// representation in the future when required.
18pub inline fn snan(comptime T: type) T {
19 return nan(T);
2720}
lib/std/math/pow.zig+6-6
......@@ -82,7 +82,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
8282 }
8383 // pow(x, +inf) = +0 for |x| < 1
8484 // pow(x, -inf) = +0 for |x| > 1
85 else if ((math.fabs(x) < 1) == math.isPositiveInf(y)) {
85 else if ((@fabs(x) < 1) == math.isPositiveInf(y)) {
8686 return 0;
8787 }
8888 // pow(x, -inf) = +inf for |x| < 1
......@@ -108,14 +108,14 @@ pub fn pow(comptime T: type, x: T, y: T) T {
108108
109109 // special case sqrt
110110 if (y == 0.5) {
111 return math.sqrt(x);
111 return @sqrt(x);
112112 }
113113
114114 if (y == -0.5) {
115 return 1 / math.sqrt(x);
115 return 1 / @sqrt(x);
116116 }
117117
118 const r1 = math.modf(math.fabs(y));
118 const r1 = math.modf(@fabs(y));
119119 var yi = r1.ipart;
120120 var yf = r1.fpart;
121121
......@@ -123,7 +123,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
123123 return math.nan(T);
124124 }
125125 if (yi >= 1 << (@typeInfo(T).Float.bits - 1)) {
126 return math.exp(y * math.ln(x));
126 return @exp(y * @log(x));
127127 }
128128
129129 // a = a1 * 2^ae
......@@ -136,7 +136,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
136136 yf -= 1;
137137 yi += 1;
138138 }
139 a1 = math.exp(yf * math.ln(x));
139 a1 = @exp(yf * @log(x));
140140 }
141141
142142 // a *= x^yi
lib/std/math/round.zig deleted-185
......@@ -1,185 +0,0 @@
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/roundf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c
6
7const expect = std.testing.expect;
8const std = @import("../std.zig");
9const math = std.math;
10
11/// Returns x rounded to the nearest integer, rounding half away from zero.
12///
13/// Special Cases:
14/// - round(+-0) = +-0
15/// - round(+-inf) = +-inf
16/// - round(nan) = nan
17pub fn round(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);
19 return switch (T) {
20 f32 => round32(x),
21 f64 => round64(x),
22 f128 => round128(x),
23
24 // TODO this is not correct for some targets
25 c_longdouble => @floatCast(c_longdouble, round128(x)),
26
27 else => @compileError("round not implemented for " ++ @typeName(T)),
28 };
29}
30
31fn round32(x_: f32) f32 {
32 const f32_toint = 1.0 / math.floatEps(f32);
33
34 var x = x_;
35 const u = @bitCast(u32, x);
36 const e = (u >> 23) & 0xFF;
37 var y: f32 = undefined;
38
39 if (e >= 0x7F + 23) {
40 return x;
41 }
42 if (u >> 31 != 0) {
43 x = -x;
44 }
45 if (e < 0x7F - 1) {
46 math.doNotOptimizeAway(x + f32_toint);
47 return 0 * @bitCast(f32, u);
48 }
49
50 y = x + f32_toint - f32_toint - x;
51 if (y > 0.5) {
52 y = y + x - 1;
53 } else if (y <= -0.5) {
54 y = y + x + 1;
55 } else {
56 y = y + x;
57 }
58
59 if (u >> 31 != 0) {
60 return -y;
61 } else {
62 return y;
63 }
64}
65
66fn round64(x_: f64) f64 {
67 const f64_toint = 1.0 / math.floatEps(f64);
68
69 var x = x_;
70 const u = @bitCast(u64, x);
71 const e = (u >> 52) & 0x7FF;
72 var y: f64 = undefined;
73
74 if (e >= 0x3FF + 52) {
75 return x;
76 }
77 if (u >> 63 != 0) {
78 x = -x;
79 }
80 if (e < 0x3ff - 1) {
81 math.doNotOptimizeAway(x + f64_toint);
82 return 0 * @bitCast(f64, u);
83 }
84
85 y = x + f64_toint - f64_toint - x;
86 if (y > 0.5) {
87 y = y + x - 1;
88 } else if (y <= -0.5) {
89 y = y + x + 1;
90 } else {
91 y = y + x;
92 }
93
94 if (u >> 63 != 0) {
95 return -y;
96 } else {
97 return y;
98 }
99}
100
101fn round128(x_: f128) f128 {
102 const f128_toint = 1.0 / math.floatEps(f128);
103
104 var x = x_;
105 const u = @bitCast(u128, x);
106 const e = (u >> 112) & 0x7FFF;
107 var y: f128 = undefined;
108
109 if (e >= 0x3FFF + 112) {
110 return x;
111 }
112 if (u >> 127 != 0) {
113 x = -x;
114 }
115 if (e < 0x3FFF - 1) {
116 math.doNotOptimizeAway(x + f128_toint);
117 return 0 * @bitCast(f128, u);
118 }
119
120 y = x + f128_toint - f128_toint - x;
121 if (y > 0.5) {
122 y = y + x - 1;
123 } else if (y <= -0.5) {
124 y = y + x + 1;
125 } else {
126 y = y + x;
127 }
128
129 if (u >> 127 != 0) {
130 return -y;
131 } else {
132 return y;
133 }
134}
135
136test "math.round" {
137 try expect(round(@as(f32, 1.3)) == round32(1.3));
138 try expect(round(@as(f64, 1.3)) == round64(1.3));
139 try expect(round(@as(f128, 1.3)) == round128(1.3));
140}
141
142test "math.round32" {
143 try expect(round32(1.3) == 1.0);
144 try expect(round32(-1.3) == -1.0);
145 try expect(round32(0.2) == 0.0);
146 try expect(round32(1.8) == 2.0);
147}
148
149test "math.round64" {
150 try expect(round64(1.3) == 1.0);
151 try expect(round64(-1.3) == -1.0);
152 try expect(round64(0.2) == 0.0);
153 try expect(round64(1.8) == 2.0);
154}
155
156test "math.round128" {
157 try expect(round128(1.3) == 1.0);
158 try expect(round128(-1.3) == -1.0);
159 try expect(round128(0.2) == 0.0);
160 try expect(round128(1.8) == 2.0);
161}
162
163test "math.round32.special" {
164 try expect(round32(0.0) == 0.0);
165 try expect(round32(-0.0) == -0.0);
166 try expect(math.isPositiveInf(round32(math.inf(f32))));
167 try expect(math.isNegativeInf(round32(-math.inf(f32))));
168 try expect(math.isNan(round32(math.nan(f32))));
169}
170
171test "math.round64.special" {
172 try expect(round64(0.0) == 0.0);
173 try expect(round64(-0.0) == -0.0);
174 try expect(math.isPositiveInf(round64(math.inf(f64))));
175 try expect(math.isNegativeInf(round64(-math.inf(f64))));
176 try expect(math.isNan(round64(math.nan(f64))));
177}
178
179test "math.round128.special" {
180 try expect(round128(0.0) == 0.0);
181 try expect(round128(-0.0) == -0.0);
182 try expect(math.isPositiveInf(round128(math.inf(f128))));
183 try expect(math.isNegativeInf(round128(-math.inf(f128))));
184 try expect(math.isNan(round128(math.nan(f128))));
185}
lib/std/math/sin.zig deleted-168
......@@ -1,168 +0,0 @@
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/sinf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/sin.c
6//
7const std = @import("../std.zig");
8const math = std.math;
9const expect = std.testing.expect;
10
11const kernel = @import("__trig.zig");
12const __rem_pio2 = @import("__rem_pio2.zig").__rem_pio2;
13const __rem_pio2f = @import("__rem_pio2f.zig").__rem_pio2f;
14
15/// Returns the sine of the radian value x.
16///
17/// Special Cases:
18/// - sin(+-0) = +-0
19/// - sin(+-inf) = nan
20/// - sin(nan) = nan
21pub fn sin(x: anytype) @TypeOf(x) {
22 const T = @TypeOf(x);
23 return switch (T) {
24 f32 => sin32(x),
25 f64 => sin64(x),
26 else => @compileError("sin not implemented for " ++ @typeName(T)),
27 };
28}
29
30fn sin32(x: f32) f32 {
31 // Small multiples of pi/2 rounded to double precision.
32 const s1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
33 const s2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
34 const s3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
35 const s4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
36
37 var ix = @bitCast(u32, x);
38 const sign = ix >> 31 != 0;
39 ix &= 0x7fffffff;
40
41 if (ix <= 0x3f490fda) { // |x| ~<= pi/4
42 if (ix < 0x39800000) { // |x| < 2**-12
43 // raise inexact if x!=0 and underflow if subnormal
44 math.doNotOptimizeAway(if (ix < 0x00800000) x / 0x1p120 else x + 0x1p120);
45 return x;
46 }
47 return kernel.__sindf(x);
48 }
49 if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4
50 if (ix <= 0x4016cbe3) { // |x| ~<= 3pi/4
51 if (sign) {
52 return -kernel.__cosdf(x + s1pio2);
53 } else {
54 return kernel.__cosdf(x - s1pio2);
55 }
56 }
57 return kernel.__sindf(if (sign) -(x + s2pio2) else -(x - s2pio2));
58 }
59 if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4
60 if (ix <= 0x40afeddf) { // |x| ~<= 7*pi/4
61 if (sign) {
62 return kernel.__cosdf(x + s3pio2);
63 } else {
64 return -kernel.__cosdf(x - s3pio2);
65 }
66 }
67 return kernel.__sindf(if (sign) x + s4pio2 else x - s4pio2);
68 }
69
70 // sin(Inf or NaN) is NaN
71 if (ix >= 0x7f800000) {
72 return x - x;
73 }
74
75 var y: f64 = undefined;
76 const n = __rem_pio2f(x, &y);
77 return switch (n & 3) {
78 0 => kernel.__sindf(y),
79 1 => kernel.__cosdf(y),
80 2 => kernel.__sindf(-y),
81 else => -kernel.__cosdf(y),
82 };
83}
84
85fn sin64(x: f64) f64 {
86 var ix = @bitCast(u64, x) >> 32;
87 ix &= 0x7fffffff;
88
89 // |x| ~< pi/4
90 if (ix <= 0x3fe921fb) {
91 if (ix < 0x3e500000) { // |x| < 2**-26
92 // raise inexact if x != 0 and underflow if subnormal
93 math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
94 return x;
95 }
96 return kernel.__sin(x, 0.0, 0);
97 }
98
99 // sin(Inf or NaN) is NaN
100 if (ix >= 0x7ff00000) {
101 return x - x;
102 }
103
104 var y: [2]f64 = undefined;
105 const n = __rem_pio2(x, &y);
106 return switch (n & 3) {
107 0 => kernel.__sin(y[0], y[1], 1),
108 1 => kernel.__cos(y[0], y[1]),
109 2 => -kernel.__sin(y[0], y[1], 1),
110 else => -kernel.__cos(y[0], y[1]),
111 };
112}
113
114test "math.sin" {
115 try expect(sin(@as(f32, 0.0)) == sin32(0.0));
116 try expect(sin(@as(f64, 0.0)) == sin64(0.0));
117 try expect(comptime (math.sin(@as(f64, 2))) == math.sin(@as(f64, 2)));
118}
119
120test "math.sin32" {
121 const epsilon = 0.00001;
122
123 try expect(math.approxEqAbs(f32, sin32(0.0), 0.0, epsilon));
124 try expect(math.approxEqAbs(f32, sin32(0.2), 0.198669, epsilon));
125 try expect(math.approxEqAbs(f32, sin32(0.8923), 0.778517, epsilon));
126 try expect(math.approxEqAbs(f32, sin32(1.5), 0.997495, epsilon));
127 try expect(math.approxEqAbs(f32, sin32(-1.5), -0.997495, epsilon));
128 try expect(math.approxEqAbs(f32, sin32(37.45), -0.246544, epsilon));
129 try expect(math.approxEqAbs(f32, sin32(89.123), 0.916166, epsilon));
130}
131
132test "math.sin64" {
133 const epsilon = 0.000001;
134
135 try expect(math.approxEqAbs(f64, sin64(0.0), 0.0, epsilon));
136 try expect(math.approxEqAbs(f64, sin64(0.2), 0.198669, epsilon));
137 try expect(math.approxEqAbs(f64, sin64(0.8923), 0.778517, epsilon));
138 try expect(math.approxEqAbs(f64, sin64(1.5), 0.997495, epsilon));
139 try expect(math.approxEqAbs(f64, sin64(-1.5), -0.997495, epsilon));
140 try expect(math.approxEqAbs(f64, sin64(37.45), -0.246543, epsilon));
141 try expect(math.approxEqAbs(f64, sin64(89.123), 0.916166, epsilon));
142}
143
144test "math.sin32.special" {
145 try expect(sin32(0.0) == 0.0);
146 try expect(sin32(-0.0) == -0.0);
147 try expect(math.isNan(sin32(math.inf(f32))));
148 try expect(math.isNan(sin32(-math.inf(f32))));
149 try expect(math.isNan(sin32(math.nan(f32))));
150}
151
152test "math.sin64.special" {
153 try expect(sin64(0.0) == 0.0);
154 try expect(sin64(-0.0) == -0.0);
155 try expect(math.isNan(sin64(math.inf(f64))));
156 try expect(math.isNan(sin64(-math.inf(f64))));
157 try expect(math.isNan(sin64(math.nan(f64))));
158}
159
160test "math.sin32 #9901" {
161 const float = @bitCast(f32, @as(u32, 0b11100011111111110000000000000000));
162 _ = std.math.sin(float);
163}
164
165test "math.sin64 #9901" {
166 const float = @bitCast(f64, @as(u64, 0b1111111101000001000000001111110111111111100000000000000000000001));
167 _ = std.math.sin(float);
168}
lib/std/math/tan.zig deleted-140
......@@ -1,140 +0,0 @@
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/tanf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/tan.c
6// https://golang.org/src/math/tan.go
7
8const std = @import("../std.zig");
9const math = std.math;
10const expect = std.testing.expect;
11
12const kernel = @import("__trig.zig");
13const __rem_pio2 = @import("__rem_pio2.zig").__rem_pio2;
14const __rem_pio2f = @import("__rem_pio2f.zig").__rem_pio2f;
15
16/// Returns the tangent of the radian value x.
17///
18/// Special Cases:
19/// - tan(+-0) = +-0
20/// - tan(+-inf) = nan
21/// - tan(nan) = nan
22pub fn tan(x: anytype) @TypeOf(x) {
23 const T = @TypeOf(x);
24 return switch (T) {
25 f32 => tan32(x),
26 f64 => tan64(x),
27 else => @compileError("tan not implemented for " ++ @typeName(T)),
28 };
29}
30
31fn tan32(x: f32) f32 {
32 // Small multiples of pi/2 rounded to double precision.
33 const t1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
34 const t2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
35 const t3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
36 const t4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
37
38 var ix = @bitCast(u32, x);
39 const sign = ix >> 31 != 0;
40 ix &= 0x7fffffff;
41
42 if (ix <= 0x3f490fda) { // |x| ~<= pi/4
43 if (ix < 0x39800000) { // |x| < 2**-12
44 // raise inexact if x!=0 and underflow if subnormal
45 math.doNotOptimizeAway(if (ix < 0x00800000) x / 0x1p120 else x + 0x1p120);
46 return x;
47 }
48 return kernel.__tandf(x, false);
49 }
50 if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4
51 if (ix <= 0x4016cbe3) { // |x| ~<= 3pi/4
52 return kernel.__tandf((if (sign) x + t1pio2 else x - t1pio2), true);
53 } else {
54 return kernel.__tandf((if (sign) x + t2pio2 else x - t2pio2), false);
55 }
56 }
57 if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4
58 if (ix <= 0x40afeddf) { // |x| ~<= 7*pi/4
59 return kernel.__tandf((if (sign) x + t3pio2 else x - t3pio2), true);
60 } else {
61 return kernel.__tandf((if (sign) x + t4pio2 else x - t4pio2), false);
62 }
63 }
64
65 // tan(Inf or NaN) is NaN
66 if (ix >= 0x7f800000) {
67 return x - x;
68 }
69
70 var y: f64 = undefined;
71 const n = __rem_pio2f(x, &y);
72 return kernel.__tandf(y, n & 1 != 0);
73}
74
75fn tan64(x: f64) f64 {
76 var ix = @bitCast(u64, x) >> 32;
77 ix &= 0x7fffffff;
78
79 // |x| ~< pi/4
80 if (ix <= 0x3fe921fb) {
81 if (ix < 0x3e400000) { // |x| < 2**-27
82 // raise inexact if x!=0 and underflow if subnormal
83 math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
84 return x;
85 }
86 return kernel.__tan(x, 0.0, false);
87 }
88
89 // tan(Inf or NaN) is NaN
90 if (ix >= 0x7ff00000) {
91 return x - x;
92 }
93
94 var y: [2]f64 = undefined;
95 const n = __rem_pio2(x, &y);
96 return kernel.__tan(y[0], y[1], n & 1 != 0);
97}
98
99test "math.tan" {
100 try expect(tan(@as(f32, 0.0)) == tan32(0.0));
101 try expect(tan(@as(f64, 0.0)) == tan64(0.0));
102}
103
104test "math.tan32" {
105 const epsilon = 0.00001;
106
107 try expect(math.approxEqAbs(f32, tan32(0.0), 0.0, epsilon));
108 try expect(math.approxEqAbs(f32, tan32(0.2), 0.202710, epsilon));
109 try expect(math.approxEqAbs(f32, tan32(0.8923), 1.240422, epsilon));
110 try expect(math.approxEqAbs(f32, tan32(1.5), 14.101420, epsilon));
111 try expect(math.approxEqAbs(f32, tan32(37.45), -0.254397, epsilon));
112 try expect(math.approxEqAbs(f32, tan32(89.123), 2.285852, epsilon));
113}
114
115test "math.tan64" {
116 const epsilon = 0.000001;
117
118 try expect(math.approxEqAbs(f64, tan64(0.0), 0.0, epsilon));
119 try expect(math.approxEqAbs(f64, tan64(0.2), 0.202710, epsilon));
120 try expect(math.approxEqAbs(f64, tan64(0.8923), 1.240422, epsilon));
121 try expect(math.approxEqAbs(f64, tan64(1.5), 14.101420, epsilon));
122 try expect(math.approxEqAbs(f64, tan64(37.45), -0.254397, epsilon));
123 try expect(math.approxEqAbs(f64, tan64(89.123), 2.2858376, epsilon));
124}
125
126test "math.tan32.special" {
127 try expect(tan32(0.0) == 0.0);
128 try expect(tan32(-0.0) == -0.0);
129 try expect(math.isNan(tan32(math.inf(f32))));
130 try expect(math.isNan(tan32(-math.inf(f32))));
131 try expect(math.isNan(tan32(math.nan(f32))));
132}
133
134test "math.tan64.special" {
135 try expect(tan64(0.0) == 0.0);
136 try expect(tan64(-0.0) == -0.0);
137 try expect(math.isNan(tan64(math.inf(f64))));
138 try expect(math.isNan(tan64(-math.inf(f64))));
139 try expect(math.isNan(tan64(math.nan(f64))));
140}
lib/std/math/trunc.zig deleted-141
......@@ -1,141 +0,0 @@
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/truncf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c
6
7const std = @import("../std.zig");
8const math = std.math;
9const expect = std.testing.expect;
10const maxInt = std.math.maxInt;
11
12/// Returns the integer value of x.
13///
14/// Special Cases:
15/// - trunc(+-0) = +-0
16/// - trunc(+-inf) = +-inf
17/// - trunc(nan) = nan
18pub fn trunc(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);
20 return switch (T) {
21 f32 => trunc32(x),
22 f64 => trunc64(x),
23 f128 => trunc128(x),
24
25 // TODO this is not correct for some targets
26 c_longdouble => @floatCast(c_longdouble, trunc128(x)),
27
28 else => @compileError("trunc not implemented for " ++ @typeName(T)),
29 };
30}
31
32fn trunc32(x: f32) f32 {
33 const u = @bitCast(u32, x);
34 var e = @intCast(i32, ((u >> 23) & 0xFF)) - 0x7F + 9;
35 var m: u32 = undefined;
36
37 if (e >= 23 + 9) {
38 return x;
39 }
40 if (e < 9) {
41 e = 1;
42 }
43
44 m = @as(u32, maxInt(u32)) >> @intCast(u5, e);
45 if (u & m == 0) {
46 return x;
47 } else {
48 math.doNotOptimizeAway(x + 0x1p120);
49 return @bitCast(f32, u & ~m);
50 }
51}
52
53fn trunc64(x: f64) f64 {
54 const u = @bitCast(u64, x);
55 var e = @intCast(i32, ((u >> 52) & 0x7FF)) - 0x3FF + 12;
56 var m: u64 = undefined;
57
58 if (e >= 52 + 12) {
59 return x;
60 }
61 if (e < 12) {
62 e = 1;
63 }
64
65 m = @as(u64, maxInt(u64)) >> @intCast(u6, e);
66 if (u & m == 0) {
67 return x;
68 } else {
69 math.doNotOptimizeAway(x + 0x1p120);
70 return @bitCast(f64, u & ~m);
71 }
72}
73
74fn trunc128(x: f128) f128 {
75 const u = @bitCast(u128, x);
76 var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16;
77 var m: u128 = undefined;
78
79 if (e >= 112 + 16) {
80 return x;
81 }
82 if (e < 16) {
83 e = 1;
84 }
85
86 m = @as(u128, maxInt(u128)) >> @intCast(u7, e);
87 if (u & m == 0) {
88 return x;
89 } else {
90 math.doNotOptimizeAway(x + 0x1p120);
91 return @bitCast(f128, u & ~m);
92 }
93}
94
95test "math.trunc" {
96 try expect(trunc(@as(f32, 1.3)) == trunc32(1.3));
97 try expect(trunc(@as(f64, 1.3)) == trunc64(1.3));
98 try expect(trunc(@as(f128, 1.3)) == trunc128(1.3));
99}
100
101test "math.trunc32" {
102 try expect(trunc32(1.3) == 1.0);
103 try expect(trunc32(-1.3) == -1.0);
104 try expect(trunc32(0.2) == 0.0);
105}
106
107test "math.trunc64" {
108 try expect(trunc64(1.3) == 1.0);
109 try expect(trunc64(-1.3) == -1.0);
110 try expect(trunc64(0.2) == 0.0);
111}
112
113test "math.trunc128" {
114 try expect(trunc128(1.3) == 1.0);
115 try expect(trunc128(-1.3) == -1.0);
116 try expect(trunc128(0.2) == 0.0);
117}
118
119test "math.trunc32.special" {
120 try expect(trunc32(0.0) == 0.0); // 0x3F800000
121 try expect(trunc32(-0.0) == -0.0);
122 try expect(math.isPositiveInf(trunc32(math.inf(f32))));
123 try expect(math.isNegativeInf(trunc32(-math.inf(f32))));
124 try expect(math.isNan(trunc32(math.nan(f32))));
125}
126
127test "math.trunc64.special" {
128 try expect(trunc64(0.0) == 0.0);
129 try expect(trunc64(-0.0) == -0.0);
130 try expect(math.isPositiveInf(trunc64(math.inf(f64))));
131 try expect(math.isNegativeInf(trunc64(-math.inf(f64))));
132 try expect(math.isNan(trunc64(math.nan(f64))));
133}
134
135test "math.trunc128.special" {
136 try expect(trunc128(0.0) == 0.0);
137 try expect(trunc128(-0.0) == -0.0);
138 try expect(math.isPositiveInf(trunc128(math.inf(f128))));
139 try expect(math.isNegativeInf(trunc128(-math.inf(f128))));
140 try expect(math.isNan(trunc128(math.nan(f128))));
141}
lib/std/rand/ziggurat.zig+8-8
......@@ -33,7 +33,7 @@ pub fn next_f64(random: Random, comptime tables: ZigTable) f64 {
3333 };
3434
3535 const x = u * tables.x[i];
36 const test_x = if (tables.is_symmetric) math.fabs(x) else x;
36 const test_x = if (tables.is_symmetric) @fabs(x) else x;
3737
3838 // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])
3939 if (test_x < tables.x[i + 1]) {
......@@ -106,18 +106,18 @@ const norm_r = 3.6541528853610088;
106106const norm_v = 0.00492867323399;
107107
108108fn norm_f(x: f64) f64 {
109 return math.exp(-x * x / 2.0);
109 return @exp(-x * x / 2.0);
110110}
111111fn norm_f_inv(y: f64) f64 {
112 return math.sqrt(-2.0 * math.ln(y));
112 return @sqrt(-2.0 * @log(y));
113113}
114114fn norm_zero_case(random: Random, u: f64) f64 {
115115 var x: f64 = 1;
116116 var y: f64 = 0;
117117
118118 while (-2.0 * y < x * x) {
119 x = math.ln(random.float(f64)) / norm_r;
120 y = math.ln(random.float(f64));
119 x = @log(random.float(f64)) / norm_r;
120 y = @log(random.float(f64));
121121 }
122122
123123 if (u < 0) {
......@@ -151,13 +151,13 @@ const exp_r = 7.69711747013104972;
151151const exp_v = 0.0039496598225815571993;
152152
153153fn exp_f(x: f64) f64 {
154 return math.exp(-x);
154 return @exp(-x);
155155}
156156fn exp_f_inv(y: f64) f64 {
157 return -math.ln(y);
157 return -@log(y);
158158}
159159fn exp_zero_case(random: Random, _: f64) f64 {
160 return exp_r - math.ln(random.float(f64));
160 return exp_r - @log(random.float(f64));
161161}
162162
163163test "exp dist sanity" {
lib/std/special/c.zig-591
......@@ -12,7 +12,6 @@ const maxInt = std.math.maxInt;
1212const native_os = builtin.os.tag;
1313const native_arch = builtin.cpu.arch;
1414const native_abi = builtin.abi;
15const long_double_is_f128 = builtin.target.longDoubleIs(f128);
1615
1716const is_wasm = switch (native_arch) {
1817 .wasm32, .wasm64 => true,
......@@ -55,53 +54,6 @@ comptime {
5554 } else if (is_msvc) {
5655 @export(_fltused, .{ .name = "_fltused", .linkage = .Strong });
5756 }
58
59 @export(trunc, .{ .name = "trunc", .linkage = .Strong });
60 @export(truncf, .{ .name = "truncf", .linkage = .Strong });
61 @export(truncl, .{ .name = "truncl", .linkage = .Strong });
62
63 @export(log, .{ .name = "log", .linkage = .Strong });
64 @export(logf, .{ .name = "logf", .linkage = .Strong });
65
66 @export(sin, .{ .name = "sin", .linkage = .Strong });
67 @export(sinf, .{ .name = "sinf", .linkage = .Strong });
68
69 @export(cos, .{ .name = "cos", .linkage = .Strong });
70 @export(cosf, .{ .name = "cosf", .linkage = .Strong });
71
72 @export(exp, .{ .name = "exp", .linkage = .Strong });
73 @export(expf, .{ .name = "expf", .linkage = .Strong });
74
75 @export(exp2, .{ .name = "exp2", .linkage = .Strong });
76 @export(exp2f, .{ .name = "exp2f", .linkage = .Strong });
77
78 @export(log2, .{ .name = "log2", .linkage = .Strong });
79 @export(log2f, .{ .name = "log2f", .linkage = .Strong });
80
81 @export(log10, .{ .name = "log10", .linkage = .Strong });
82 @export(log10f, .{ .name = "log10f", .linkage = .Strong });
83
84 @export(fmod, .{ .name = "fmod", .linkage = .Strong });
85 @export(fmodf, .{ .name = "fmodf", .linkage = .Strong });
86
87 @export(sincos, .{ .name = "sincos", .linkage = .Strong });
88 @export(sincosf, .{ .name = "sincosf", .linkage = .Strong });
89
90 @export(fabs, .{ .name = "fabs", .linkage = .Strong });
91 @export(fabsf, .{ .name = "fabsf", .linkage = .Strong });
92
93 @export(round, .{ .name = "round", .linkage = .Strong });
94 @export(roundf, .{ .name = "roundf", .linkage = .Strong });
95 @export(roundl, .{ .name = "roundl", .linkage = .Strong });
96
97 @export(fmin, .{ .name = "fmin", .linkage = .Strong });
98 @export(fminf, .{ .name = "fminf", .linkage = .Strong });
99
100 @export(fmax, .{ .name = "fmax", .linkage = .Strong });
101 @export(fmaxf, .{ .name = "fmaxf", .linkage = .Strong });
102
103 @export(sqrt, .{ .name = "sqrt", .linkage = .Strong });
104 @export(sqrtf, .{ .name = "sqrtf", .linkage = .Strong });
10557}
10658
10759// Avoid dragging in the runtime safety mechanisms into this .o file,
......@@ -352,549 +304,6 @@ test "strncmp" {
352304 try std.testing.expect(strncmp("\xff", "\x02", 1) == 253);
353305}
354306
355fn trunc(a: f64) callconv(.C) f64 {
356 return math.trunc(a);
357}
358
359fn truncf(a: f32) callconv(.C) f32 {
360 return math.trunc(a);
361}
362
363fn truncl(a: c_longdouble) callconv(.C) c_longdouble {
364 if (!long_double_is_f128) {
365 @panic("TODO implement this");
366 }
367 return math.trunc(a);
368}
369
370fn log(a: f64) callconv(.C) f64 {
371 return math.ln(a);
372}
373
374fn logf(a: f32) callconv(.C) f32 {
375 return math.ln(a);
376}
377
378fn sin(a: f64) callconv(.C) f64 {
379 return math.sin(a);
380}
381
382fn sinf(a: f32) callconv(.C) f32 {
383 return math.sin(a);
384}
385
386fn cos(a: f64) callconv(.C) f64 {
387 return math.cos(a);
388}
389
390fn cosf(a: f32) callconv(.C) f32 {
391 return math.cos(a);
392}
393
394fn exp(a: f64) callconv(.C) f64 {
395 return math.exp(a);
396}
397
398fn expf(a: f32) callconv(.C) f32 {
399 return math.exp(a);
400}
401
402fn exp2(a: f64) callconv(.C) f64 {
403 return math.exp2(a);
404}
405
406fn exp2f(a: f32) callconv(.C) f32 {
407 return math.exp2(a);
408}
409
410fn log2(a: f64) callconv(.C) f64 {
411 return math.log2(a);
412}
413
414fn log2f(a: f32) callconv(.C) f32 {
415 return math.log2(a);
416}
417
418fn log10(a: f64) callconv(.C) f64 {
419 return math.log10(a);
420}
421
422fn log10f(a: f32) callconv(.C) f32 {
423 return math.log10(a);
424}
425
426fn fmodf(x: f32, y: f32) callconv(.C) f32 {
427 return generic_fmod(f32, x, y);
428}
429fn fmod(x: f64, y: f64) callconv(.C) f64 {
430 return generic_fmod(f64, x, y);
431}
432
433fn generic_fmod(comptime T: type, x: T, y: T) T {
434 @setRuntimeSafety(false);
435
436 const bits = @typeInfo(T).Float.bits;
437 const uint = std.meta.Int(.unsigned, bits);
438 const log2uint = math.Log2Int(uint);
439 const digits = if (T == f32) 23 else 52;
440 const exp_bits = if (T == f32) 9 else 12;
441 const bits_minus_1 = bits - 1;
442 const mask = if (T == f32) 0xff else 0x7ff;
443 var ux = @bitCast(uint, x);
444 var uy = @bitCast(uint, y);
445 var ex = @intCast(i32, (ux >> digits) & mask);
446 var ey = @intCast(i32, (uy >> digits) & mask);
447 const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1);
448 var i: uint = undefined;
449
450 if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask)
451 return (x * y) / (x * y);
452
453 if (ux << 1 <= uy << 1) {
454 if (ux << 1 == uy << 1)
455 return 0 * x;
456 return x;
457 }
458
459 // normalize x and y
460 if (ex == 0) {
461 i = ux << exp_bits;
462 while (i >> bits_minus_1 == 0) : ({
463 ex -= 1;
464 i <<= 1;
465 }) {}
466 ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1));
467 } else {
468 ux &= maxInt(uint) >> exp_bits;
469 ux |= 1 << digits;
470 }
471 if (ey == 0) {
472 i = uy << exp_bits;
473 while (i >> bits_minus_1 == 0) : ({
474 ey -= 1;
475 i <<= 1;
476 }) {}
477 uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1));
478 } else {
479 uy &= maxInt(uint) >> exp_bits;
480 uy |= 1 << digits;
481 }
482
483 // x mod y
484 while (ex > ey) : (ex -= 1) {
485 i = ux -% uy;
486 if (i >> bits_minus_1 == 0) {
487 if (i == 0)
488 return 0 * x;
489 ux = i;
490 }
491 ux <<= 1;
492 }
493 i = ux -% uy;
494 if (i >> bits_minus_1 == 0) {
495 if (i == 0)
496 return 0 * x;
497 ux = i;
498 }
499 while (ux >> digits == 0) : ({
500 ux <<= 1;
501 ex -= 1;
502 }) {}
503
504 // scale result up
505 if (ex > 0) {
506 ux -%= 1 << digits;
507 ux |= @as(uint, @bitCast(u32, ex)) << digits;
508 } else {
509 ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1));
510 }
511 if (T == f32) {
512 ux |= sx;
513 } else {
514 ux |= @intCast(uint, sx) << bits_minus_1;
515 }
516 return @bitCast(T, ux);
517}
518
519test "fmod, fmodf" {
520 inline for ([_]type{ f32, f64 }) |T| {
521 const nan_val = math.nan(T);
522 const inf_val = math.inf(T);
523
524 try std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0)));
525 try std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val)));
526 try std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0)));
527 try std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0)));
528 try std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0)));
529
530 try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0));
531 try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0));
532
533 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0));
534 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0));
535 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0));
536 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0));
537 }
538}
539
540fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void {
541 r_sin.* = math.sin(a);
542 r_cos.* = math.cos(a);
543}
544
545fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void {
546 r_sin.* = math.sin(a);
547 r_cos.* = math.cos(a);
548}
549
550fn fabs(a: f64) callconv(.C) f64 {
551 return math.fabs(a);
552}
553
554fn fabsf(a: f32) callconv(.C) f32 {
555 return math.fabs(a);
556}
557
558fn roundf(a: f32) callconv(.C) f32 {
559 return math.round(a);
560}
561
562fn round(a: f64) callconv(.C) f64 {
563 return math.round(a);
564}
565
566fn roundl(a: c_longdouble) callconv(.C) c_longdouble {
567 if (!long_double_is_f128) {
568 @panic("TODO implement this");
569 }
570 return math.round(a);
571}
572
573fn fminf(x: f32, y: f32) callconv(.C) f32 {
574 return generic_fmin(f32, x, y);
575}
576
577fn fmin(x: f64, y: f64) callconv(.C) f64 {
578 return generic_fmin(f64, x, y);
579}
580
581fn generic_fmin(comptime T: type, x: T, y: T) T {
582 if (isNan(x))
583 return y;
584 if (isNan(y))
585 return x;
586 return if (x < y) x else y;
587}
588
589test "fmin, fminf" {
590 inline for ([_]type{ f32, f64 }) |T| {
591 const nan_val = math.nan(T);
592
593 try std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val)));
594 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0));
595 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val));
596
597 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0));
598 try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0));
599 }
600}
601
602fn fmaxf(x: f32, y: f32) callconv(.C) f32 {
603 return generic_fmax(f32, x, y);
604}
605
606fn fmax(x: f64, y: f64) callconv(.C) f64 {
607 return generic_fmax(f64, x, y);
608}
609
610fn generic_fmax(comptime T: type, x: T, y: T) T {
611 if (isNan(x))
612 return y;
613 if (isNan(y))
614 return x;
615 return if (x < y) y else x;
616}
617
618test "fmax, fmaxf" {
619 inline for ([_]type{ f32, f64 }) |T| {
620 const nan_val = math.nan(T);
621
622 try std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val)));
623 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0));
624 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val));
625
626 try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0));
627 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0));
628 }
629}
630
631// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
632// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
633// potentially some edge cases remaining that are not handled in the same way.
634fn sqrt(x: f64) callconv(.C) f64 {
635 const tiny: f64 = 1.0e-300;
636 const sign: u32 = 0x80000000;
637 const u = @bitCast(u64, x);
638
639 var ix0 = @intCast(u32, u >> 32);
640 var ix1 = @intCast(u32, u & 0xFFFFFFFF);
641
642 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
643 if (ix0 & 0x7FF00000 == 0x7FF00000) {
644 return x * x + x;
645 }
646
647 // sqrt(+-0) = +-0
648 if (x == 0.0) {
649 return x;
650 }
651 // sqrt(-ve) = snan
652 if (ix0 & sign != 0) {
653 return math.snan(f64);
654 }
655
656 // normalize x
657 var m = @intCast(i32, ix0 >> 20);
658 if (m == 0) {
659 // subnormal
660 while (ix0 == 0) {
661 m -= 21;
662 ix0 |= ix1 >> 11;
663 ix1 <<= 21;
664 }
665
666 // subnormal
667 var i: u32 = 0;
668 while (ix0 & 0x00100000 == 0) : (i += 1) {
669 ix0 <<= 1;
670 }
671 m -= @intCast(i32, i) - 1;
672 ix0 |= ix1 >> @intCast(u5, 32 - i);
673 ix1 <<= @intCast(u5, i);
674 }
675
676 // unbias exponent
677 m -= 1023;
678 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
679 if (m & 1 != 0) {
680 ix0 += ix0 + (ix1 >> 31);
681 ix1 = ix1 +% ix1;
682 }
683 m >>= 1;
684
685 // sqrt(x) bit by bit
686 ix0 += ix0 + (ix1 >> 31);
687 ix1 = ix1 +% ix1;
688
689 var q: u32 = 0;
690 var q1: u32 = 0;
691 var s0: u32 = 0;
692 var s1: u32 = 0;
693 var r: u32 = 0x00200000;
694 var t: u32 = undefined;
695 var t1: u32 = undefined;
696
697 while (r != 0) {
698 t = s0 +% r;
699 if (t <= ix0) {
700 s0 = t + r;
701 ix0 -= t;
702 q += r;
703 }
704 ix0 = ix0 +% ix0 +% (ix1 >> 31);
705 ix1 = ix1 +% ix1;
706 r >>= 1;
707 }
708
709 r = sign;
710 while (r != 0) {
711 t1 = s1 +% r;
712 t = s0;
713 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
714 s1 = t1 +% r;
715 if (t1 & sign == sign and s1 & sign == 0) {
716 s0 += 1;
717 }
718 ix0 -= t;
719 if (ix1 < t1) {
720 ix0 -= 1;
721 }
722 ix1 = ix1 -% t1;
723 q1 += r;
724 }
725 ix0 = ix0 +% ix0 +% (ix1 >> 31);
726 ix1 = ix1 +% ix1;
727 r >>= 1;
728 }
729
730 // rounding direction
731 if (ix0 | ix1 != 0) {
732 var z = 1.0 - tiny; // raise inexact
733 if (z >= 1.0) {
734 z = 1.0 + tiny;
735 if (q1 == 0xFFFFFFFF) {
736 q1 = 0;
737 q += 1;
738 } else if (z > 1.0) {
739 if (q1 == 0xFFFFFFFE) {
740 q += 1;
741 }
742 q1 += 2;
743 } else {
744 q1 += q1 & 1;
745 }
746 }
747 }
748
749 ix0 = (q >> 1) + 0x3FE00000;
750 ix1 = q1 >> 1;
751 if (q & 1 != 0) {
752 ix1 |= 0x80000000;
753 }
754
755 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
756 // behaviour at least.
757 var iix0 = @intCast(i32, ix0);
758 iix0 = iix0 +% (m << 20);
759
760 const uz = (@intCast(u64, iix0) << 32) | ix1;
761 return @bitCast(f64, uz);
762}
763
764test "sqrt" {
765 const V = [_]f64{
766 0.0,
767 4.089288054930154,
768 7.538757127071935,
769 8.97780793672623,
770 5.304443821913729,
771 5.682408965311888,
772 0.5846878579110049,
773 3.650338664297043,
774 0.3178091951800732,
775 7.1505232436382835,
776 3.6589165881946464,
777 };
778
779 // Note that @sqrt will either generate the sqrt opcode (if supported by the
780 // target ISA) or a call to `sqrtf` otherwise.
781 for (V) |val|
782 try std.testing.expectEqual(@sqrt(val), sqrt(val));
783}
784
785test "sqrt special" {
786 try std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64))));
787 try std.testing.expect(sqrt(0.0) == 0.0);
788 try std.testing.expect(sqrt(-0.0) == -0.0);
789 try std.testing.expect(isNan(sqrt(-1.0)));
790 try std.testing.expect(isNan(sqrt(std.math.nan(f64))));
791}
792
793fn sqrtf(x: f32) callconv(.C) f32 {
794 const tiny: f32 = 1.0e-30;
795 const sign: i32 = @bitCast(i32, @as(u32, 0x80000000));
796 var ix: i32 = @bitCast(i32, x);
797
798 if ((ix & 0x7F800000) == 0x7F800000) {
799 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
800 }
801
802 // zero
803 if (ix <= 0) {
804 if (ix & ~sign == 0) {
805 return x; // sqrt (+-0) = +-0
806 }
807 if (ix < 0) {
808 return math.snan(f32);
809 }
810 }
811
812 // normalize
813 var m = ix >> 23;
814 if (m == 0) {
815 // subnormal
816 var i: i32 = 0;
817 while (ix & 0x00800000 == 0) : (i += 1) {
818 ix <<= 1;
819 }
820 m -= i - 1;
821 }
822
823 m -= 127; // unbias exponent
824 ix = (ix & 0x007FFFFF) | 0x00800000;
825
826 if (m & 1 != 0) { // odd m, double x to even
827 ix += ix;
828 }
829
830 m >>= 1; // m = [m / 2]
831
832 // sqrt(x) bit by bit
833 ix += ix;
834 var q: i32 = 0; // q = sqrt(x)
835 var s: i32 = 0;
836 var r: i32 = 0x01000000; // r = moving bit right -> left
837
838 while (r != 0) {
839 const t = s + r;
840 if (t <= ix) {
841 s = t + r;
842 ix -= t;
843 q += r;
844 }
845 ix += ix;
846 r >>= 1;
847 }
848
849 // floating add to find rounding direction
850 if (ix != 0) {
851 var z = 1.0 - tiny; // inexact
852 if (z >= 1.0) {
853 z = 1.0 + tiny;
854 if (z > 1.0) {
855 q += 2;
856 } else {
857 if (q & 1 != 0) {
858 q += 1;
859 }
860 }
861 }
862 }
863
864 ix = (q >> 1) + 0x3f000000;
865 ix += m << 23;
866 return @bitCast(f32, ix);
867}
868
869test "sqrtf" {
870 const V = [_]f32{
871 0.0,
872 4.089288054930154,
873 7.538757127071935,
874 8.97780793672623,
875 5.304443821913729,
876 5.682408965311888,
877 0.5846878579110049,
878 3.650338664297043,
879 0.3178091951800732,
880 7.1505232436382835,
881 3.6589165881946464,
882 };
883
884 // Note that @sqrt will either generate the sqrt opcode (if supported by the
885 // target ISA) or a call to `sqrtf` otherwise.
886 for (V) |val|
887 try std.testing.expectEqual(@sqrt(val), sqrtf(val));
888}
889
890test "sqrtf special" {
891 try std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32))));
892 try std.testing.expect(sqrtf(0.0) == 0.0);
893 try std.testing.expect(sqrtf(-0.0) == -0.0);
894 try std.testing.expect(isNan(sqrtf(-1.0)));
895 try std.testing.expect(isNan(sqrtf(std.math.nan(f32))));
896}
897
898307// TODO we should be able to put this directly in std/linux/x86_64.zig but
899308// it causes a segfault in release mode. this is a workaround of calling it
900309// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
lib/std/special/compiler_rt.zig+62-93
......@@ -8,6 +8,7 @@ const abi = builtin.abi;
88const is_gnu = abi.isGnu();
99const is_mingw = os_tag == .windows and is_gnu;
1010const is_darwin = std.Target.Os.Tag.isDarwin(os_tag);
11const is_ppc = arch.isPPC() or arch.isPPC64();
1112
1213const linkage = if (is_test)
1314 std.builtin.GlobalLinkage.Internal
......@@ -19,9 +20,6 @@ const strong_linkage = if (is_test)
1920else
2021 std.builtin.GlobalLinkage.Strong;
2122
22const long_double_is_f80 = builtin.target.longDoubleIs(f80);
23const long_double_is_f128 = builtin.target.longDoubleIs(f128);
24
2523comptime {
2624 // These files do their own comptime exporting logic.
2725 _ = @import("compiler_rt/atomics.zig");
......@@ -726,42 +724,25 @@ comptime {
726724 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
727725 }
728726
729 if (!is_test) {
730 if (long_double_is_f80) {
731 @export(fmodx, .{ .name = "fmodl", .linkage = linkage });
732 } else if (long_double_is_f128) {
733 @export(fmodq, .{ .name = "fmodl", .linkage = linkage });
734 } else {
735 @export(fmodl, .{ .name = "fmodl", .linkage = linkage });
736 }
737 if (long_double_is_f80 or builtin.zig_backend == .stage1) {
738 // TODO: https://github.com/ziglang/zig/issues/11161
739 @export(fmodx, .{ .name = "fmodx", .linkage = linkage });
740 }
741 @export(fmodq, .{ .name = "fmodq", .linkage = linkage });
742
743 @export(floorf, .{ .name = "floorf", .linkage = linkage });
744 @export(floor, .{ .name = "floor", .linkage = linkage });
745 @export(floorl, .{ .name = "floorl", .linkage = linkage });
746
747 @export(ceilf, .{ .name = "ceilf", .linkage = linkage });
748 @export(ceil, .{ .name = "ceil", .linkage = linkage });
749 @export(ceill, .{ .name = "ceill", .linkage = linkage });
750
751 @export(fma, .{ .name = "fma", .linkage = linkage });
752 @export(fmaf, .{ .name = "fmaf", .linkage = linkage });
753 @export(fmal, .{ .name = "fmal", .linkage = linkage });
754 if (long_double_is_f80) {
755 @export(fmal, .{ .name = "__fmax", .linkage = linkage });
756 } else {
757 @export(__fmax, .{ .name = "__fmax", .linkage = linkage });
758 }
759 if (long_double_is_f128) {
760 @export(fmal, .{ .name = "fmaq", .linkage = linkage });
761 } else {
762 @export(fmaq, .{ .name = "fmaq", .linkage = linkage });
763 }
764 }
727 mathExport("ceil", @import("./compiler_rt/ceil.zig"), true);
728 mathExport("cos", @import("./compiler_rt/cos.zig"), true);
729 mathExport("exp", @import("./compiler_rt/exp.zig"), true);
730 mathExport("exp2", @import("./compiler_rt/exp2.zig"), true);
731 mathExport("fabs", @import("./compiler_rt/fabs.zig"), true);
732 mathExport("floor", @import("./compiler_rt/floor.zig"), true);
733 mathExport("fma", @import("./compiler_rt/fma.zig"), true);
734 mathExport("fmax", @import("./compiler_rt/fmax.zig"), true);
735 mathExport("fmin", @import("./compiler_rt/fmin.zig"), true);
736 mathExport("fmod", @import("./compiler_rt/fmod.zig"), true);
737 mathExport("log", @import("./compiler_rt/log.zig"), true);
738 mathExport("log10", @import("./compiler_rt/log10.zig"), true);
739 mathExport("log2", @import("./compiler_rt/log2.zig"), true);
740 mathExport("round", @import("./compiler_rt/round.zig"), true);
741 mathExport("sin", @import("./compiler_rt/sin.zig"), true);
742 mathExport("sincos", @import("./compiler_rt/sincos.zig"), true);
743 mathExport("sqrt", @import("./compiler_rt/sqrt.zig"), true);
744 mathExport("tan", @import("./compiler_rt/tan.zig"), false);
745 mathExport("trunc", @import("./compiler_rt/trunc.zig"), true);
765746
766747 if (arch.isSPARC()) {
767748 // SPARC systems use a different naming scheme
......@@ -815,7 +796,7 @@ comptime {
815796 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
816797 }
817798
818 if ((arch.isPPC() or arch.isPPC64()) and !is_test) {
799 if (is_ppc and !is_test) {
819800 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });
820801 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });
821802 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });
......@@ -840,65 +821,53 @@ comptime {
840821 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });
841822 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });
842823 @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage });
843
844 // LLVM PPC backend lowers f128 fma to `fmaf128`.
845 @export(fmal, .{ .name = "fmaf128", .linkage = linkage });
846 }
847}
848
849const math = std.math;
850
851fn fmaf(a: f32, b: f32, c: f32) callconv(.C) f32 {
852 return math.fma(f32, a, b, c);
853}
854fn fma(a: f64, b: f64, c: f64) callconv(.C) f64 {
855 return math.fma(f64, a, b, c);
856}
857fn __fmax(a: f80, b: f80, c: f80) callconv(.C) f80 {
858 return math.fma(f80, a, b, c);
859}
860fn fmaq(a: f128, b: f128, c: f128) callconv(.C) f128 {
861 return math.fma(f128, a, b, c);
862}
863fn fmal(a: c_longdouble, b: c_longdouble, c: c_longdouble) callconv(.C) c_longdouble {
864 return math.fma(c_longdouble, a, b, c);
865}
866
867// TODO add intrinsics for these (and probably the double version too)
868// and have the math stuff use the intrinsic. same as @mod and @rem
869fn floorf(x: f32) callconv(.C) f32 {
870 return math.floor(x);
871}
872fn floor(x: f64) callconv(.C) f64 {
873 return math.floor(x);
874}
875fn floorl(x: c_longdouble) callconv(.C) c_longdouble {
876 if (!long_double_is_f128) {
877 @panic("TODO implement this");
878824 }
879 return math.floor(x);
880825}
881826
882fn ceilf(x: f32) callconv(.C) f32 {
883 return math.ceil(x);
884}
885fn ceil(x: f64) callconv(.C) f64 {
886 return math.ceil(x);
887}
888fn ceill(x: c_longdouble) callconv(.C) c_longdouble {
889 if (!long_double_is_f128) {
890 @panic("TODO implement this");
827inline fn mathExport(double_name: []const u8, comptime import: type, is_standard: bool) void {
828 const half_name = "__" ++ double_name ++ "h";
829 const half_fn = @field(import, half_name);
830 const float_name = double_name ++ "f";
831 const float_fn = @field(import, float_name);
832 const double_fn = @field(import, double_name);
833 const long_double_name = double_name ++ "l";
834 const xf80_name = "__" ++ double_name ++ "x";
835 const xf80_fn = @field(import, xf80_name);
836 const quad_name = double_name ++ "q";
837 const quad_fn = @field(import, quad_name);
838
839 @export(half_fn, .{ .name = half_name, .linkage = linkage });
840 @export(float_fn, .{ .name = float_name, .linkage = linkage });
841 @export(double_fn, .{ .name = double_name, .linkage = linkage });
842 @export(xf80_fn, .{ .name = xf80_name, .linkage = linkage });
843 @export(quad_fn, .{ .name = quad_name, .linkage = linkage });
844
845 if (is_test) return;
846
847 const pairs = .{
848 .{ f16, half_fn },
849 .{ f32, float_fn },
850 .{ f64, double_fn },
851 .{ f80, xf80_fn },
852 .{ f128, quad_fn },
853 };
854
855 // Weak aliases don't work on Windows, so we avoid exporting the `l` alias
856 // on this platform for functions we know will collide.
857 if (builtin.os.tag != .windows or !builtin.link_libc or !is_standard) {
858 inline for (pairs) |pair| {
859 const F = pair[0];
860 const func = pair[1];
861 if (builtin.target.longDoubleIs(F)) {
862 @export(func, .{ .name = long_double_name, .linkage = linkage });
863 }
864 }
891865 }
892 return math.ceil(x);
893}
894866
895const fmodq = @import("compiler_rt/fmodq.zig").fmodq;
896const fmodx = @import("compiler_rt/fmodx.zig").fmodx;
897fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble {
898 if (!long_double_is_f128) {
899 @panic("TODO implement this");
867 if (is_ppc and is_standard) {
868 // LLVM PPC backend lowers f128 ops with the suffix `f128` instead of `l`.
869 @export(quad_fn, .{ .name = double_name ++ "f128", .linkage = linkage });
900870 }
901 return @floatCast(c_longdouble, fmodq(x, y));
902871}
903872
904873// Avoid dragging in the runtime safety mechanisms into this .o file,
lib/std/special/compiler_rt/ceil.zig created+154
......@@ -0,0 +1,154 @@
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/ceilf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10
11pub fn __ceilh(x: f16) callconv(.C) f16 {
12 // TODO: more efficient implementation
13 return @floatCast(f16, ceilf(x));
14}
15
16pub fn ceilf(x: f32) callconv(.C) f32 {
17 var u = @bitCast(u32, x);
18 var e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F;
19 var m: u32 = undefined;
20
21 // TODO: Shouldn't need this explicit check.
22 if (x == 0.0) {
23 return x;
24 }
25
26 if (e >= 23) {
27 return x;
28 } else if (e >= 0) {
29 m = @as(u32, 0x007FFFFF) >> @intCast(u5, e);
30 if (u & m == 0) {
31 return x;
32 }
33 math.doNotOptimizeAway(x + 0x1.0p120);
34 if (u >> 31 == 0) {
35 u += m;
36 }
37 u &= ~m;
38 return @bitCast(f32, u);
39 } else {
40 math.doNotOptimizeAway(x + 0x1.0p120);
41 if (u >> 31 != 0) {
42 return -0.0;
43 } else {
44 return 1.0;
45 }
46 }
47}
48
49pub fn ceil(x: f64) callconv(.C) f64 {
50 const f64_toint = 1.0 / math.floatEps(f64);
51
52 const u = @bitCast(u64, x);
53 const e = (u >> 52) & 0x7FF;
54 var y: f64 = undefined;
55
56 if (e >= 0x3FF + 52 or x == 0) {
57 return x;
58 }
59
60 if (u >> 63 != 0) {
61 y = x - f64_toint + f64_toint - x;
62 } else {
63 y = x + f64_toint - f64_toint - x;
64 }
65
66 if (e <= 0x3FF - 1) {
67 math.doNotOptimizeAway(y);
68 if (u >> 63 != 0) {
69 return -0.0;
70 } else {
71 return 1.0;
72 }
73 } else if (y < 0) {
74 return x + y + 1;
75 } else {
76 return x + y;
77 }
78}
79
80pub fn __ceilx(x: f80) callconv(.C) f80 {
81 // TODO: more efficient implementation
82 return @floatCast(f80, ceilq(x));
83}
84
85pub fn ceilq(x: f128) callconv(.C) f128 {
86 const f128_toint = 1.0 / math.floatEps(f128);
87
88 const u = @bitCast(u128, x);
89 const e = (u >> 112) & 0x7FFF;
90 var y: f128 = undefined;
91
92 if (e >= 0x3FFF + 112 or x == 0) return x;
93
94 if (u >> 127 != 0) {
95 y = x - f128_toint + f128_toint - x;
96 } else {
97 y = x + f128_toint - f128_toint - x;
98 }
99
100 if (e <= 0x3FFF - 1) {
101 math.doNotOptimizeAway(y);
102 if (u >> 127 != 0) {
103 return -0.0;
104 } else {
105 return 1.0;
106 }
107 } else if (y < 0) {
108 return x + y + 1;
109 } else {
110 return x + y;
111 }
112}
113
114test "ceil32" {
115 try expect(ceilf(1.3) == 2.0);
116 try expect(ceilf(-1.3) == -1.0);
117 try expect(ceilf(0.2) == 1.0);
118}
119
120test "ceil64" {
121 try expect(ceil(1.3) == 2.0);
122 try expect(ceil(-1.3) == -1.0);
123 try expect(ceil(0.2) == 1.0);
124}
125
126test "ceil128" {
127 try expect(ceilq(1.3) == 2.0);
128 try expect(ceilq(-1.3) == -1.0);
129 try expect(ceilq(0.2) == 1.0);
130}
131
132test "ceil32.special" {
133 try expect(ceilf(0.0) == 0.0);
134 try expect(ceilf(-0.0) == -0.0);
135 try expect(math.isPositiveInf(ceilf(math.inf(f32))));
136 try expect(math.isNegativeInf(ceilf(-math.inf(f32))));
137 try expect(math.isNan(ceilf(math.nan(f32))));
138}
139
140test "ceil64.special" {
141 try expect(ceil(0.0) == 0.0);
142 try expect(ceil(-0.0) == -0.0);
143 try expect(math.isPositiveInf(ceil(math.inf(f64))));
144 try expect(math.isNegativeInf(ceil(-math.inf(f64))));
145 try expect(math.isNan(ceil(math.nan(f64))));
146}
147
148test "ceil128.special" {
149 try expect(ceilq(0.0) == 0.0);
150 try expect(ceilq(-0.0) == -0.0);
151 try expect(math.isPositiveInf(ceilq(math.inf(f128))));
152 try expect(math.isNegativeInf(ceilq(-math.inf(f128))));
153 try expect(math.isNan(ceilq(math.nan(f128))));
154}
lib/std/special/compiler_rt/cos.zig created+144
......@@ -0,0 +1,144 @@
1const std = @import("std");
2const math = std.math;
3const expect = std.testing.expect;
4
5const trig = @import("trig.zig");
6const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
7const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
8
9pub fn __cosh(a: f16) callconv(.C) f16 {
10 // TODO: more efficient implementation
11 return @floatCast(f16, cosf(a));
12}
13
14pub fn cosf(x: f32) callconv(.C) f32 {
15 // Small multiples of pi/2 rounded to double precision.
16 const c1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
17 const c2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
18 const c3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
19 const c4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
20
21 var ix = @bitCast(u32, x);
22 const sign = ix >> 31 != 0;
23 ix &= 0x7fffffff;
24
25 if (ix <= 0x3f490fda) { // |x| ~<= pi/4
26 if (ix < 0x39800000) { // |x| < 2**-12
27 // raise inexact if x != 0
28 math.doNotOptimizeAway(x + 0x1p120);
29 return 1.0;
30 }
31 return trig.__cosdf(x);
32 }
33 if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4
34 if (ix > 0x4016cbe3) { // |x| ~> 3*pi/4
35 return -trig.__cosdf(if (sign) x + c2pio2 else x - c2pio2);
36 } else {
37 if (sign) {
38 return trig.__sindf(x + c1pio2);
39 } else {
40 return trig.__sindf(c1pio2 - x);
41 }
42 }
43 }
44 if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4
45 if (ix > 0x40afeddf) { // |x| ~> 7*pi/4
46 return trig.__cosdf(if (sign) x + c4pio2 else x - c4pio2);
47 } else {
48 if (sign) {
49 return trig.__sindf(-x - c3pio2);
50 } else {
51 return trig.__sindf(x - c3pio2);
52 }
53 }
54 }
55
56 // cos(Inf or NaN) is NaN
57 if (ix >= 0x7f800000) {
58 return x - x;
59 }
60
61 var y: f64 = undefined;
62 const n = rem_pio2f(x, &y);
63 return switch (n & 3) {
64 0 => trig.__cosdf(y),
65 1 => trig.__sindf(-y),
66 2 => -trig.__cosdf(y),
67 else => trig.__sindf(y),
68 };
69}
70
71pub fn cos(x: f64) callconv(.C) f64 {
72 var ix = @bitCast(u64, x) >> 32;
73 ix &= 0x7fffffff;
74
75 // |x| ~< pi/4
76 if (ix <= 0x3fe921fb) {
77 if (ix < 0x3e46a09e) { // |x| < 2**-27 * sqrt(2)
78 // raise inexact if x!=0
79 math.doNotOptimizeAway(x + 0x1p120);
80 return 1.0;
81 }
82 return trig.__cos(x, 0);
83 }
84
85 // cos(Inf or NaN) is NaN
86 if (ix >= 0x7ff00000) {
87 return x - x;
88 }
89
90 var y: [2]f64 = undefined;
91 const n = rem_pio2(x, &y);
92 return switch (n & 3) {
93 0 => trig.__cos(y[0], y[1]),
94 1 => -trig.__sin(y[0], y[1], 1),
95 2 => -trig.__cos(y[0], y[1]),
96 else => trig.__sin(y[0], y[1], 1),
97 };
98}
99
100pub fn __cosx(a: f80) callconv(.C) f80 {
101 // TODO: more efficient implementation
102 return @floatCast(f80, cosq(a));
103}
104
105pub fn cosq(a: f128) callconv(.C) f128 {
106 // TODO: more correct implementation
107 return cos(@floatCast(f64, a));
108}
109
110test "cos32" {
111 const epsilon = 0.00001;
112
113 try expect(math.approxEqAbs(f32, cosf(0.0), 1.0, epsilon));
114 try expect(math.approxEqAbs(f32, cosf(0.2), 0.980067, epsilon));
115 try expect(math.approxEqAbs(f32, cosf(0.8923), 0.627623, epsilon));
116 try expect(math.approxEqAbs(f32, cosf(1.5), 0.070737, epsilon));
117 try expect(math.approxEqAbs(f32, cosf(-1.5), 0.070737, epsilon));
118 try expect(math.approxEqAbs(f32, cosf(37.45), 0.969132, epsilon));
119 try expect(math.approxEqAbs(f32, cosf(89.123), 0.400798, epsilon));
120}
121
122test "cos64" {
123 const epsilon = 0.000001;
124
125 try expect(math.approxEqAbs(f64, cos(0.0), 1.0, epsilon));
126 try expect(math.approxEqAbs(f64, cos(0.2), 0.980067, epsilon));
127 try expect(math.approxEqAbs(f64, cos(0.8923), 0.627623, epsilon));
128 try expect(math.approxEqAbs(f64, cos(1.5), 0.070737, epsilon));
129 try expect(math.approxEqAbs(f64, cos(-1.5), 0.070737, epsilon));
130 try expect(math.approxEqAbs(f64, cos(37.45), 0.969132, epsilon));
131 try expect(math.approxEqAbs(f64, cos(89.123), 0.40080, epsilon));
132}
133
134test "cos32.special" {
135 try expect(math.isNan(cosf(math.inf(f32))));
136 try expect(math.isNan(cosf(-math.inf(f32))));
137 try expect(math.isNan(cosf(math.nan(f32))));
138}
139
140test "cos64.special" {
141 try expect(math.isNan(cos(math.inf(f64))));
142 try expect(math.isNan(cos(-math.inf(f64))));
143 try expect(math.isNan(cos(math.nan(f64))));
144}
lib/std/special/compiler_rt/divxf3_test.zig+3-3
......@@ -30,9 +30,9 @@ fn test__divxf3(a: f80, b: f80) !void {
3030 const x_minus_eps = @bitCast(f80, (@bitCast(u80, x) - 1) | integerBit);
3131
3232 // Make sure result is more accurate than the adjacent floats
33 const err_x = std.math.fabs(@mulAdd(f80, x, b, -a));
34 const err_x_plus_eps = std.math.fabs(@mulAdd(f80, x_plus_eps, b, -a));
35 const err_x_minus_eps = std.math.fabs(@mulAdd(f80, x_minus_eps, b, -a));
33 const err_x = @fabs(@mulAdd(f80, x, b, -a));
34 const err_x_plus_eps = @fabs(@mulAdd(f80, x_plus_eps, b, -a));
35 const err_x_minus_eps = @fabs(@mulAdd(f80, x_minus_eps, b, -a));
3636
3737 try testing.expect(err_x_minus_eps > err_x);
3838 try testing.expect(err_x_plus_eps > err_x);
lib/std/special/compiler_rt/exp.zig created+213
......@@ -0,0 +1,213 @@
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/expf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/exp.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10
11pub fn __exph(a: f16) callconv(.C) f16 {
12 // TODO: more efficient implementation
13 return @floatCast(f16, expf(a));
14}
15
16pub fn expf(x_: f32) callconv(.C) f32 {
17 const half = [_]f32{ 0.5, -0.5 };
18 const ln2hi = 6.9314575195e-1;
19 const ln2lo = 1.4286067653e-6;
20 const invln2 = 1.4426950216e+0;
21 const P1 = 1.6666625440e-1;
22 const P2 = -2.7667332906e-3;
23
24 var x = x_;
25 var hx = @bitCast(u32, x);
26 const sign = @intCast(i32, hx >> 31);
27 hx &= 0x7FFFFFFF;
28
29 if (math.isNan(x)) {
30 return x;
31 }
32
33 // |x| >= -87.33655 or nan
34 if (hx >= 0x42AEAC50) {
35 // nan
36 if (hx > 0x7F800000) {
37 return x;
38 }
39 // x >= 88.722839
40 if (hx >= 0x42b17218 and sign == 0) {
41 return x * 0x1.0p127;
42 }
43 if (sign != 0) {
44 math.doNotOptimizeAway(-0x1.0p-149 / x); // overflow
45 // x <= -103.972084
46 if (hx >= 0x42CFF1B5) {
47 return 0;
48 }
49 }
50 }
51
52 var k: i32 = undefined;
53 var hi: f32 = undefined;
54 var lo: f32 = undefined;
55
56 // |x| > 0.5 * ln2
57 if (hx > 0x3EB17218) {
58 // |x| > 1.5 * ln2
59 if (hx > 0x3F851592) {
60 k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]);
61 } else {
62 k = 1 - sign - sign;
63 }
64
65 const fk = @intToFloat(f32, k);
66 hi = x - fk * ln2hi;
67 lo = fk * ln2lo;
68 x = hi - lo;
69 }
70 // |x| > 2^(-14)
71 else if (hx > 0x39000000) {
72 k = 0;
73 hi = x;
74 lo = 0;
75 } else {
76 math.doNotOptimizeAway(0x1.0p127 + x); // inexact
77 return 1 + x;
78 }
79
80 const xx = x * x;
81 const c = x - xx * (P1 + xx * P2);
82 const y = 1 + (x * c / (2 - c) - lo + hi);
83
84 if (k == 0) {
85 return y;
86 } else {
87 return math.scalbn(y, k);
88 }
89}
90
91pub fn exp(x_: f64) callconv(.C) f64 {
92 const half = [_]f64{ 0.5, -0.5 };
93 const ln2hi: f64 = 6.93147180369123816490e-01;
94 const ln2lo: f64 = 1.90821492927058770002e-10;
95 const invln2: f64 = 1.44269504088896338700e+00;
96 const P1: f64 = 1.66666666666666019037e-01;
97 const P2: f64 = -2.77777777770155933842e-03;
98 const P3: f64 = 6.61375632143793436117e-05;
99 const P4: f64 = -1.65339022054652515390e-06;
100 const P5: f64 = 4.13813679705723846039e-08;
101
102 var x = x_;
103 var ux = @bitCast(u64, x);
104 var hx = ux >> 32;
105 const sign = @intCast(i32, hx >> 31);
106 hx &= 0x7FFFFFFF;
107
108 if (math.isNan(x)) {
109 return x;
110 }
111
112 // |x| >= 708.39 or nan
113 if (hx >= 0x4086232B) {
114 // nan
115 if (hx > 0x7FF00000) {
116 return x;
117 }
118 if (x > 709.782712893383973096) {
119 // overflow if x != inf
120 if (!math.isInf(x)) {
121 math.raiseOverflow();
122 }
123 return math.inf(f64);
124 }
125 if (x < -708.39641853226410622) {
126 // underflow if x != -inf
127 // math.doNotOptimizeAway(@as(f32, -0x1.0p-149 / x));
128 if (x < -745.13321910194110842) {
129 return 0;
130 }
131 }
132 }
133
134 // argument reduction
135 var k: i32 = undefined;
136 var hi: f64 = undefined;
137 var lo: f64 = undefined;
138
139 // |x| > 0.5 * ln2
140 if (hx > 0x3FD62E42) {
141 // |x| >= 1.5 * ln2
142 if (hx > 0x3FF0A2B2) {
143 k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]);
144 } else {
145 k = 1 - sign - sign;
146 }
147
148 const dk = @intToFloat(f64, k);
149 hi = x - dk * ln2hi;
150 lo = dk * ln2lo;
151 x = hi - lo;
152 }
153 // |x| > 2^(-28)
154 else if (hx > 0x3E300000) {
155 k = 0;
156 hi = x;
157 lo = 0;
158 } else {
159 // inexact if x != 0
160 // math.doNotOptimizeAway(0x1.0p1023 + x);
161 return 1 + x;
162 }
163
164 const xx = x * x;
165 const c = x - xx * (P1 + xx * (P2 + xx * (P3 + xx * (P4 + xx * P5))));
166 const y = 1 + (x * c / (2 - c) - lo + hi);
167
168 if (k == 0) {
169 return y;
170 } else {
171 return math.scalbn(y, k);
172 }
173}
174
175pub fn __expx(a: f80) callconv(.C) f80 {
176 // TODO: more efficient implementation
177 return @floatCast(f80, expq(a));
178}
179
180pub fn expq(a: f128) callconv(.C) f128 {
181 // TODO: more correct implementation
182 return exp(@floatCast(f64, a));
183}
184
185test "exp32" {
186 const epsilon = 0.000001;
187
188 try expect(expf(0.0) == 1.0);
189 try expect(math.approxEqAbs(f32, expf(0.0), 1.0, epsilon));
190 try expect(math.approxEqAbs(f32, expf(0.2), 1.221403, epsilon));
191 try expect(math.approxEqAbs(f32, expf(0.8923), 2.440737, epsilon));
192 try expect(math.approxEqAbs(f32, expf(1.5), 4.481689, epsilon));
193}
194
195test "exp64" {
196 const epsilon = 0.000001;
197
198 try expect(exp(0.0) == 1.0);
199 try expect(math.approxEqAbs(f64, exp(0.0), 1.0, epsilon));
200 try expect(math.approxEqAbs(f64, exp(0.2), 1.221403, epsilon));
201 try expect(math.approxEqAbs(f64, exp(0.8923), 2.440737, epsilon));
202 try expect(math.approxEqAbs(f64, exp(1.5), 4.481689, epsilon));
203}
204
205test "exp32.special" {
206 try expect(math.isPositiveInf(expf(math.inf(f32))));
207 try expect(math.isNan(expf(math.nan(f32))));
208}
209
210test "exp64.special" {
211 try expect(math.isPositiveInf(exp(math.inf(f64))));
212 try expect(math.isNan(exp(math.nan(f64))));
213}
lib/std/special/compiler_rt/exp2.zig created+461
......@@ -0,0 +1,461 @@
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/exp2f.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/exp2.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10
11pub fn __exp2h(x: f16) callconv(.C) f16 {
12 // TODO: more efficient implementation
13 return @floatCast(f16, exp2f(x));
14}
15
16pub fn exp2f(x: f32) callconv(.C) f32 {
17 const tblsiz = @intCast(u32, exp2ft.len);
18 const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz);
19 const P1: f32 = 0x1.62e430p-1;
20 const P2: f32 = 0x1.ebfbe0p-3;
21 const P3: f32 = 0x1.c6b348p-5;
22 const P4: f32 = 0x1.3b2c9cp-7;
23
24 var u = @bitCast(u32, x);
25 const ix = u & 0x7FFFFFFF;
26
27 // |x| > 126
28 if (ix > 0x42FC0000) {
29 // nan
30 if (ix > 0x7F800000) {
31 return x;
32 }
33 // x >= 128
34 if (u >= 0x43000000 and u < 0x80000000) {
35 return x * 0x1.0p127;
36 }
37 // x < -126
38 if (u >= 0x80000000) {
39 if (u >= 0xC3160000 or u & 0x000FFFF != 0) {
40 math.doNotOptimizeAway(-0x1.0p-149 / x);
41 }
42 // x <= -150
43 if (u >= 0x3160000) {
44 return 0;
45 }
46 }
47 }
48 // |x| <= 0x1p-25
49 else if (ix <= 0x33000000) {
50 return 1.0 + x;
51 }
52
53 // NOTE: musl relies on unsafe behaviours which are replicated below
54 // (addition/bit-shift overflow). Appears that this produces the
55 // intended result but should confirm how GCC/Clang handle this to ensure.
56
57 var uf = x + redux;
58 var i_0 = @bitCast(u32, uf);
59 i_0 +%= tblsiz / 2;
60
61 const k = i_0 / tblsiz;
62 const uk = @bitCast(f64, @as(u64, 0x3FF + k) << 52);
63 i_0 &= tblsiz - 1;
64 uf -= redux;
65
66 const z: f64 = x - uf;
67 var r: f64 = exp2ft[@intCast(usize, i_0)];
68 const t: f64 = r * z;
69 r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4);
70 return @floatCast(f32, r * uk);
71}
72
73pub fn exp2(x: f64) callconv(.C) f64 {
74 const tblsiz: u32 = @intCast(u32, exp2dt.len / 2);
75 const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz);
76 const P1: f64 = 0x1.62e42fefa39efp-1;
77 const P2: f64 = 0x1.ebfbdff82c575p-3;
78 const P3: f64 = 0x1.c6b08d704a0a6p-5;
79 const P4: f64 = 0x1.3b2ab88f70400p-7;
80 const P5: f64 = 0x1.5d88003875c74p-10;
81
82 const ux = @bitCast(u64, x);
83 const ix = @intCast(u32, ux >> 32) & 0x7FFFFFFF;
84
85 // TODO: This should be handled beneath.
86 if (math.isNan(x)) {
87 return math.nan(f64);
88 }
89
90 // |x| >= 1022 or nan
91 if (ix >= 0x408FF000) {
92 // x >= 1024 or nan
93 if (ix >= 0x40900000 and ux >> 63 == 0) {
94 math.raiseOverflow();
95 return math.inf(f64);
96 }
97 // -inf or -nan
98 if (ix >= 0x7FF00000) {
99 return -1 / x;
100 }
101 // x <= -1022
102 if (ux >> 63 != 0) {
103 // underflow
104 if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) {
105 math.doNotOptimizeAway(@floatCast(f32, -0x1.0p-149 / x));
106 }
107 if (x <= -1075) {
108 return 0;
109 }
110 }
111 }
112 // |x| < 0x1p-54
113 else if (ix < 0x3C900000) {
114 return 1.0 + x;
115 }
116
117 // NOTE: musl relies on unsafe behaviours which are replicated below
118 // (addition overflow, division truncation, casting). Appears that this
119 // produces the intended result but should confirm how GCC/Clang handle this
120 // to ensure.
121
122 // reduce x
123 var uf: f64 = x + redux;
124 // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here
125 var i_0: u32 = @truncate(u32, @bitCast(u64, uf));
126 i_0 +%= tblsiz / 2;
127
128 const k: u32 = i_0 / tblsiz * tblsiz;
129 const ik: i32 = @divTrunc(@bitCast(i32, k), tblsiz);
130 i_0 %= tblsiz;
131 uf -= redux;
132
133 // r = exp2(y) = exp2t[i_0] * p(z - eps[i])
134 var z: f64 = x - uf;
135 const t: f64 = exp2dt[@intCast(usize, 2 * i_0)];
136 z -= exp2dt[@intCast(usize, 2 * i_0 + 1)];
137 const r: f64 = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5))));
138
139 return math.scalbn(r, ik);
140}
141
142pub fn __exp2x(x: f80) callconv(.C) f80 {
143 // TODO: more efficient implementation
144 return @floatCast(f80, exp2q(x));
145}
146
147pub fn exp2q(x: f128) callconv(.C) f128 {
148 // TODO: more correct implementation
149 return exp2(@floatCast(f64, x));
150}
151
152const exp2ft = [_]f64{
153 0x1.6a09e667f3bcdp-1,
154 0x1.7a11473eb0187p-1,
155 0x1.8ace5422aa0dbp-1,
156 0x1.9c49182a3f090p-1,
157 0x1.ae89f995ad3adp-1,
158 0x1.c199bdd85529cp-1,
159 0x1.d5818dcfba487p-1,
160 0x1.ea4afa2a490dap-1,
161 0x1.0000000000000p+0,
162 0x1.0b5586cf9890fp+0,
163 0x1.172b83c7d517bp+0,
164 0x1.2387a6e756238p+0,
165 0x1.306fe0a31b715p+0,
166 0x1.3dea64c123422p+0,
167 0x1.4bfdad5362a27p+0,
168 0x1.5ab07dd485429p+0,
169};
170
171const exp2dt = [_]f64{
172 // exp2(z + eps) eps
173 0x1.6a09e667f3d5dp-1, 0x1.9880p-44,
174 0x1.6b052fa751744p-1, 0x1.8000p-50,
175 0x1.6c012750bd9fep-1, -0x1.8780p-45,
176 0x1.6cfdcddd476bfp-1, 0x1.ec00p-46,
177 0x1.6dfb23c651a29p-1, -0x1.8000p-50,
178 0x1.6ef9298593ae3p-1, -0x1.c000p-52,
179 0x1.6ff7df9519386p-1, -0x1.fd80p-45,
180 0x1.70f7466f42da3p-1, -0x1.c880p-45,
181 0x1.71f75e8ec5fc3p-1, 0x1.3c00p-46,
182 0x1.72f8286eacf05p-1, -0x1.8300p-44,
183 0x1.73f9a48a58152p-1, -0x1.0c00p-47,
184 0x1.74fbd35d7ccfcp-1, 0x1.f880p-45,
185 0x1.75feb564267f1p-1, 0x1.3e00p-47,
186 0x1.77024b1ab6d48p-1, -0x1.7d00p-45,
187 0x1.780694fde5d38p-1, -0x1.d000p-50,
188 0x1.790b938ac1d00p-1, 0x1.3000p-49,
189 0x1.7a11473eb0178p-1, -0x1.d000p-49,
190 0x1.7b17b0976d060p-1, 0x1.0400p-45,
191 0x1.7c1ed0130c133p-1, 0x1.0000p-53,
192 0x1.7d26a62ff8636p-1, -0x1.6900p-45,
193 0x1.7e2f336cf4e3bp-1, -0x1.2e00p-47,
194 0x1.7f3878491c3e8p-1, -0x1.4580p-45,
195 0x1.80427543e1b4ep-1, 0x1.3000p-44,
196 0x1.814d2add1071ap-1, 0x1.f000p-47,
197 0x1.82589994ccd7ep-1, -0x1.1c00p-45,
198 0x1.8364c1eb942d0p-1, 0x1.9d00p-45,
199 0x1.8471a4623cab5p-1, 0x1.7100p-43,
200 0x1.857f4179f5bbcp-1, 0x1.2600p-45,
201 0x1.868d99b4491afp-1, -0x1.2c40p-44,
202 0x1.879cad931a395p-1, -0x1.3000p-45,
203 0x1.88ac7d98a65b8p-1, -0x1.a800p-45,
204 0x1.89bd0a4785800p-1, -0x1.d000p-49,
205 0x1.8ace5422aa223p-1, 0x1.3280p-44,
206 0x1.8be05bad619fap-1, 0x1.2b40p-43,
207 0x1.8cf3216b54383p-1, -0x1.ed00p-45,
208 0x1.8e06a5e08664cp-1, -0x1.0500p-45,
209 0x1.8f1ae99157807p-1, 0x1.8280p-45,
210 0x1.902fed0282c0ep-1, -0x1.cb00p-46,
211 0x1.9145b0b91ff96p-1, -0x1.5e00p-47,
212 0x1.925c353aa2ff9p-1, 0x1.5400p-48,
213 0x1.93737b0cdc64ap-1, 0x1.7200p-46,
214 0x1.948b82b5f98aep-1, -0x1.9000p-47,
215 0x1.95a44cbc852cbp-1, 0x1.5680p-45,
216 0x1.96bdd9a766f21p-1, -0x1.6d00p-44,
217 0x1.97d829fde4e2ap-1, -0x1.1000p-47,
218 0x1.98f33e47a23a3p-1, 0x1.d000p-45,
219 0x1.9a0f170ca0604p-1, -0x1.8a40p-44,
220 0x1.9b2bb4d53ff89p-1, 0x1.55c0p-44,
221 0x1.9c49182a3f15bp-1, 0x1.6b80p-45,
222 0x1.9d674194bb8c5p-1, -0x1.c000p-49,
223 0x1.9e86319e3238ep-1, 0x1.7d00p-46,
224 0x1.9fa5e8d07f302p-1, 0x1.6400p-46,
225 0x1.a0c667b5de54dp-1, -0x1.5000p-48,
226 0x1.a1e7aed8eb8f6p-1, 0x1.9e00p-47,
227 0x1.a309bec4a2e27p-1, 0x1.ad80p-45,
228 0x1.a42c980460a5dp-1, -0x1.af00p-46,
229 0x1.a5503b23e259bp-1, 0x1.b600p-47,
230 0x1.a674a8af46213p-1, 0x1.8880p-44,
231 0x1.a799e1330b3a7p-1, 0x1.1200p-46,
232 0x1.a8bfe53c12e8dp-1, 0x1.6c00p-47,
233 0x1.a9e6b5579fcd2p-1, -0x1.9b80p-45,
234 0x1.ab0e521356fb8p-1, 0x1.b700p-45,
235 0x1.ac36bbfd3f381p-1, 0x1.9000p-50,
236 0x1.ad5ff3a3c2780p-1, 0x1.4000p-49,
237 0x1.ae89f995ad2a3p-1, -0x1.c900p-45,
238 0x1.afb4ce622f367p-1, 0x1.6500p-46,
239 0x1.b0e07298db790p-1, 0x1.fd40p-45,
240 0x1.b20ce6c9a89a9p-1, 0x1.2700p-46,
241 0x1.b33a2b84f1a4bp-1, 0x1.d470p-43,
242 0x1.b468415b747e7p-1, -0x1.8380p-44,
243 0x1.b59728de5593ap-1, 0x1.8000p-54,
244 0x1.b6c6e29f1c56ap-1, 0x1.ad00p-47,
245 0x1.b7f76f2fb5e50p-1, 0x1.e800p-50,
246 0x1.b928cf22749b2p-1, -0x1.4c00p-47,
247 0x1.ba5b030a10603p-1, -0x1.d700p-47,
248 0x1.bb8e0b79a6f66p-1, 0x1.d900p-47,
249 0x1.bcc1e904bc1ffp-1, 0x1.2a00p-47,
250 0x1.bdf69c3f3a16fp-1, -0x1.f780p-46,
251 0x1.bf2c25bd71db8p-1, -0x1.0a00p-46,
252 0x1.c06286141b2e9p-1, -0x1.1400p-46,
253 0x1.c199bdd8552e0p-1, 0x1.be00p-47,
254 0x1.c2d1cd9fa64eep-1, -0x1.9400p-47,
255 0x1.c40ab5fffd02fp-1, -0x1.ed00p-47,
256 0x1.c544778fafd15p-1, 0x1.9660p-44,
257 0x1.c67f12e57d0cbp-1, -0x1.a100p-46,
258 0x1.c7ba88988c1b6p-1, -0x1.8458p-42,
259 0x1.c8f6d9406e733p-1, -0x1.a480p-46,
260 0x1.ca3405751c4dfp-1, 0x1.b000p-51,
261 0x1.cb720dcef9094p-1, 0x1.1400p-47,
262 0x1.ccb0f2e6d1689p-1, 0x1.0200p-48,
263 0x1.cdf0b555dc412p-1, 0x1.3600p-48,
264 0x1.cf3155b5bab3bp-1, -0x1.6900p-47,
265 0x1.d072d4a0789bcp-1, 0x1.9a00p-47,
266 0x1.d1b532b08c8fap-1, -0x1.5e00p-46,
267 0x1.d2f87080d8a85p-1, 0x1.d280p-46,
268 0x1.d43c8eacaa203p-1, 0x1.1a00p-47,
269 0x1.d5818dcfba491p-1, 0x1.f000p-50,
270 0x1.d6c76e862e6a1p-1, -0x1.3a00p-47,
271 0x1.d80e316c9834ep-1, -0x1.cd80p-47,
272 0x1.d955d71ff6090p-1, 0x1.4c00p-48,
273 0x1.da9e603db32aep-1, 0x1.f900p-48,
274 0x1.dbe7cd63a8325p-1, 0x1.9800p-49,
275 0x1.dd321f301b445p-1, -0x1.5200p-48,
276 0x1.de7d5641c05bfp-1, -0x1.d700p-46,
277 0x1.dfc97337b9aecp-1, -0x1.6140p-46,
278 0x1.e11676b197d5ep-1, 0x1.b480p-47,
279 0x1.e264614f5a3e7p-1, 0x1.0ce0p-43,
280 0x1.e3b333b16ee5cp-1, 0x1.c680p-47,
281 0x1.e502ee78b3fb4p-1, -0x1.9300p-47,
282 0x1.e653924676d68p-1, -0x1.5000p-49,
283 0x1.e7a51fbc74c44p-1, -0x1.7f80p-47,
284 0x1.e8f7977cdb726p-1, -0x1.3700p-48,
285 0x1.ea4afa2a490e8p-1, 0x1.5d00p-49,
286 0x1.eb9f4867ccae4p-1, 0x1.61a0p-46,
287 0x1.ecf482d8e680dp-1, 0x1.5500p-48,
288 0x1.ee4aaa2188514p-1, 0x1.6400p-51,
289 0x1.efa1bee615a13p-1, -0x1.e800p-49,
290 0x1.f0f9c1cb64106p-1, -0x1.a880p-48,
291 0x1.f252b376bb963p-1, -0x1.c900p-45,
292 0x1.f3ac948dd7275p-1, 0x1.a000p-53,
293 0x1.f50765b6e4524p-1, -0x1.4f00p-48,
294 0x1.f6632798844fdp-1, 0x1.a800p-51,
295 0x1.f7bfdad9cbe38p-1, 0x1.abc0p-48,
296 0x1.f91d802243c82p-1, -0x1.4600p-50,
297 0x1.fa7c1819e908ep-1, -0x1.b0c0p-47,
298 0x1.fbdba3692d511p-1, -0x1.0e00p-51,
299 0x1.fd3c22b8f7194p-1, -0x1.0de8p-46,
300 0x1.fe9d96b2a23eep-1, 0x1.e430p-49,
301 0x1.0000000000000p+0, 0x0.0000p+0,
302 0x1.00b1afa5abcbep+0, -0x1.3400p-52,
303 0x1.0163da9fb3303p+0, -0x1.2170p-46,
304 0x1.02168143b0282p+0, 0x1.a400p-52,
305 0x1.02c9a3e77806cp+0, 0x1.f980p-49,
306 0x1.037d42e11bbcap+0, -0x1.7400p-51,
307 0x1.04315e86e7f89p+0, 0x1.8300p-50,
308 0x1.04e5f72f65467p+0, -0x1.a3f0p-46,
309 0x1.059b0d315855ap+0, -0x1.2840p-47,
310 0x1.0650a0e3c1f95p+0, 0x1.1600p-48,
311 0x1.0706b29ddf71ap+0, 0x1.5240p-46,
312 0x1.07bd42b72a82dp+0, -0x1.9a00p-49,
313 0x1.0874518759bd0p+0, 0x1.6400p-49,
314 0x1.092bdf66607c8p+0, -0x1.0780p-47,
315 0x1.09e3ecac6f383p+0, -0x1.8000p-54,
316 0x1.0a9c79b1f3930p+0, 0x1.fa00p-48,
317 0x1.0b5586cf988fcp+0, -0x1.ac80p-48,
318 0x1.0c0f145e46c8ap+0, 0x1.9c00p-50,
319 0x1.0cc922b724816p+0, 0x1.5200p-47,
320 0x1.0d83b23395dd8p+0, -0x1.ad00p-48,
321 0x1.0e3ec32d3d1f3p+0, 0x1.bac0p-46,
322 0x1.0efa55fdfa9a6p+0, -0x1.4e80p-47,
323 0x1.0fb66affed2f0p+0, -0x1.d300p-47,
324 0x1.1073028d7234bp+0, 0x1.1500p-48,
325 0x1.11301d0125b5bp+0, 0x1.c000p-49,
326 0x1.11edbab5e2af9p+0, 0x1.6bc0p-46,
327 0x1.12abdc06c31d5p+0, 0x1.8400p-49,
328 0x1.136a814f2047dp+0, -0x1.ed00p-47,
329 0x1.1429aaea92de9p+0, 0x1.8e00p-49,
330 0x1.14e95934f3138p+0, 0x1.b400p-49,
331 0x1.15a98c8a58e71p+0, 0x1.5300p-47,
332 0x1.166a45471c3dfp+0, 0x1.3380p-47,
333 0x1.172b83c7d5211p+0, 0x1.8d40p-45,
334 0x1.17ed48695bb9fp+0, -0x1.5d00p-47,
335 0x1.18af9388c8d93p+0, -0x1.c880p-46,
336 0x1.1972658375d66p+0, 0x1.1f00p-46,
337 0x1.1a35beb6fcba7p+0, 0x1.0480p-46,
338 0x1.1af99f81387e3p+0, -0x1.7390p-43,
339 0x1.1bbe084045d54p+0, 0x1.4e40p-45,
340 0x1.1c82f95281c43p+0, -0x1.a200p-47,
341 0x1.1d4873168b9b2p+0, 0x1.3800p-49,
342 0x1.1e0e75eb44031p+0, 0x1.ac00p-49,
343 0x1.1ed5022fcd938p+0, 0x1.1900p-47,
344 0x1.1f9c18438cdf7p+0, -0x1.b780p-46,
345 0x1.2063b88628d8fp+0, 0x1.d940p-45,
346 0x1.212be3578a81ep+0, 0x1.8000p-50,
347 0x1.21f49917ddd41p+0, 0x1.b340p-45,
348 0x1.22bdda2791323p+0, 0x1.9f80p-46,
349 0x1.2387a6e7561e7p+0, -0x1.9c80p-46,
350 0x1.2451ffb821427p+0, 0x1.2300p-47,
351 0x1.251ce4fb2a602p+0, -0x1.3480p-46,
352 0x1.25e85711eceb0p+0, 0x1.2700p-46,
353 0x1.26b4565e27d16p+0, 0x1.1d00p-46,
354 0x1.2780e341de00fp+0, 0x1.1ee0p-44,
355 0x1.284dfe1f5633ep+0, -0x1.4c00p-46,
356 0x1.291ba7591bb30p+0, -0x1.3d80p-46,
357 0x1.29e9df51fdf09p+0, 0x1.8b00p-47,
358 0x1.2ab8a66d10e9bp+0, -0x1.27c0p-45,
359 0x1.2b87fd0dada3ap+0, 0x1.a340p-45,
360 0x1.2c57e39771af9p+0, -0x1.0800p-46,
361 0x1.2d285a6e402d9p+0, -0x1.ed00p-47,
362 0x1.2df961f641579p+0, -0x1.4200p-48,
363 0x1.2ecafa93e2ecfp+0, -0x1.4980p-45,
364 0x1.2f9d24abd8822p+0, -0x1.6300p-46,
365 0x1.306fe0a31b625p+0, -0x1.2360p-44,
366 0x1.31432edeea50bp+0, -0x1.0df8p-40,
367 0x1.32170fc4cd7b8p+0, -0x1.2480p-45,
368 0x1.32eb83ba8e9a2p+0, -0x1.5980p-45,
369 0x1.33c08b2641766p+0, 0x1.ed00p-46,
370 0x1.3496266e3fa27p+0, -0x1.c000p-50,
371 0x1.356c55f929f0fp+0, -0x1.0d80p-44,
372 0x1.36431a2de88b9p+0, 0x1.2c80p-45,
373 0x1.371a7373aaa39p+0, 0x1.0600p-45,
374 0x1.37f26231e74fep+0, -0x1.6600p-46,
375 0x1.38cae6d05d838p+0, -0x1.ae00p-47,
376 0x1.39a401b713ec3p+0, -0x1.4720p-43,
377 0x1.3a7db34e5a020p+0, 0x1.8200p-47,
378 0x1.3b57fbfec6e95p+0, 0x1.e800p-44,
379 0x1.3c32dc313a8f2p+0, 0x1.f800p-49,
380 0x1.3d0e544ede122p+0, -0x1.7a00p-46,
381 0x1.3dea64c1234bbp+0, 0x1.6300p-45,
382 0x1.3ec70df1c4eccp+0, -0x1.8a60p-43,
383 0x1.3fa4504ac7e8cp+0, -0x1.cdc0p-44,
384 0x1.40822c367a0bbp+0, 0x1.5b80p-45,
385 0x1.4160a21f72e95p+0, 0x1.ec00p-46,
386 0x1.423fb27094646p+0, -0x1.3600p-46,
387 0x1.431f5d950a920p+0, 0x1.3980p-45,
388 0x1.43ffa3f84b9ebp+0, 0x1.a000p-48,
389 0x1.44e0860618919p+0, -0x1.6c00p-48,
390 0x1.45c2042a7d201p+0, -0x1.bc00p-47,
391 0x1.46a41ed1d0016p+0, -0x1.2800p-46,
392 0x1.4786d668b3326p+0, 0x1.0e00p-44,
393 0x1.486a2b5c13c00p+0, -0x1.d400p-45,
394 0x1.494e1e192af04p+0, 0x1.c200p-47,
395 0x1.4a32af0d7d372p+0, -0x1.e500p-46,
396 0x1.4b17dea6db801p+0, 0x1.7800p-47,
397 0x1.4bfdad53629e1p+0, -0x1.3800p-46,
398 0x1.4ce41b817c132p+0, 0x1.0800p-47,
399 0x1.4dcb299fddddbp+0, 0x1.c700p-45,
400 0x1.4eb2d81d8ab96p+0, -0x1.ce00p-46,
401 0x1.4f9b2769d2d02p+0, 0x1.9200p-46,
402 0x1.508417f4531c1p+0, -0x1.8c00p-47,
403 0x1.516daa2cf662ap+0, -0x1.a000p-48,
404 0x1.5257de83f51eap+0, 0x1.a080p-43,
405 0x1.5342b569d4edap+0, -0x1.6d80p-45,
406 0x1.542e2f4f6ac1ap+0, -0x1.2440p-44,
407 0x1.551a4ca5d94dbp+0, 0x1.83c0p-43,
408 0x1.56070dde9116bp+0, 0x1.4b00p-45,
409 0x1.56f4736b529dep+0, 0x1.15a0p-43,
410 0x1.57e27dbe2c40ep+0, -0x1.9e00p-45,
411 0x1.58d12d497c76fp+0, -0x1.3080p-45,
412 0x1.59c0827ff0b4cp+0, 0x1.dec0p-43,
413 0x1.5ab07dd485427p+0, -0x1.4000p-51,
414 0x1.5ba11fba87af4p+0, 0x1.0080p-44,
415 0x1.5c9268a59460bp+0, -0x1.6c80p-45,
416 0x1.5d84590998e3fp+0, 0x1.69a0p-43,
417 0x1.5e76f15ad20e1p+0, -0x1.b400p-46,
418 0x1.5f6a320dcebcap+0, 0x1.7700p-46,
419 0x1.605e1b976dcb8p+0, 0x1.6f80p-45,
420 0x1.6152ae6cdf715p+0, 0x1.1000p-47,
421 0x1.6247eb03a5531p+0, -0x1.5d00p-46,
422 0x1.633dd1d1929b5p+0, -0x1.2d00p-46,
423 0x1.6434634ccc313p+0, -0x1.a800p-49,
424 0x1.652b9febc8efap+0, -0x1.8600p-45,
425 0x1.6623882553397p+0, 0x1.1fe0p-40,
426 0x1.671c1c708328ep+0, -0x1.7200p-44,
427 0x1.68155d44ca97ep+0, 0x1.6800p-49,
428 0x1.690f4b19e9471p+0, -0x1.9780p-45,
429};
430
431test "exp2_32" {
432 const epsilon = 0.000001;
433
434 try expect(exp2f(0.0) == 1.0);
435 try expect(math.approxEqAbs(f32, exp2f(0.2), 1.148698, epsilon));
436 try expect(math.approxEqAbs(f32, exp2f(0.8923), 1.856133, epsilon));
437 try expect(math.approxEqAbs(f32, exp2f(1.5), 2.828427, epsilon));
438 try expect(math.approxEqAbs(f32, exp2f(37.45), 187747237888, epsilon));
439 try expect(math.approxEqAbs(f32, exp2f(-1), 0.5, epsilon));
440}
441
442test "exp2_64" {
443 const epsilon = 0.000001;
444
445 try expect(exp2(0.0) == 1.0);
446 try expect(math.approxEqAbs(f64, exp2(0.2), 1.148698, epsilon));
447 try expect(math.approxEqAbs(f64, exp2(0.8923), 1.856133, epsilon));
448 try expect(math.approxEqAbs(f64, exp2(1.5), 2.828427, epsilon));
449 try expect(math.approxEqAbs(f64, exp2(-1), 0.5, epsilon));
450 try expect(math.approxEqAbs(f64, exp2(-0x1.a05cc754481d1p-2), 0x1.824056efc687cp-1, epsilon));
451}
452
453test "exp2_32.special" {
454 try expect(math.isPositiveInf(exp2f(math.inf(f32))));
455 try expect(math.isNan(exp2f(math.nan(f32))));
456}
457
458test "exp2_64.special" {
459 try expect(math.isPositiveInf(exp2(math.inf(f64))));
460 try expect(math.isNan(exp2(math.nan(f64))));
461}
lib/std/special/compiler_rt/fabs.zig created+29
......@@ -0,0 +1,29 @@
1const std = @import("std");
2
3pub fn __fabsh(a: f16) callconv(.C) f16 {
4 return generic_fabs(a);
5}
6
7pub fn fabsf(a: f32) callconv(.C) f32 {
8 return generic_fabs(a);
9}
10
11pub fn fabs(a: f64) callconv(.C) f64 {
12 return generic_fabs(a);
13}
14
15pub fn __fabsx(a: f80) callconv(.C) f80 {
16 return generic_fabs(a);
17}
18
19pub fn fabsq(a: f128) callconv(.C) f128 {
20 return generic_fabs(a);
21}
22
23inline fn generic_fabs(x: anytype) @TypeOf(x) {
24 const T = @TypeOf(x);
25 const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
26 const float_bits = @bitCast(TBits, x);
27 const remove_sign = ~@as(TBits, 0) >> 1;
28 return @bitCast(T, float_bits & remove_sign);
29}
lib/std/special/compiler_rt/floor.zig created+198
......@@ -0,0 +1,198 @@
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/floorf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10
11pub fn __floorh(x: f16) callconv(.C) f16 {
12 var u = @bitCast(u16, x);
13 const e = @intCast(i16, (u >> 10) & 31) - 15;
14 var m: u16 = undefined;
15
16 // TODO: Shouldn't need this explicit check.
17 if (x == 0.0) {
18 return x;
19 }
20
21 if (e >= 10) {
22 return x;
23 }
24
25 if (e >= 0) {
26 m = @as(u16, 1023) >> @intCast(u4, e);
27 if (u & m == 0) {
28 return x;
29 }
30 math.doNotOptimizeAway(x + 0x1.0p120);
31 if (u >> 15 != 0) {
32 u += m;
33 }
34 return @bitCast(f16, u & ~m);
35 } else {
36 math.doNotOptimizeAway(x + 0x1.0p120);
37 if (u >> 15 == 0) {
38 return 0.0;
39 } else {
40 return -1.0;
41 }
42 }
43}
44
45pub fn floorf(x: f32) callconv(.C) f32 {
46 var u = @bitCast(u32, x);
47 const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F;
48 var m: u32 = undefined;
49
50 // TODO: Shouldn't need this explicit check.
51 if (x == 0.0) {
52 return x;
53 }
54
55 if (e >= 23) {
56 return x;
57 }
58
59 if (e >= 0) {
60 m = @as(u32, 0x007FFFFF) >> @intCast(u5, e);
61 if (u & m == 0) {
62 return x;
63 }
64 math.doNotOptimizeAway(x + 0x1.0p120);
65 if (u >> 31 != 0) {
66 u += m;
67 }
68 return @bitCast(f32, u & ~m);
69 } else {
70 math.doNotOptimizeAway(x + 0x1.0p120);
71 if (u >> 31 == 0) {
72 return 0.0;
73 } else {
74 return -1.0;
75 }
76 }
77}
78
79pub fn floor(x: f64) callconv(.C) f64 {
80 const f64_toint = 1.0 / math.floatEps(f64);
81
82 const u = @bitCast(u64, x);
83 const e = (u >> 52) & 0x7FF;
84 var y: f64 = undefined;
85
86 if (e >= 0x3FF + 52 or x == 0) {
87 return x;
88 }
89
90 if (u >> 63 != 0) {
91 y = x - f64_toint + f64_toint - x;
92 } else {
93 y = x + f64_toint - f64_toint - x;
94 }
95
96 if (e <= 0x3FF - 1) {
97 math.doNotOptimizeAway(y);
98 if (u >> 63 != 0) {
99 return -1.0;
100 } else {
101 return 0.0;
102 }
103 } else if (y > 0) {
104 return x + y - 1;
105 } else {
106 return x + y;
107 }
108}
109
110pub fn __floorx(x: f80) callconv(.C) f80 {
111 // TODO: more efficient implementation
112 return @floatCast(f80, floorq(x));
113}
114
115pub fn floorq(x: f128) callconv(.C) f128 {
116 const f128_toint = 1.0 / math.floatEps(f128);
117
118 const u = @bitCast(u128, x);
119 const e = (u >> 112) & 0x7FFF;
120 var y: f128 = undefined;
121
122 if (e >= 0x3FFF + 112 or x == 0) return x;
123
124 if (u >> 127 != 0) {
125 y = x - f128_toint + f128_toint - x;
126 } else {
127 y = x + f128_toint - f128_toint - x;
128 }
129
130 if (e <= 0x3FFF - 1) {
131 math.doNotOptimizeAway(y);
132 if (u >> 127 != 0) {
133 return -1.0;
134 } else {
135 return 0.0;
136 }
137 } else if (y > 0) {
138 return x + y - 1;
139 } else {
140 return x + y;
141 }
142}
143
144test "floor16" {
145 try expect(__floorh(1.3) == 1.0);
146 try expect(__floorh(-1.3) == -2.0);
147 try expect(__floorh(0.2) == 0.0);
148}
149
150test "floor32" {
151 try expect(floorf(1.3) == 1.0);
152 try expect(floorf(-1.3) == -2.0);
153 try expect(floorf(0.2) == 0.0);
154}
155
156test "floor64" {
157 try expect(floor(1.3) == 1.0);
158 try expect(floor(-1.3) == -2.0);
159 try expect(floor(0.2) == 0.0);
160}
161
162test "floor128" {
163 try expect(floorq(1.3) == 1.0);
164 try expect(floorq(-1.3) == -2.0);
165 try expect(floorq(0.2) == 0.0);
166}
167
168test "floor16.special" {
169 try expect(__floorh(0.0) == 0.0);
170 try expect(__floorh(-0.0) == -0.0);
171 try expect(math.isPositiveInf(__floorh(math.inf(f16))));
172 try expect(math.isNegativeInf(__floorh(-math.inf(f16))));
173 try expect(math.isNan(__floorh(math.nan(f16))));
174}
175
176test "floor32.special" {
177 try expect(floorf(0.0) == 0.0);
178 try expect(floorf(-0.0) == -0.0);
179 try expect(math.isPositiveInf(floorf(math.inf(f32))));
180 try expect(math.isNegativeInf(floorf(-math.inf(f32))));
181 try expect(math.isNan(floorf(math.nan(f32))));
182}
183
184test "floor64.special" {
185 try expect(floor(0.0) == 0.0);
186 try expect(floor(-0.0) == -0.0);
187 try expect(math.isPositiveInf(floor(math.inf(f64))));
188 try expect(math.isNegativeInf(floor(-math.inf(f64))));
189 try expect(math.isNan(floor(math.nan(f64))));
190}
191
192test "floor128.special" {
193 try expect(floorq(0.0) == 0.0);
194 try expect(floorq(-0.0) == -0.0);
195 try expect(math.isPositiveInf(floorq(math.inf(f128))));
196 try expect(math.isNegativeInf(floorq(-math.inf(f128))));
197 try expect(math.isNan(floorq(math.nan(f128))));
198}
lib/std/special/compiler_rt/fma.zig created+327
......@@ -0,0 +1,327 @@
1// Ported from musl, which is MIT licensed:
2// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
3//
4// https://git.musl-libc.org/cgit/musl/tree/src/math/fmal.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c
6// https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c
7
8const std = @import("std");
9const math = std.math;
10const expect = std.testing.expect;
11
12pub fn __fmah(x: f16, y: f16, z: f16) callconv(.C) f16 {
13 // TODO: more efficient implementation
14 return @floatCast(f16, fmaf(x, y, z));
15}
16
17pub fn fmaf(x: f32, y: f32, z: f32) callconv(.C) f32 {
18 const xy = @as(f64, x) * y;
19 const xy_z = xy + z;
20 const u = @bitCast(u64, xy_z);
21 const e = (u >> 52) & 0x7FF;
22
23 if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or (xy_z - xy == z and xy_z - z == xy)) {
24 return @floatCast(f32, xy_z);
25 } else {
26 // TODO: Handle inexact case with double-rounding
27 return @floatCast(f32, xy_z);
28 }
29}
30
31/// NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately.
32pub fn fma(x: f64, y: f64, z: f64) callconv(.C) f64 {
33 if (!math.isFinite(x) or !math.isFinite(y)) {
34 return x * y + z;
35 }
36 if (!math.isFinite(z)) {
37 return z;
38 }
39 if (x == 0.0 or y == 0.0) {
40 return x * y + z;
41 }
42 if (z == 0.0) {
43 return x * y;
44 }
45
46 const x1 = math.frexp(x);
47 var ex = x1.exponent;
48 var xs = x1.significand;
49 const x2 = math.frexp(y);
50 var ey = x2.exponent;
51 var ys = x2.significand;
52 const x3 = math.frexp(z);
53 var ez = x3.exponent;
54 var zs = x3.significand;
55
56 var spread = ex + ey - ez;
57 if (spread <= 53 * 2) {
58 zs = math.scalbn(zs, -spread);
59 } else {
60 zs = math.copysign(f64, math.floatMin(f64), zs);
61 }
62
63 const xy = dd_mul(xs, ys);
64 const r = dd_add(xy.hi, zs);
65 spread = ex + ey;
66
67 if (r.hi == 0.0) {
68 return xy.hi + zs + math.scalbn(xy.lo, spread);
69 }
70
71 const adj = add_adjusted(r.lo, xy.lo);
72 if (spread + math.ilogb(r.hi) > -1023) {
73 return math.scalbn(r.hi + adj, spread);
74 } else {
75 return add_and_denorm(r.hi, adj, spread);
76 }
77}
78
79pub fn __fmax(a: f80, b: f80, c: f80) callconv(.C) f80 {
80 // TODO: more efficient implementation
81 return @floatCast(f80, fmaq(a, b, c));
82}
83
84/// Fused multiply-add: Compute x * y + z with a single rounding error.
85///
86/// We use scaling to avoid overflow/underflow, along with the
87/// canonical precision-doubling technique adapted from:
88///
89/// Dekker, T. A Floating-Point Technique for Extending the
90/// Available Precision. Numer. Math. 18, 224-242 (1971).
91pub fn fmaq(x: f128, y: f128, z: f128) callconv(.C) f128 {
92 if (!math.isFinite(x) or !math.isFinite(y)) {
93 return x * y + z;
94 }
95 if (!math.isFinite(z)) {
96 return z;
97 }
98 if (x == 0.0 or y == 0.0) {
99 return x * y + z;
100 }
101 if (z == 0.0) {
102 return x * y;
103 }
104
105 const x1 = math.frexp(x);
106 var ex = x1.exponent;
107 var xs = x1.significand;
108 const x2 = math.frexp(y);
109 var ey = x2.exponent;
110 var ys = x2.significand;
111 const x3 = math.frexp(z);
112 var ez = x3.exponent;
113 var zs = x3.significand;
114
115 var spread = ex + ey - ez;
116 if (spread <= 113 * 2) {
117 zs = math.scalbn(zs, -spread);
118 } else {
119 zs = math.copysign(f128, math.floatMin(f128), zs);
120 }
121
122 const xy = dd_mul128(xs, ys);
123 const r = dd_add128(xy.hi, zs);
124 spread = ex + ey;
125
126 if (r.hi == 0.0) {
127 return xy.hi + zs + math.scalbn(xy.lo, spread);
128 }
129
130 const adj = add_adjusted128(r.lo, xy.lo);
131 if (spread + math.ilogb(r.hi) > -16383) {
132 return math.scalbn(r.hi + adj, spread);
133 } else {
134 return add_and_denorm128(r.hi, adj, spread);
135 }
136}
137
138const dd = struct {
139 hi: f64,
140 lo: f64,
141};
142
143fn dd_add(a: f64, b: f64) dd {
144 var ret: dd = undefined;
145 ret.hi = a + b;
146 const s = ret.hi - a;
147 ret.lo = (a - (ret.hi - s)) + (b - s);
148 return ret;
149}
150
151fn dd_mul(a: f64, b: f64) dd {
152 var ret: dd = undefined;
153 const split: f64 = 0x1.0p27 + 1.0;
154
155 var p = a * split;
156 var ha = a - p;
157 ha += p;
158 var la = a - ha;
159
160 p = b * split;
161 var hb = b - p;
162 hb += p;
163 var lb = b - hb;
164
165 p = ha * hb;
166 var q = ha * lb + la * hb;
167
168 ret.hi = p + q;
169 ret.lo = p - ret.hi + q + la * lb;
170 return ret;
171}
172
173fn add_adjusted(a: f64, b: f64) f64 {
174 var sum = dd_add(a, b);
175 if (sum.lo != 0) {
176 var uhii = @bitCast(u64, sum.hi);
177 if (uhii & 1 == 0) {
178 // hibits += copysign(1.0, sum.hi, sum.lo)
179 const uloi = @bitCast(u64, sum.lo);
180 uhii += 1 - ((uhii ^ uloi) >> 62);
181 sum.hi = @bitCast(f64, uhii);
182 }
183 }
184 return sum.hi;
185}
186
187fn add_and_denorm(a: f64, b: f64, scale: i32) f64 {
188 var sum = dd_add(a, b);
189 if (sum.lo != 0) {
190 var uhii = @bitCast(u64, sum.hi);
191 const bits_lost = -@intCast(i32, (uhii >> 52) & 0x7FF) - scale + 1;
192 if ((bits_lost != 1) == (uhii & 1 != 0)) {
193 const uloi = @bitCast(u64, sum.lo);
194 uhii += 1 - (((uhii ^ uloi) >> 62) & 2);
195 sum.hi = @bitCast(f64, uhii);
196 }
197 }
198 return math.scalbn(sum.hi, scale);
199}
200
201/// A struct that represents a floating-point number with twice the precision
202/// of f128. We maintain the invariant that "hi" stores the high-order
203/// bits of the result.
204const dd128 = struct {
205 hi: f128,
206 lo: f128,
207};
208
209/// Compute a+b exactly, returning the exact result in a struct dd. We assume
210/// that both a and b are finite, but make no assumptions about their relative
211/// magnitudes.
212fn dd_add128(a: f128, b: f128) dd128 {
213 var ret: dd128 = undefined;
214 ret.hi = a + b;
215 const s = ret.hi - a;
216 ret.lo = (a - (ret.hi - s)) + (b - s);
217 return ret;
218}
219
220/// Compute a+b, with a small tweak: The least significant bit of the
221/// result is adjusted into a sticky bit summarizing all the bits that
222/// were lost to rounding. This adjustment negates the effects of double
223/// rounding when the result is added to another number with a higher
224/// exponent. For an explanation of round and sticky bits, see any reference
225/// on FPU design, e.g.,
226///
227/// J. Coonen. An Implementation Guide to a Proposed Standard for
228/// Floating-Point Arithmetic. Computer, vol. 13, no. 1, Jan 1980.
229fn add_adjusted128(a: f128, b: f128) f128 {
230 var sum = dd_add128(a, b);
231 if (sum.lo != 0) {
232 var uhii = @bitCast(u128, sum.hi);
233 if (uhii & 1 == 0) {
234 // hibits += copysign(1.0, sum.hi, sum.lo)
235 const uloi = @bitCast(u128, sum.lo);
236 uhii += 1 - ((uhii ^ uloi) >> 126);
237 sum.hi = @bitCast(f128, uhii);
238 }
239 }
240 return sum.hi;
241}
242
243/// Compute ldexp(a+b, scale) with a single rounding error. It is assumed
244/// that the result will be subnormal, and care is taken to ensure that
245/// double rounding does not occur.
246fn add_and_denorm128(a: f128, b: f128, scale: i32) f128 {
247 var sum = dd_add128(a, b);
248 // If we are losing at least two bits of accuracy to denormalization,
249 // then the first lost bit becomes a round bit, and we adjust the
250 // lowest bit of sum.hi to make it a sticky bit summarizing all the
251 // bits in sum.lo. With the sticky bit adjusted, the hardware will
252 // break any ties in the correct direction.
253 //
254 // If we are losing only one bit to denormalization, however, we must
255 // break the ties manually.
256 if (sum.lo != 0) {
257 var uhii = @bitCast(u128, sum.hi);
258 const bits_lost = -@intCast(i32, (uhii >> 112) & 0x7FFF) - scale + 1;
259 if ((bits_lost != 1) == (uhii & 1 != 0)) {
260 const uloi = @bitCast(u128, sum.lo);
261 uhii += 1 - (((uhii ^ uloi) >> 126) & 2);
262 sum.hi = @bitCast(f128, uhii);
263 }
264 }
265 return math.scalbn(sum.hi, scale);
266}
267
268/// Compute a*b exactly, returning the exact result in a struct dd. We assume
269/// that both a and b are normalized, so no underflow or overflow will occur.
270/// The current rounding mode must be round-to-nearest.
271fn dd_mul128(a: f128, b: f128) dd128 {
272 var ret: dd128 = undefined;
273 const split: f128 = 0x1.0p57 + 1.0;
274
275 var p = a * split;
276 var ha = a - p;
277 ha += p;
278 var la = a - ha;
279
280 p = b * split;
281 var hb = b - p;
282 hb += p;
283 var lb = b - hb;
284
285 p = ha * hb;
286 var q = ha * lb + la * hb;
287
288 ret.hi = p + q;
289 ret.lo = p - ret.hi + q + la * lb;
290 return ret;
291}
292
293test "32" {
294 const epsilon = 0.000001;
295
296 try expect(math.approxEqAbs(f32, fmaf(0.0, 5.0, 9.124), 9.124, epsilon));
297 try expect(math.approxEqAbs(f32, fmaf(0.2, 5.0, 9.124), 10.124, epsilon));
298 try expect(math.approxEqAbs(f32, fmaf(0.8923, 5.0, 9.124), 13.5855, epsilon));
299 try expect(math.approxEqAbs(f32, fmaf(1.5, 5.0, 9.124), 16.624, epsilon));
300 try expect(math.approxEqAbs(f32, fmaf(37.45, 5.0, 9.124), 196.374004, epsilon));
301 try expect(math.approxEqAbs(f32, fmaf(89.123, 5.0, 9.124), 454.739005, epsilon));
302 try expect(math.approxEqAbs(f32, fmaf(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
303}
304
305test "64" {
306 const epsilon = 0.000001;
307
308 try expect(math.approxEqAbs(f64, fma(0.0, 5.0, 9.124), 9.124, epsilon));
309 try expect(math.approxEqAbs(f64, fma(0.2, 5.0, 9.124), 10.124, epsilon));
310 try expect(math.approxEqAbs(f64, fma(0.8923, 5.0, 9.124), 13.5855, epsilon));
311 try expect(math.approxEqAbs(f64, fma(1.5, 5.0, 9.124), 16.624, epsilon));
312 try expect(math.approxEqAbs(f64, fma(37.45, 5.0, 9.124), 196.374, epsilon));
313 try expect(math.approxEqAbs(f64, fma(89.123, 5.0, 9.124), 454.739, epsilon));
314 try expect(math.approxEqAbs(f64, fma(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
315}
316
317test "128" {
318 const epsilon = 0.000001;
319
320 try expect(math.approxEqAbs(f128, fmaq(0.0, 5.0, 9.124), 9.124, epsilon));
321 try expect(math.approxEqAbs(f128, fmaq(0.2, 5.0, 9.124), 10.124, epsilon));
322 try expect(math.approxEqAbs(f128, fmaq(0.8923, 5.0, 9.124), 13.5855, epsilon));
323 try expect(math.approxEqAbs(f128, fmaq(1.5, 5.0, 9.124), 16.624, epsilon));
324 try expect(math.approxEqAbs(f128, fmaq(37.45, 5.0, 9.124), 196.374, epsilon));
325 try expect(math.approxEqAbs(f128, fmaq(89.123, 5.0, 9.124), 454.739, epsilon));
326 try expect(math.approxEqAbs(f128, fmaq(123123.234375, 5.0, 9.124), 615625.295875, epsilon));
327}
lib/std/special/compiler_rt/fmax.zig created+43
......@@ -0,0 +1,43 @@
1const std = @import("std");
2const math = std.math;
3
4pub fn __fmaxh(x: f16, y: f16) callconv(.C) f16 {
5 return generic_fmax(f16, x, y);
6}
7
8pub fn fmaxf(x: f32, y: f32) callconv(.C) f32 {
9 return generic_fmax(f32, x, y);
10}
11
12pub fn fmax(x: f64, y: f64) callconv(.C) f64 {
13 return generic_fmax(f64, x, y);
14}
15
16pub fn __fmaxx(x: f80, y: f80) callconv(.C) f80 {
17 return generic_fmax(f80, x, y);
18}
19
20pub fn fmaxq(x: f128, y: f128) callconv(.C) f128 {
21 return generic_fmax(f128, x, y);
22}
23
24inline fn generic_fmax(comptime T: type, x: T, y: T) T {
25 if (math.isNan(x))
26 return y;
27 if (math.isNan(y))
28 return x;
29 return if (x < y) y else x;
30}
31
32test "generic_fmax" {
33 inline for ([_]type{ f32, f64, c_longdouble, f80, f128 }) |T| {
34 const nan_val = math.nan(T);
35
36 try std.testing.expect(math.isNan(generic_fmax(T, nan_val, nan_val)));
37 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0));
38 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val));
39
40 try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0));
41 try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0));
42 }
43}
lib/std/special/compiler_rt/fmin.zig created+43
......@@ -0,0 +1,43 @@
1const std = @import("std");
2const math = std.math;
3
4pub fn __fminh(x: f16, y: f16) callconv(.C) f16 {
5 return generic_fmin(f16, x, y);
6}
7
8pub fn fminf(x: f32, y: f32) callconv(.C) f32 {
9 return generic_fmin(f32, x, y);
10}
11
12pub fn fmin(x: f64, y: f64) callconv(.C) f64 {
13 return generic_fmin(f64, x, y);
14}
15
16pub fn __fminx(x: f80, y: f80) callconv(.C) f80 {
17 return generic_fmin(f80, x, y);
18}
19
20pub fn fminq(x: f128, y: f128) callconv(.C) f128 {
21 return generic_fmin(f128, x, y);
22}
23
24inline fn generic_fmin(comptime T: type, x: T, y: T) T {
25 if (math.isNan(x))
26 return y;
27 if (math.isNan(y))
28 return x;
29 return if (x < y) x else y;
30}
31
32test "generic_fmin" {
33 inline for ([_]type{ f32, f64, c_longdouble, f80, f128 }) |T| {
34 const nan_val = math.nan(T);
35
36 try std.testing.expect(math.isNan(generic_fmin(T, nan_val, nan_val)));
37 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0));
38 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val));
39
40 try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0));
41 try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0));
42 }
43}
lib/std/special/compiler_rt/fmod.zig created+351
......@@ -0,0 +1,351 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const math = std.math;
4const assert = std.debug.assert;
5const normalize = @import("divdf3.zig").normalize;
6
7pub fn __fmodh(x: f16, y: f16) callconv(.C) f16 {
8 // TODO: more efficient implementation
9 return @floatCast(f16, fmodf(x, y));
10}
11
12pub fn fmodf(x: f32, y: f32) callconv(.C) f32 {
13 return generic_fmod(f32, x, y);
14}
15
16pub fn fmod(x: f64, y: f64) callconv(.C) f64 {
17 return generic_fmod(f64, x, y);
18}
19
20/// fmodx - floating modulo large, returns the remainder of division for f80 types
21/// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits
22pub fn __fmodx(a: f80, b: f80) callconv(.C) f80 {
23 @setRuntimeSafety(builtin.is_test);
24
25 const T = f80;
26 const Z = std.meta.Int(.unsigned, @bitSizeOf(T));
27
28 const significandBits = math.floatMantissaBits(T);
29 const fractionalBits = math.floatFractionalBits(T);
30 const exponentBits = math.floatExponentBits(T);
31
32 const signBit = (@as(Z, 1) << (significandBits + exponentBits));
33 const maxExponent = ((1 << exponentBits) - 1);
34
35 var aRep = @bitCast(Z, a);
36 var bRep = @bitCast(Z, b);
37
38 const signA = aRep & signBit;
39 var expA = @intCast(i32, (@bitCast(Z, a) >> significandBits) & maxExponent);
40 var expB = @intCast(i32, (@bitCast(Z, b) >> significandBits) & maxExponent);
41
42 // There are 3 cases where the answer is undefined, check for:
43 // - fmodx(val, 0)
44 // - fmodx(val, NaN)
45 // - fmodx(inf, val)
46 // The sign on checked values does not matter.
47 // Doing (a * b) / (a * b) procudes undefined results
48 // because the three cases always produce undefined calculations:
49 // - 0 / 0
50 // - val * NaN
51 // - inf / inf
52 if (b == 0 or math.isNan(b) or expA == maxExponent) {
53 return (a * b) / (a * b);
54 }
55
56 // Remove the sign from both
57 aRep &= ~signBit;
58 bRep &= ~signBit;
59 if (aRep <= bRep) {
60 if (aRep == bRep) {
61 return 0 * a;
62 }
63 return a;
64 }
65
66 if (expA == 0) expA = normalize(f80, &aRep);
67 if (expB == 0) expB = normalize(f80, &bRep);
68
69 var highA: u64 = 0;
70 var highB: u64 = 0;
71 var lowA: u64 = @truncate(u64, aRep);
72 var lowB: u64 = @truncate(u64, bRep);
73
74 while (expA > expB) : (expA -= 1) {
75 var high = highA -% highB;
76 var low = lowA -% lowB;
77 if (lowA < lowB) {
78 high -%= 1;
79 }
80 if (high >> 63 == 0) {
81 if ((high | low) == 0) {
82 return 0 * a;
83 }
84 highA = 2 *% high + (low >> 63);
85 lowA = 2 *% low;
86 } else {
87 highA = 2 *% highA + (lowA >> 63);
88 lowA = 2 *% lowA;
89 }
90 }
91
92 var high = highA -% highB;
93 var low = lowA -% lowB;
94 if (lowA < lowB) {
95 high -%= 1;
96 }
97 if (high >> 63 == 0) {
98 if ((high | low) == 0) {
99 return 0 * a;
100 }
101 highA = high;
102 lowA = low;
103 }
104
105 while ((lowA >> fractionalBits) == 0) {
106 lowA = 2 *% lowA;
107 expA = expA - 1;
108 }
109
110 // Combine the exponent with the sign and significand, normalize if happened to be denormalized
111 if (expA < -fractionalBits) {
112 return @bitCast(T, signA);
113 } else if (expA <= 0) {
114 return @bitCast(T, (lowA >> @intCast(math.Log2Int(u64), 1 - expA)) | signA);
115 } else {
116 return @bitCast(T, lowA | (@as(Z, @intCast(u16, expA)) << significandBits) | signA);
117 }
118}
119
120/// fmodq - floating modulo large, returns the remainder of division for f128 types
121/// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits
122pub fn fmodq(a: f128, b: f128) callconv(.C) f128 {
123 @setRuntimeSafety(builtin.is_test);
124 var amod = a;
125 var bmod = b;
126 const aPtr_u64 = @ptrCast([*]u64, &amod);
127 const bPtr_u64 = @ptrCast([*]u64, &bmod);
128 const aPtr_u16 = @ptrCast([*]u16, &amod);
129 const bPtr_u16 = @ptrCast([*]u16, &bmod);
130
131 const exp_and_sign_index = comptime switch (builtin.target.cpu.arch.endian()) {
132 .Little => 7,
133 .Big => 0,
134 };
135 const low_index = comptime switch (builtin.target.cpu.arch.endian()) {
136 .Little => 0,
137 .Big => 1,
138 };
139 const high_index = comptime switch (builtin.target.cpu.arch.endian()) {
140 .Little => 1,
141 .Big => 0,
142 };
143
144 const signA = aPtr_u16[exp_and_sign_index] & 0x8000;
145 var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff));
146 var expB = @intCast(i32, (bPtr_u16[exp_and_sign_index] & 0x7fff));
147
148 // There are 3 cases where the answer is undefined, check for:
149 // - fmodq(val, 0)
150 // - fmodq(val, NaN)
151 // - fmodq(inf, val)
152 // The sign on checked values does not matter.
153 // Doing (a * b) / (a * b) procudes undefined results
154 // because the three cases always produce undefined calculations:
155 // - 0 / 0
156 // - val * NaN
157 // - inf / inf
158 if (b == 0 or std.math.isNan(b) or expA == 0x7fff) {
159 return (a * b) / (a * b);
160 }
161
162 // Remove the sign from both
163 aPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expA));
164 bPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expB));
165 if (amod <= bmod) {
166 if (amod == bmod) {
167 return 0 * a;
168 }
169 return a;
170 }
171
172 if (expA == 0) {
173 amod *= 0x1p120;
174 expA = @as(i32, aPtr_u16[exp_and_sign_index]) - 120;
175 }
176
177 if (expB == 0) {
178 bmod *= 0x1p120;
179 expB = @as(i32, bPtr_u16[exp_and_sign_index]) - 120;
180 }
181
182 // OR in extra non-stored mantissa digit
183 var highA: u64 = (aPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48;
184 var highB: u64 = (bPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48;
185 var lowA: u64 = aPtr_u64[low_index];
186 var lowB: u64 = bPtr_u64[low_index];
187
188 while (expA > expB) : (expA -= 1) {
189 var high = highA -% highB;
190 var low = lowA -% lowB;
191 if (lowA < lowB) {
192 high -%= 1;
193 }
194 if (high >> 63 == 0) {
195 if ((high | low) == 0) {
196 return 0 * a;
197 }
198 highA = 2 *% high + (low >> 63);
199 lowA = 2 *% low;
200 } else {
201 highA = 2 *% highA + (lowA >> 63);
202 lowA = 2 *% lowA;
203 }
204 }
205
206 var high = highA -% highB;
207 var low = lowA -% lowB;
208 if (lowA < lowB) {
209 high -= 1;
210 }
211 if (high >> 63 == 0) {
212 if ((high | low) == 0) {
213 return 0 * a;
214 }
215 highA = high;
216 lowA = low;
217 }
218
219 while (highA >> 48 == 0) {
220 highA = 2 *% highA + (lowA >> 63);
221 lowA = 2 *% lowA;
222 expA = expA - 1;
223 }
224
225 // Overwrite the current amod with the values in highA and lowA
226 aPtr_u64[high_index] = highA;
227 aPtr_u64[low_index] = lowA;
228
229 // Combine the exponent with the sign, normalize if happend to be denormalized
230 if (expA <= 0) {
231 aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, (expA +% 120))) | signA;
232 amod *= 0x1p-120;
233 } else {
234 aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, expA)) | signA;
235 }
236
237 return amod;
238}
239
240inline fn generic_fmod(comptime T: type, x: T, y: T) T {
241 @setRuntimeSafety(false);
242
243 const bits = @typeInfo(T).Float.bits;
244 const uint = std.meta.Int(.unsigned, bits);
245 const log2uint = math.Log2Int(uint);
246 comptime assert(T == f32 or T == f64);
247 const digits = if (T == f32) 23 else 52;
248 const exp_bits = if (T == f32) 9 else 12;
249 const bits_minus_1 = bits - 1;
250 const mask = if (T == f32) 0xff else 0x7ff;
251 var ux = @bitCast(uint, x);
252 var uy = @bitCast(uint, y);
253 var ex = @intCast(i32, (ux >> digits) & mask);
254 var ey = @intCast(i32, (uy >> digits) & mask);
255 const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1);
256 var i: uint = undefined;
257
258 if (uy << 1 == 0 or math.isNan(@bitCast(T, uy)) or ex == mask)
259 return (x * y) / (x * y);
260
261 if (ux << 1 <= uy << 1) {
262 if (ux << 1 == uy << 1)
263 return 0 * x;
264 return x;
265 }
266
267 // normalize x and y
268 if (ex == 0) {
269 i = ux << exp_bits;
270 while (i >> bits_minus_1 == 0) : ({
271 ex -= 1;
272 i <<= 1;
273 }) {}
274 ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1));
275 } else {
276 ux &= math.maxInt(uint) >> exp_bits;
277 ux |= 1 << digits;
278 }
279 if (ey == 0) {
280 i = uy << exp_bits;
281 while (i >> bits_minus_1 == 0) : ({
282 ey -= 1;
283 i <<= 1;
284 }) {}
285 uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1));
286 } else {
287 uy &= math.maxInt(uint) >> exp_bits;
288 uy |= 1 << digits;
289 }
290
291 // x mod y
292 while (ex > ey) : (ex -= 1) {
293 i = ux -% uy;
294 if (i >> bits_minus_1 == 0) {
295 if (i == 0)
296 return 0 * x;
297 ux = i;
298 }
299 ux <<= 1;
300 }
301 i = ux -% uy;
302 if (i >> bits_minus_1 == 0) {
303 if (i == 0)
304 return 0 * x;
305 ux = i;
306 }
307 while (ux >> digits == 0) : ({
308 ux <<= 1;
309 ex -= 1;
310 }) {}
311
312 // scale result up
313 if (ex > 0) {
314 ux -%= 1 << digits;
315 ux |= @as(uint, @bitCast(u32, ex)) << digits;
316 } else {
317 ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1));
318 }
319 if (T == f32) {
320 ux |= sx;
321 } else {
322 ux |= @intCast(uint, sx) << bits_minus_1;
323 }
324 return @bitCast(T, ux);
325}
326
327test "fmod, fmodf" {
328 inline for ([_]type{ f32, f64 }) |T| {
329 const nan_val = math.nan(T);
330 const inf_val = math.inf(T);
331
332 try std.testing.expect(math.isNan(generic_fmod(T, nan_val, 1.0)));
333 try std.testing.expect(math.isNan(generic_fmod(T, 1.0, nan_val)));
334 try std.testing.expect(math.isNan(generic_fmod(T, inf_val, 1.0)));
335 try std.testing.expect(math.isNan(generic_fmod(T, 0.0, 0.0)));
336 try std.testing.expect(math.isNan(generic_fmod(T, 1.0, 0.0)));
337
338 try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0));
339 try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0));
340
341 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0));
342 try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0));
343 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0));
344 try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0));
345 }
346}
347
348test {
349 _ = @import("fmodq_test.zig");
350 _ = @import("fmodx_test.zig");
351}
lib/std/special/compiler_rt/fmodq.zig deleted-126
......@@ -1,126 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("std");
3
4// fmodq - floating modulo large, returns the remainder of division for f128 types
5// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits
6pub fn fmodq(a: f128, b: f128) callconv(.C) f128 {
7 @setRuntimeSafety(builtin.is_test);
8 var amod = a;
9 var bmod = b;
10 const aPtr_u64 = @ptrCast([*]u64, &amod);
11 const bPtr_u64 = @ptrCast([*]u64, &bmod);
12 const aPtr_u16 = @ptrCast([*]u16, &amod);
13 const bPtr_u16 = @ptrCast([*]u16, &bmod);
14
15 const exp_and_sign_index = comptime switch (builtin.target.cpu.arch.endian()) {
16 .Little => 7,
17 .Big => 0,
18 };
19 const low_index = comptime switch (builtin.target.cpu.arch.endian()) {
20 .Little => 0,
21 .Big => 1,
22 };
23 const high_index = comptime switch (builtin.target.cpu.arch.endian()) {
24 .Little => 1,
25 .Big => 0,
26 };
27
28 const signA = aPtr_u16[exp_and_sign_index] & 0x8000;
29 var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff));
30 var expB = @intCast(i32, (bPtr_u16[exp_and_sign_index] & 0x7fff));
31
32 // There are 3 cases where the answer is undefined, check for:
33 // - fmodq(val, 0)
34 // - fmodq(val, NaN)
35 // - fmodq(inf, val)
36 // The sign on checked values does not matter.
37 // Doing (a * b) / (a * b) procudes undefined results
38 // because the three cases always produce undefined calculations:
39 // - 0 / 0
40 // - val * NaN
41 // - inf / inf
42 if (b == 0 or std.math.isNan(b) or expA == 0x7fff) {
43 return (a * b) / (a * b);
44 }
45
46 // Remove the sign from both
47 aPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expA));
48 bPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expB));
49 if (amod <= bmod) {
50 if (amod == bmod) {
51 return 0 * a;
52 }
53 return a;
54 }
55
56 if (expA == 0) {
57 amod *= 0x1p120;
58 expA = @as(i32, aPtr_u16[exp_and_sign_index]) - 120;
59 }
60
61 if (expB == 0) {
62 bmod *= 0x1p120;
63 expB = @as(i32, bPtr_u16[exp_and_sign_index]) - 120;
64 }
65
66 // OR in extra non-stored mantissa digit
67 var highA: u64 = (aPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48;
68 var highB: u64 = (bPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48;
69 var lowA: u64 = aPtr_u64[low_index];
70 var lowB: u64 = bPtr_u64[low_index];
71
72 while (expA > expB) : (expA -= 1) {
73 var high = highA -% highB;
74 var low = lowA -% lowB;
75 if (lowA < lowB) {
76 high -%= 1;
77 }
78 if (high >> 63 == 0) {
79 if ((high | low) == 0) {
80 return 0 * a;
81 }
82 highA = 2 *% high + (low >> 63);
83 lowA = 2 *% low;
84 } else {
85 highA = 2 *% highA + (lowA >> 63);
86 lowA = 2 *% lowA;
87 }
88 }
89
90 var high = highA -% highB;
91 var low = lowA -% lowB;
92 if (lowA < lowB) {
93 high -= 1;
94 }
95 if (high >> 63 == 0) {
96 if ((high | low) == 0) {
97 return 0 * a;
98 }
99 highA = high;
100 lowA = low;
101 }
102
103 while (highA >> 48 == 0) {
104 highA = 2 *% highA + (lowA >> 63);
105 lowA = 2 *% lowA;
106 expA = expA - 1;
107 }
108
109 // Overwrite the current amod with the values in highA and lowA
110 aPtr_u64[high_index] = highA;
111 aPtr_u64[low_index] = lowA;
112
113 // Combine the exponent with the sign, normalize if happend to be denormalized
114 if (expA <= 0) {
115 aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, (expA +% 120))) | signA;
116 amod *= 0x1p-120;
117 } else {
118 aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, expA)) | signA;
119 }
120
121 return amod;
122}
123
124test {
125 _ = @import("fmodq_test.zig");
126}
lib/std/special/compiler_rt/fmodq_test.zig+10-10
......@@ -1,24 +1,24 @@
11const std = @import("std");
2const fmodq = @import("fmodq.zig");
2const fmod = @import("fmod.zig");
33const testing = std.testing;
44
55fn test_fmodq(a: f128, b: f128, exp: f128) !void {
6 const res = fmodq.fmodq(a, b);
6 const res = fmod.fmodq(a, b);
77 try testing.expect(exp == res);
88}
99
1010fn test_fmodq_nans() !void {
11 try testing.expect(std.math.isNan(fmodq.fmodq(1.0, std.math.nan(f128))));
12 try testing.expect(std.math.isNan(fmodq.fmodq(1.0, -std.math.nan(f128))));
13 try testing.expect(std.math.isNan(fmodq.fmodq(std.math.nan(f128), 1.0)));
14 try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.nan(f128), 1.0)));
11 try testing.expect(std.math.isNan(fmod.fmodq(1.0, std.math.nan(f128))));
12 try testing.expect(std.math.isNan(fmod.fmodq(1.0, -std.math.nan(f128))));
13 try testing.expect(std.math.isNan(fmod.fmodq(std.math.nan(f128), 1.0)));
14 try testing.expect(std.math.isNan(fmod.fmodq(-std.math.nan(f128), 1.0)));
1515}
1616
1717fn test_fmodq_infs() !void {
18 try testing.expect(fmodq.fmodq(1.0, std.math.inf(f128)) == 1.0);
19 try testing.expect(fmodq.fmodq(1.0, -std.math.inf(f128)) == 1.0);
20 try testing.expect(std.math.isNan(fmodq.fmodq(std.math.inf(f128), 1.0)));
21 try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.inf(f128), 1.0)));
18 try testing.expect(fmod.fmodq(1.0, std.math.inf(f128)) == 1.0);
19 try testing.expect(fmod.fmodq(1.0, -std.math.inf(f128)) == 1.0);
20 try testing.expect(std.math.isNan(fmod.fmodq(std.math.inf(f128), 1.0)));
21 try testing.expect(std.math.isNan(fmod.fmodq(-std.math.inf(f128), 1.0)));
2222}
2323
2424test "fmodq" {
lib/std/special/compiler_rt/fmodx.zig deleted-108
......@@ -1,108 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const math = std.math;
4const normalize = @import("divdf3.zig").normalize;
5
6// fmodx - floating modulo large, returns the remainder of division for f80 types
7// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits
8pub fn fmodx(a: f80, b: f80) callconv(.C) f80 {
9 @setRuntimeSafety(builtin.is_test);
10
11 const T = f80;
12 const Z = std.meta.Int(.unsigned, @bitSizeOf(T));
13
14 const significandBits = math.floatMantissaBits(T);
15 const fractionalBits = math.floatFractionalBits(T);
16 const exponentBits = math.floatExponentBits(T);
17
18 const signBit = (@as(Z, 1) << (significandBits + exponentBits));
19 const maxExponent = ((1 << exponentBits) - 1);
20
21 var aRep = @bitCast(Z, a);
22 var bRep = @bitCast(Z, b);
23
24 const signA = aRep & signBit;
25 var expA = @intCast(i32, (@bitCast(Z, a) >> significandBits) & maxExponent);
26 var expB = @intCast(i32, (@bitCast(Z, b) >> significandBits) & maxExponent);
27
28 // There are 3 cases where the answer is undefined, check for:
29 // - fmodx(val, 0)
30 // - fmodx(val, NaN)
31 // - fmodx(inf, val)
32 // The sign on checked values does not matter.
33 // Doing (a * b) / (a * b) procudes undefined results
34 // because the three cases always produce undefined calculations:
35 // - 0 / 0
36 // - val * NaN
37 // - inf / inf
38 if (b == 0 or math.isNan(b) or expA == maxExponent) {
39 return (a * b) / (a * b);
40 }
41
42 // Remove the sign from both
43 aRep &= ~signBit;
44 bRep &= ~signBit;
45 if (aRep <= bRep) {
46 if (aRep == bRep) {
47 return 0 * a;
48 }
49 return a;
50 }
51
52 if (expA == 0) expA = normalize(f80, &aRep);
53 if (expB == 0) expB = normalize(f80, &bRep);
54
55 var highA: u64 = 0;
56 var highB: u64 = 0;
57 var lowA: u64 = @truncate(u64, aRep);
58 var lowB: u64 = @truncate(u64, bRep);
59
60 while (expA > expB) : (expA -= 1) {
61 var high = highA -% highB;
62 var low = lowA -% lowB;
63 if (lowA < lowB) {
64 high -%= 1;
65 }
66 if (high >> 63 == 0) {
67 if ((high | low) == 0) {
68 return 0 * a;
69 }
70 highA = 2 *% high + (low >> 63);
71 lowA = 2 *% low;
72 } else {
73 highA = 2 *% highA + (lowA >> 63);
74 lowA = 2 *% lowA;
75 }
76 }
77
78 var high = highA -% highB;
79 var low = lowA -% lowB;
80 if (lowA < lowB) {
81 high -%= 1;
82 }
83 if (high >> 63 == 0) {
84 if ((high | low) == 0) {
85 return 0 * a;
86 }
87 highA = high;
88 lowA = low;
89 }
90
91 while ((lowA >> fractionalBits) == 0) {
92 lowA = 2 *% lowA;
93 expA = expA - 1;
94 }
95
96 // Combine the exponent with the sign and significand, normalize if happened to be denormalized
97 if (expA < -fractionalBits) {
98 return @bitCast(T, signA);
99 } else if (expA <= 0) {
100 return @bitCast(T, (lowA >> @intCast(math.Log2Int(u64), 1 - expA)) | signA);
101 } else {
102 return @bitCast(T, lowA | (@as(Z, @intCast(u16, expA)) << significandBits) | signA);
103 }
104}
105
106test {
107 _ = @import("fmodx_test.zig");
108}
lib/std/special/compiler_rt/fmodx_test.zig+10-10
......@@ -1,24 +1,24 @@
11const std = @import("std");
2const fmodx = @import("fmodx.zig");
2const fmod = @import("fmod.zig");
33const testing = std.testing;
44
55fn test_fmodx(a: f80, b: f80, exp: f80) !void {
6 const res = fmodx.fmodx(a, b);
6 const res = fmod.__fmodx(a, b);
77 try testing.expect(exp == res);
88}
99
1010fn test_fmodx_nans() !void {
11 try testing.expect(std.math.isNan(fmodx.fmodx(1.0, std.math.nan(f80))));
12 try testing.expect(std.math.isNan(fmodx.fmodx(1.0, -std.math.nan(f80))));
13 try testing.expect(std.math.isNan(fmodx.fmodx(std.math.nan(f80), 1.0)));
14 try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.nan(f80), 1.0)));
11 try testing.expect(std.math.isNan(fmod.__fmodx(1.0, std.math.nan(f80))));
12 try testing.expect(std.math.isNan(fmod.__fmodx(1.0, -std.math.nan(f80))));
13 try testing.expect(std.math.isNan(fmod.__fmodx(std.math.nan(f80), 1.0)));
14 try testing.expect(std.math.isNan(fmod.__fmodx(-std.math.nan(f80), 1.0)));
1515}
1616
1717fn test_fmodx_infs() !void {
18 try testing.expect(fmodx.fmodx(1.0, std.math.inf(f80)) == 1.0);
19 try testing.expect(fmodx.fmodx(1.0, -std.math.inf(f80)) == 1.0);
20 try testing.expect(std.math.isNan(fmodx.fmodx(std.math.inf(f80), 1.0)));
21 try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.inf(f80), 1.0)));
18 try testing.expect(fmod.__fmodx(1.0, std.math.inf(f80)) == 1.0);
19 try testing.expect(fmod.__fmodx(1.0, -std.math.inf(f80)) == 1.0);
20 try testing.expect(std.math.isNan(fmod.__fmodx(std.math.inf(f80), 1.0)));
21 try testing.expect(std.math.isNan(fmod.__fmodx(-std.math.inf(f80), 1.0)));
2222}
2323
2424test "fmodx" {
lib/std/special/compiler_rt/log.zig created+168
......@@ -0,0 +1,168 @@
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/lnf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c
6
7const std = @import("std");
8const math = std.math;
9const testing = std.testing;
10
11pub fn __logh(a: f16) callconv(.C) f16 {
12 // TODO: more efficient implementation
13 return @floatCast(f16, logf(a));
14}
15
16pub fn logf(x_: f32) callconv(.C) f32 {
17 const ln2_hi: f32 = 6.9313812256e-01;
18 const ln2_lo: f32 = 9.0580006145e-06;
19 const Lg1: f32 = 0xaaaaaa.0p-24;
20 const Lg2: f32 = 0xccce13.0p-25;
21 const Lg3: f32 = 0x91e9ee.0p-25;
22 const Lg4: f32 = 0xf89e26.0p-26;
23
24 var x = x_;
25 var ix = @bitCast(u32, x);
26 var k: i32 = 0;
27
28 // x < 2^(-126)
29 if (ix < 0x00800000 or ix >> 31 != 0) {
30 // log(+-0) = -inf
31 if (ix << 1 == 0) {
32 return -math.inf(f32);
33 }
34 // log(-#) = nan
35 if (ix >> 31 != 0) {
36 return math.nan(f32);
37 }
38
39 // subnormal, scale x
40 k -= 25;
41 x *= 0x1.0p25;
42 ix = @bitCast(u32, x);
43 } else if (ix >= 0x7F800000) {
44 return x;
45 } else if (ix == 0x3F800000) {
46 return 0;
47 }
48
49 // x into [sqrt(2) / 2, sqrt(2)]
50 ix += 0x3F800000 - 0x3F3504F3;
51 k += @intCast(i32, ix >> 23) - 0x7F;
52 ix = (ix & 0x007FFFFF) + 0x3F3504F3;
53 x = @bitCast(f32, ix);
54
55 const f = x - 1.0;
56 const s = f / (2.0 + f);
57 const z = s * s;
58 const w = z * z;
59 const t1 = w * (Lg2 + w * Lg4);
60 const t2 = z * (Lg1 + w * Lg3);
61 const R = t2 + t1;
62 const hfsq = 0.5 * f * f;
63 const dk = @intToFloat(f32, k);
64
65 return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
66}
67
68pub fn log(x_: f64) callconv(.C) f64 {
69 const ln2_hi: f64 = 6.93147180369123816490e-01;
70 const ln2_lo: f64 = 1.90821492927058770002e-10;
71 const Lg1: f64 = 6.666666666666735130e-01;
72 const Lg2: f64 = 3.999999999940941908e-01;
73 const Lg3: f64 = 2.857142874366239149e-01;
74 const Lg4: f64 = 2.222219843214978396e-01;
75 const Lg5: f64 = 1.818357216161805012e-01;
76 const Lg6: f64 = 1.531383769920937332e-01;
77 const Lg7: f64 = 1.479819860511658591e-01;
78
79 var x = x_;
80 var ix = @bitCast(u64, x);
81 var hx = @intCast(u32, ix >> 32);
82 var k: i32 = 0;
83
84 if (hx < 0x00100000 or hx >> 31 != 0) {
85 // log(+-0) = -inf
86 if (ix << 1 == 0) {
87 return -math.inf(f64);
88 }
89 // log(-#) = nan
90 if (hx >> 31 != 0) {
91 return math.nan(f64);
92 }
93
94 // subnormal, scale x
95 k -= 54;
96 x *= 0x1.0p54;
97 hx = @intCast(u32, @bitCast(u64, ix) >> 32);
98 } else if (hx >= 0x7FF00000) {
99 return x;
100 } else if (hx == 0x3FF00000 and ix << 32 == 0) {
101 return 0;
102 }
103
104 // x into [sqrt(2) / 2, sqrt(2)]
105 hx += 0x3FF00000 - 0x3FE6A09E;
106 k += @intCast(i32, hx >> 20) - 0x3FF;
107 hx = (hx & 0x000FFFFF) + 0x3FE6A09E;
108 ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF);
109 x = @bitCast(f64, ix);
110
111 const f = x - 1.0;
112 const hfsq = 0.5 * f * f;
113 const s = f / (2.0 + f);
114 const z = s * s;
115 const w = z * z;
116 const t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
117 const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
118 const R = t2 + t1;
119 const dk = @intToFloat(f64, k);
120
121 return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
122}
123
124pub fn __logx(a: f80) callconv(.C) f80 {
125 // TODO: more efficient implementation
126 return @floatCast(f80, logq(a));
127}
128
129pub fn logq(a: f128) callconv(.C) f128 {
130 // TODO: more correct implementation
131 return log(@floatCast(f64, a));
132}
133
134test "ln32" {
135 const epsilon = 0.000001;
136
137 try testing.expect(math.approxEqAbs(f32, logf(0.2), -1.609438, epsilon));
138 try testing.expect(math.approxEqAbs(f32, logf(0.8923), -0.113953, epsilon));
139 try testing.expect(math.approxEqAbs(f32, logf(1.5), 0.405465, epsilon));
140 try testing.expect(math.approxEqAbs(f32, logf(37.45), 3.623007, epsilon));
141 try testing.expect(math.approxEqAbs(f32, logf(89.123), 4.490017, epsilon));
142 try testing.expect(math.approxEqAbs(f32, logf(123123.234375), 11.720941, epsilon));
143}
144
145test "ln64" {
146 const epsilon = 0.000001;
147
148 try testing.expect(math.approxEqAbs(f64, log(0.2), -1.609438, epsilon));
149 try testing.expect(math.approxEqAbs(f64, log(0.8923), -0.113953, epsilon));
150 try testing.expect(math.approxEqAbs(f64, log(1.5), 0.405465, epsilon));
151 try testing.expect(math.approxEqAbs(f64, log(37.45), 3.623007, epsilon));
152 try testing.expect(math.approxEqAbs(f64, log(89.123), 4.490017, epsilon));
153 try testing.expect(math.approxEqAbs(f64, log(123123.234375), 11.720941, epsilon));
154}
155
156test "ln32.special" {
157 try testing.expect(math.isPositiveInf(logf(math.inf(f32))));
158 try testing.expect(math.isNegativeInf(logf(0.0)));
159 try testing.expect(math.isNan(logf(-1.0)));
160 try testing.expect(math.isNan(logf(math.nan(f32))));
161}
162
163test "ln64.special" {
164 try testing.expect(math.isPositiveInf(log(math.inf(f64))));
165 try testing.expect(math.isNegativeInf(log(0.0)));
166 try testing.expect(math.isNan(log(-1.0)));
167 try testing.expect(math.isNan(log(math.nan(f64))));
168}
lib/std/special/compiler_rt/log10.zig created+196
......@@ -0,0 +1,196 @@
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/log10f.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c
6
7const std = @import("std");
8const math = std.math;
9const testing = std.testing;
10const maxInt = std.math.maxInt;
11
12pub fn __log10h(a: f16) callconv(.C) f16 {
13 // TODO: more efficient implementation
14 return @floatCast(f16, log10f(a));
15}
16
17pub fn log10f(x_: f32) callconv(.C) f32 {
18 const ivln10hi: f32 = 4.3432617188e-01;
19 const ivln10lo: f32 = -3.1689971365e-05;
20 const log10_2hi: f32 = 3.0102920532e-01;
21 const log10_2lo: f32 = 7.9034151668e-07;
22 const Lg1: f32 = 0xaaaaaa.0p-24;
23 const Lg2: f32 = 0xccce13.0p-25;
24 const Lg3: f32 = 0x91e9ee.0p-25;
25 const Lg4: f32 = 0xf89e26.0p-26;
26
27 var x = x_;
28 var u = @bitCast(u32, x);
29 var ix = u;
30 var k: i32 = 0;
31
32 // x < 2^(-126)
33 if (ix < 0x00800000 or ix >> 31 != 0) {
34 // log(+-0) = -inf
35 if (ix << 1 == 0) {
36 return -math.inf(f32);
37 }
38 // log(-#) = nan
39 if (ix >> 31 != 0) {
40 return math.nan(f32);
41 }
42
43 k -= 25;
44 x *= 0x1.0p25;
45 ix = @bitCast(u32, x);
46 } else if (ix >= 0x7F800000) {
47 return x;
48 } else if (ix == 0x3F800000) {
49 return 0;
50 }
51
52 // x into [sqrt(2) / 2, sqrt(2)]
53 ix += 0x3F800000 - 0x3F3504F3;
54 k += @intCast(i32, ix >> 23) - 0x7F;
55 ix = (ix & 0x007FFFFF) + 0x3F3504F3;
56 x = @bitCast(f32, ix);
57
58 const f = x - 1.0;
59 const s = f / (2.0 + f);
60 const z = s * s;
61 const w = z * z;
62 const t1 = w * (Lg2 + w * Lg4);
63 const t2 = z * (Lg1 + w * Lg3);
64 const R = t2 + t1;
65 const hfsq = 0.5 * f * f;
66
67 var hi = f - hfsq;
68 u = @bitCast(u32, hi);
69 u &= 0xFFFFF000;
70 hi = @bitCast(f32, u);
71 const lo = f - hi - hfsq + s * (hfsq + R);
72 const dk = @intToFloat(f32, k);
73
74 return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi;
75}
76
77pub fn log10(x_: f64) callconv(.C) f64 {
78 const ivln10hi: f64 = 4.34294481878168880939e-01;
79 const ivln10lo: f64 = 2.50829467116452752298e-11;
80 const log10_2hi: f64 = 3.01029995663611771306e-01;
81 const log10_2lo: f64 = 3.69423907715893078616e-13;
82 const Lg1: f64 = 6.666666666666735130e-01;
83 const Lg2: f64 = 3.999999999940941908e-01;
84 const Lg3: f64 = 2.857142874366239149e-01;
85 const Lg4: f64 = 2.222219843214978396e-01;
86 const Lg5: f64 = 1.818357216161805012e-01;
87 const Lg6: f64 = 1.531383769920937332e-01;
88 const Lg7: f64 = 1.479819860511658591e-01;
89
90 var x = x_;
91 var ix = @bitCast(u64, x);
92 var hx = @intCast(u32, ix >> 32);
93 var k: i32 = 0;
94
95 if (hx < 0x00100000 or hx >> 31 != 0) {
96 // log(+-0) = -inf
97 if (ix << 1 == 0) {
98 return -math.inf(f32);
99 }
100 // log(-#) = nan
101 if (hx >> 31 != 0) {
102 return math.nan(f32);
103 }
104
105 // subnormal, scale x
106 k -= 54;
107 x *= 0x1.0p54;
108 hx = @intCast(u32, @bitCast(u64, x) >> 32);
109 } else if (hx >= 0x7FF00000) {
110 return x;
111 } else if (hx == 0x3FF00000 and ix << 32 == 0) {
112 return 0;
113 }
114
115 // x into [sqrt(2) / 2, sqrt(2)]
116 hx += 0x3FF00000 - 0x3FE6A09E;
117 k += @intCast(i32, hx >> 20) - 0x3FF;
118 hx = (hx & 0x000FFFFF) + 0x3FE6A09E;
119 ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF);
120 x = @bitCast(f64, ix);
121
122 const f = x - 1.0;
123 const hfsq = 0.5 * f * f;
124 const s = f / (2.0 + f);
125 const z = s * s;
126 const w = z * z;
127 const t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
128 const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
129 const R = t2 + t1;
130
131 // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
132 var hi = f - hfsq;
133 var hii = @bitCast(u64, hi);
134 hii &= @as(u64, maxInt(u64)) << 32;
135 hi = @bitCast(f64, hii);
136 const lo = f - hi - hfsq + s * (hfsq + R);
137
138 // val_hi + val_lo ~ log10(1 + f) + k * log10(2)
139 var val_hi = hi * ivln10hi;
140 const dk = @intToFloat(f64, k);
141 const y = dk * log10_2hi;
142 var val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi;
143
144 // Extra precision multiplication
145 const ww = y + val_hi;
146 val_lo += (y - ww) + val_hi;
147 val_hi = ww;
148
149 return val_lo + val_hi;
150}
151
152pub fn __log10x(a: f80) callconv(.C) f80 {
153 // TODO: more efficient implementation
154 return @floatCast(f80, log10q(a));
155}
156
157pub fn log10q(a: f128) callconv(.C) f128 {
158 // TODO: more correct implementation
159 return log10(@floatCast(f64, a));
160}
161
162test "log10_32" {
163 const epsilon = 0.000001;
164
165 try testing.expect(math.approxEqAbs(f32, log10f(0.2), -0.698970, epsilon));
166 try testing.expect(math.approxEqAbs(f32, log10f(0.8923), -0.049489, epsilon));
167 try testing.expect(math.approxEqAbs(f32, log10f(1.5), 0.176091, epsilon));
168 try testing.expect(math.approxEqAbs(f32, log10f(37.45), 1.573452, epsilon));
169 try testing.expect(math.approxEqAbs(f32, log10f(89.123), 1.94999, epsilon));
170 try testing.expect(math.approxEqAbs(f32, log10f(123123.234375), 5.09034, epsilon));
171}
172
173test "log10_64" {
174 const epsilon = 0.000001;
175
176 try testing.expect(math.approxEqAbs(f64, log10(0.2), -0.698970, epsilon));
177 try testing.expect(math.approxEqAbs(f64, log10(0.8923), -0.049489, epsilon));
178 try testing.expect(math.approxEqAbs(f64, log10(1.5), 0.176091, epsilon));
179 try testing.expect(math.approxEqAbs(f64, log10(37.45), 1.573452, epsilon));
180 try testing.expect(math.approxEqAbs(f64, log10(89.123), 1.94999, epsilon));
181 try testing.expect(math.approxEqAbs(f64, log10(123123.234375), 5.09034, epsilon));
182}
183
184test "log10_32.special" {
185 try testing.expect(math.isPositiveInf(log10f(math.inf(f32))));
186 try testing.expect(math.isNegativeInf(log10f(0.0)));
187 try testing.expect(math.isNan(log10f(-1.0)));
188 try testing.expect(math.isNan(log10f(math.nan(f32))));
189}
190
191test "log10_64.special" {
192 try testing.expect(math.isPositiveInf(log10(math.inf(f64))));
193 try testing.expect(math.isNegativeInf(log10(0.0)));
194 try testing.expect(math.isNan(log10(-1.0)));
195 try testing.expect(math.isNan(log10(math.nan(f64))));
196}
lib/std/special/compiler_rt/log2.zig created+185
......@@ -0,0 +1,185 @@
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/log2f.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10const maxInt = std.math.maxInt;
11
12pub fn __log2h(a: f16) callconv(.C) f16 {
13 // TODO: more efficient implementation
14 return @floatCast(f16, log2f(a));
15}
16
17pub fn log2f(x_: f32) callconv(.C) f32 {
18 const ivln2hi: f32 = 1.4428710938e+00;
19 const ivln2lo: f32 = -1.7605285393e-04;
20 const Lg1: f32 = 0xaaaaaa.0p-24;
21 const Lg2: f32 = 0xccce13.0p-25;
22 const Lg3: f32 = 0x91e9ee.0p-25;
23 const Lg4: f32 = 0xf89e26.0p-26;
24
25 var x = x_;
26 var u = @bitCast(u32, x);
27 var ix = u;
28 var k: i32 = 0;
29
30 // x < 2^(-126)
31 if (ix < 0x00800000 or ix >> 31 != 0) {
32 // log(+-0) = -inf
33 if (ix << 1 == 0) {
34 return -math.inf(f32);
35 }
36 // log(-#) = nan
37 if (ix >> 31 != 0) {
38 return math.nan(f32);
39 }
40
41 k -= 25;
42 x *= 0x1.0p25;
43 ix = @bitCast(u32, x);
44 } else if (ix >= 0x7F800000) {
45 return x;
46 } else if (ix == 0x3F800000) {
47 return 0;
48 }
49
50 // x into [sqrt(2) / 2, sqrt(2)]
51 ix += 0x3F800000 - 0x3F3504F3;
52 k += @intCast(i32, ix >> 23) - 0x7F;
53 ix = (ix & 0x007FFFFF) + 0x3F3504F3;
54 x = @bitCast(f32, ix);
55
56 const f = x - 1.0;
57 const s = f / (2.0 + f);
58 const z = s * s;
59 const w = z * z;
60 const t1 = w * (Lg2 + w * Lg4);
61 const t2 = z * (Lg1 + w * Lg3);
62 const R = t2 + t1;
63 const hfsq = 0.5 * f * f;
64
65 var hi = f - hfsq;
66 u = @bitCast(u32, hi);
67 u &= 0xFFFFF000;
68 hi = @bitCast(f32, u);
69 const lo = f - hi - hfsq + s * (hfsq + R);
70 return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + @intToFloat(f32, k);
71}
72
73pub fn log2(x_: f64) callconv(.C) f64 {
74 const ivln2hi: f64 = 1.44269504072144627571e+00;
75 const ivln2lo: f64 = 1.67517131648865118353e-10;
76 const Lg1: f64 = 6.666666666666735130e-01;
77 const Lg2: f64 = 3.999999999940941908e-01;
78 const Lg3: f64 = 2.857142874366239149e-01;
79 const Lg4: f64 = 2.222219843214978396e-01;
80 const Lg5: f64 = 1.818357216161805012e-01;
81 const Lg6: f64 = 1.531383769920937332e-01;
82 const Lg7: f64 = 1.479819860511658591e-01;
83
84 var x = x_;
85 var ix = @bitCast(u64, x);
86 var hx = @intCast(u32, ix >> 32);
87 var k: i32 = 0;
88
89 if (hx < 0x00100000 or hx >> 31 != 0) {
90 // log(+-0) = -inf
91 if (ix << 1 == 0) {
92 return -math.inf(f64);
93 }
94 // log(-#) = nan
95 if (hx >> 31 != 0) {
96 return math.nan(f64);
97 }
98
99 // subnormal, scale x
100 k -= 54;
101 x *= 0x1.0p54;
102 hx = @intCast(u32, @bitCast(u64, x) >> 32);
103 } else if (hx >= 0x7FF00000) {
104 return x;
105 } else if (hx == 0x3FF00000 and ix << 32 == 0) {
106 return 0;
107 }
108
109 // x into [sqrt(2) / 2, sqrt(2)]
110 hx += 0x3FF00000 - 0x3FE6A09E;
111 k += @intCast(i32, hx >> 20) - 0x3FF;
112 hx = (hx & 0x000FFFFF) + 0x3FE6A09E;
113 ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF);
114 x = @bitCast(f64, ix);
115
116 const f = x - 1.0;
117 const hfsq = 0.5 * f * f;
118 const s = f / (2.0 + f);
119 const z = s * s;
120 const w = z * z;
121 const t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
122 const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
123 const R = t2 + t1;
124
125 // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f)
126 var hi = f - hfsq;
127 var hii = @bitCast(u64, hi);
128 hii &= @as(u64, maxInt(u64)) << 32;
129 hi = @bitCast(f64, hii);
130 const lo = f - hi - hfsq + s * (hfsq + R);
131
132 var val_hi = hi * ivln2hi;
133 var val_lo = (lo + hi) * ivln2lo + lo * ivln2hi;
134
135 // spadd(val_hi, val_lo, y)
136 const y = @intToFloat(f64, k);
137 const ww = y + val_hi;
138 val_lo += (y - ww) + val_hi;
139 val_hi = ww;
140
141 return val_lo + val_hi;
142}
143
144pub fn __log2x(a: f80) callconv(.C) f80 {
145 // TODO: more efficient implementation
146 return @floatCast(f80, log2q(a));
147}
148
149pub fn log2q(a: f128) callconv(.C) f128 {
150 return math.log2(a);
151}
152
153test "log2_32" {
154 const epsilon = 0.000001;
155
156 try expect(math.approxEqAbs(f32, log2f(0.2), -2.321928, epsilon));
157 try expect(math.approxEqAbs(f32, log2f(0.8923), -0.164399, epsilon));
158 try expect(math.approxEqAbs(f32, log2f(1.5), 0.584962, epsilon));
159 try expect(math.approxEqAbs(f32, log2f(37.45), 5.226894, epsilon));
160 try expect(math.approxEqAbs(f32, log2f(123123.234375), 16.909744, epsilon));
161}
162
163test "log2_64" {
164 const epsilon = 0.000001;
165
166 try expect(math.approxEqAbs(f64, log2(0.2), -2.321928, epsilon));
167 try expect(math.approxEqAbs(f64, log2(0.8923), -0.164399, epsilon));
168 try expect(math.approxEqAbs(f64, log2(1.5), 0.584962, epsilon));
169 try expect(math.approxEqAbs(f64, log2(37.45), 5.226894, epsilon));
170 try expect(math.approxEqAbs(f64, log2(123123.234375), 16.909744, epsilon));
171}
172
173test "log2_32.special" {
174 try expect(math.isPositiveInf(log2f(math.inf(f32))));
175 try expect(math.isNegativeInf(log2f(0.0)));
176 try expect(math.isNan(log2f(-1.0)));
177 try expect(math.isNan(log2f(math.nan(f32))));
178}
179
180test "log2_64.special" {
181 try expect(math.isPositiveInf(log2(math.inf(f64))));
182 try expect(math.isNegativeInf(log2(0.0)));
183 try expect(math.isNan(log2(-1.0)));
184 try expect(math.isNan(log2(math.nan(f64))));
185}
lib/std/special/compiler_rt/rem_pio2.zig created+198
......@@ -0,0 +1,198 @@
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/__rem_pio2.c
5
6const std = @import("std");
7const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large;
8const math = std.math;
9
10const toint = 1.5 / math.floatEps(f64);
11// pi/4
12const pio4 = 0x1.921fb54442d18p-1;
13// invpio2: 53 bits of 2/pi
14const invpio2 = 6.36619772367581382433e-01; // 0x3FE45F30, 0x6DC9C883
15// pio2_1: first 33 bit of pi/2
16const pio2_1 = 1.57079632673412561417e+00; // 0x3FF921FB, 0x54400000
17// pio2_1t: pi/2 - pio2_1
18const pio2_1t = 6.07710050650619224932e-11; // 0x3DD0B461, 0x1A626331
19// pio2_2: second 33 bit of pi/2
20const pio2_2 = 6.07710050630396597660e-11; // 0x3DD0B461, 0x1A600000
21// pio2_2t: pi/2 - (pio2_1+pio2_2)
22const pio2_2t = 2.02226624879595063154e-21; // 0x3BA3198A, 0x2E037073
23// pio2_3: third 33 bit of pi/2
24const pio2_3 = 2.02226624871116645580e-21; // 0x3BA3198A, 0x2E000000
25// pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3)
26const pio2_3t = 8.47842766036889956997e-32; // 0x397B839A, 0x252049C1
27
28fn U(x: anytype) usize {
29 return @intCast(usize, x);
30}
31
32fn medium(ix: u32, x: f64, y: *[2]f64) i32 {
33 var w: f64 = undefined;
34 var t: f64 = undefined;
35 var r: f64 = undefined;
36 var @"fn": f64 = undefined;
37 var n: i32 = undefined;
38 var ex: i32 = undefined;
39 var ey: i32 = undefined;
40 var ui: u64 = undefined;
41
42 // rint(x/(pi/2))
43 @"fn" = x * invpio2 + toint - toint;
44 n = @floatToInt(i32, @"fn");
45 r = x - @"fn" * pio2_1;
46 w = @"fn" * pio2_1t; // 1st round, good to 85 bits
47 // Matters with directed rounding.
48 if (r - w < -pio4) {
49 n -= 1;
50 @"fn" -= 1;
51 r = x - @"fn" * pio2_1;
52 w = @"fn" * pio2_1t;
53 } else if (r - w > pio4) {
54 n += 1;
55 @"fn" += 1;
56 r = x - @"fn" * pio2_1;
57 w = @"fn" * pio2_1t;
58 }
59 y[0] = r - w;
60 ui = @bitCast(u64, y[0]);
61 ey = @intCast(i32, (ui >> 52) & 0x7ff);
62 ex = @intCast(i32, ix >> 20);
63 if (ex - ey > 16) { // 2nd round, good to 118 bits
64 t = r;
65 w = @"fn" * pio2_2;
66 r = t - w;
67 w = @"fn" * pio2_2t - ((t - r) - w);
68 y[0] = r - w;
69 ui = @bitCast(u64, y[0]);
70 ey = @intCast(i32, (ui >> 52) & 0x7ff);
71 if (ex - ey > 49) { // 3rd round, good to 151 bits, covers all cases
72 t = r;
73 w = @"fn" * pio2_3;
74 r = t - w;
75 w = @"fn" * pio2_3t - ((t - r) - w);
76 y[0] = r - w;
77 }
78 }
79 y[1] = (r - y[0]) - w;
80 return n;
81}
82
83// Returns the remainder of x rem pi/2 in y[0]+y[1]
84//
85// use rem_pio2_large() for large x
86//
87// caller must handle the case when reduction is not needed: |x| ~<= pi/4 */
88pub fn rem_pio2(x: f64, y: *[2]f64) i32 {
89 var z: f64 = undefined;
90 var tx: [3]f64 = undefined;
91 var ty: [2]f64 = undefined;
92 var n: i32 = undefined;
93 var ix: u32 = undefined;
94 var sign: bool = undefined;
95 var i: i32 = undefined;
96 var ui: u64 = undefined;
97
98 ui = @bitCast(u64, x);
99 sign = ui >> 63 != 0;
100 ix = @truncate(u32, (ui >> 32) & 0x7fffffff);
101 if (ix <= 0x400f6a7a) { // |x| ~<= 5pi/4
102 if ((ix & 0xfffff) == 0x921fb) { // |x| ~= pi/2 or 2pi/2
103 return medium(ix, x, y);
104 }
105 if (ix <= 0x4002d97c) { // |x| ~<= 3pi/4
106 if (!sign) {
107 z = x - pio2_1; // one round good to 85 bits
108 y[0] = z - pio2_1t;
109 y[1] = (z - y[0]) - pio2_1t;
110 return 1;
111 } else {
112 z = x + pio2_1;
113 y[0] = z + pio2_1t;
114 y[1] = (z - y[0]) + pio2_1t;
115 return -1;
116 }
117 } else {
118 if (!sign) {
119 z = x - 2 * pio2_1;
120 y[0] = z - 2 * pio2_1t;
121 y[1] = (z - y[0]) - 2 * pio2_1t;
122 return 2;
123 } else {
124 z = x + 2 * pio2_1;
125 y[0] = z + 2 * pio2_1t;
126 y[1] = (z - y[0]) + 2 * pio2_1t;
127 return -2;
128 }
129 }
130 }
131 if (ix <= 0x401c463b) { // |x| ~<= 9pi/4
132 if (ix <= 0x4015fdbc) { // |x| ~<= 7pi/4
133 if (ix == 0x4012d97c) { // |x| ~= 3pi/2
134 return medium(ix, x, y);
135 }
136 if (!sign) {
137 z = x - 3 * pio2_1;
138 y[0] = z - 3 * pio2_1t;
139 y[1] = (z - y[0]) - 3 * pio2_1t;
140 return 3;
141 } else {
142 z = x + 3 * pio2_1;
143 y[0] = z + 3 * pio2_1t;
144 y[1] = (z - y[0]) + 3 * pio2_1t;
145 return -3;
146 }
147 } else {
148 if (ix == 0x401921fb) { // |x| ~= 4pi/2 */
149 return medium(ix, x, y);
150 }
151 if (!sign) {
152 z = x - 4 * pio2_1;
153 y[0] = z - 4 * pio2_1t;
154 y[1] = (z - y[0]) - 4 * pio2_1t;
155 return 4;
156 } else {
157 z = x + 4 * pio2_1;
158 y[0] = z + 4 * pio2_1t;
159 y[1] = (z - y[0]) + 4 * pio2_1t;
160 return -4;
161 }
162 }
163 }
164 if (ix < 0x413921fb) { // |x| ~< 2^20*(pi/2), medium size
165 return medium(ix, x, y);
166 }
167 // all other (large) arguments
168 if (ix >= 0x7ff00000) { // x is inf or NaN
169 y[0] = x - x;
170 y[1] = y[0];
171 return 0;
172 }
173 // set z = scalbn(|x|,-ilogb(x)+23)
174 ui = @bitCast(u64, x);
175 ui &= std.math.maxInt(u64) >> 12;
176 ui |= @as(u64, 0x3ff + 23) << 52;
177 z = @bitCast(f64, ui);
178
179 i = 0;
180 while (i < 2) : (i += 1) {
181 tx[U(i)] = @intToFloat(f64, @floatToInt(i32, z));
182 z = (z - tx[U(i)]) * 0x1p24;
183 }
184 tx[U(i)] = z;
185 // skip zero terms, first term is non-zero
186 while (tx[U(i)] == 0.0) {
187 i -= 1;
188 }
189 n = rem_pio2_large(tx[0..], ty[0..], @intCast(i32, (ix >> 20)) - (0x3ff + 23), i + 1, 1);
190 if (sign) {
191 y[0] = -ty[0];
192 y[1] = -ty[1];
193 return -n;
194 }
195 y[0] = ty[0];
196 y[1] = ty[1];
197 return n;
198}
lib/std/special/compiler_rt/rem_pio2_large.zig created+506
......@@ -0,0 +1,506 @@
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/__rem_pio2_large.c
5
6const std = @import("std");
7const math = std.math;
8
9const init_jk = [_]i32{ 3, 4, 4, 6 }; // initial value for jk
10
11///
12/// Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi
13///
14/// integer array, contains the (24*i)-th to (24*i+23)-th
15/// bit of 2/pi after binary point. The corresponding
16/// floating value is
17///
18/// ipio2[i] * 2^(-24(i+1)).
19///
20/// NB: This table must have at least (e0-3)/24 + jk terms.
21/// For quad precision (e0 <= 16360, jk = 6), this is 686.
22const ipio2 = [_]i32{
23 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62,
24 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A,
25 0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129,
26 0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41,
27 0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8,
28 0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF,
29 0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5,
30 0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08,
31 0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3,
32 0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
33 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B,
34
35 0x47C419, 0xC367CD, 0xDCE809, 0x2A8359, 0xC4768B, 0x961CA6,
36 0xDDAF44, 0xD15719, 0x053EA5, 0xFF0705, 0x3F7E33, 0xE832C2,
37 0xDE4F98, 0x327DBB, 0xC33D26, 0xEF6B1E, 0x5EF89F, 0x3A1F35,
38 0xCAF27F, 0x1D87F1, 0x21907C, 0x7C246A, 0xFA6ED5, 0x772D30,
39 0x433B15, 0xC614B5, 0x9D19C3, 0xC2C4AD, 0x414D2C, 0x5D000C,
40 0x467D86, 0x2D71E3, 0x9AC69B, 0x006233, 0x7CD2B4, 0x97A7B4,
41 0xD55537, 0xF63ED7, 0x1810A3, 0xFC764D, 0x2A9D64, 0xABD770,
42 0xF87C63, 0x57B07A, 0xE71517, 0x5649C0, 0xD9D63B, 0x3884A7,
43 0xCB2324, 0x778AD6, 0x23545A, 0xB91F00, 0x1B0AF1, 0xDFCE19,
44 0xFF319F, 0x6A1E66, 0x615799, 0x47FBAC, 0xD87F7E, 0xB76522,
45 0x89E832, 0x60BFE6, 0xCDC4EF, 0x09366C, 0xD43F5D, 0xD7DE16,
46 0xDE3B58, 0x929BDE, 0x2822D2, 0xE88628, 0x4D58E2, 0x32CAC6,
47 0x16E308, 0xCB7DE0, 0x50C017, 0xA71DF3, 0x5BE018, 0x34132E,
48 0x621283, 0x014883, 0x5B8EF5, 0x7FB0AD, 0xF2E91E, 0x434A48,
49 0xD36710, 0xD8DDAA, 0x425FAE, 0xCE616A, 0xA4280A, 0xB499D3,
50 0xF2A606, 0x7F775C, 0x83C2A3, 0x883C61, 0x78738A, 0x5A8CAF,
51 0xBDD76F, 0x63A62D, 0xCBBFF4, 0xEF818D, 0x67C126, 0x45CA55,
52 0x36D9CA, 0xD2A828, 0x8D61C2, 0x77C912, 0x142604, 0x9B4612,
53 0xC459C4, 0x44C5C8, 0x91B24D, 0xF31700, 0xAD43D4, 0xE54929,
54 0x10D5FD, 0xFCBE00, 0xCC941E, 0xEECE70, 0xF53E13, 0x80F1EC,
55 0xC3E7B3, 0x28F8C7, 0x940593, 0x3E71C1, 0xB3092E, 0xF3450B,
56 0x9C1288, 0x7B20AB, 0x9FB52E, 0xC29247, 0x2F327B, 0x6D550C,
57 0x90A772, 0x1FE76B, 0x96CB31, 0x4A1679, 0xE27941, 0x89DFF4,
58 0x9794E8, 0x84E6E2, 0x973199, 0x6BED88, 0x365F5F, 0x0EFDBB,
59 0xB49A48, 0x6CA467, 0x427271, 0x325D8D, 0xB8159F, 0x09E5BC,
60 0x25318D, 0x3974F7, 0x1C0530, 0x010C0D, 0x68084B, 0x58EE2C,
61 0x90AA47, 0x02E774, 0x24D6BD, 0xA67DF7, 0x72486E, 0xEF169F,
62 0xA6948E, 0xF691B4, 0x5153D1, 0xF20ACF, 0x339820, 0x7E4BF5,
63 0x6863B2, 0x5F3EDD, 0x035D40, 0x7F8985, 0x295255, 0xC06437,
64 0x10D86D, 0x324832, 0x754C5B, 0xD4714E, 0x6E5445, 0xC1090B,
65 0x69F52A, 0xD56614, 0x9D0727, 0x50045D, 0xDB3BB4, 0xC576EA,
66 0x17F987, 0x7D6B49, 0xBA271D, 0x296996, 0xACCCC6, 0x5414AD,
67 0x6AE290, 0x89D988, 0x50722C, 0xBEA404, 0x940777, 0x7030F3,
68 0x27FC00, 0xA871EA, 0x49C266, 0x3DE064, 0x83DD97, 0x973FA3,
69 0xFD9443, 0x8C860D, 0xDE4131, 0x9D3992, 0x8C70DD, 0xE7B717,
70 0x3BDF08, 0x2B3715, 0xA0805C, 0x93805A, 0x921110, 0xD8E80F,
71 0xAF806C, 0x4BFFDB, 0x0F9038, 0x761859, 0x15A562, 0xBBCB61,
72 0xB989C7, 0xBD4010, 0x04F2D2, 0x277549, 0xF6B6EB, 0xBB22DB,
73 0xAA140A, 0x2F2689, 0x768364, 0x333B09, 0x1A940E, 0xAA3A51,
74 0xC2A31D, 0xAEEDAF, 0x12265C, 0x4DC26D, 0x9C7A2D, 0x9756C0,
75 0x833F03, 0xF6F009, 0x8C402B, 0x99316D, 0x07B439, 0x15200C,
76 0x5BC3D8, 0xC492F5, 0x4BADC6, 0xA5CA4E, 0xCD37A7, 0x36A9E6,
77 0x9492AB, 0x6842DD, 0xDE6319, 0xEF8C76, 0x528B68, 0x37DBFC,
78 0xABA1AE, 0x3115DF, 0xA1AE00, 0xDAFB0C, 0x664D64, 0xB705ED,
79 0x306529, 0xBF5657, 0x3AFF47, 0xB9F96A, 0xF3BE75, 0xDF9328,
80 0x3080AB, 0xF68C66, 0x15CB04, 0x0622FA, 0x1DE4D9, 0xA4B33D,
81 0x8F1B57, 0x09CD36, 0xE9424E, 0xA4BE13, 0xB52333, 0x1AAAF0,
82 0xA8654F, 0xA5C1D2, 0x0F3F0B, 0xCD785B, 0x76F923, 0x048B7B,
83 0x721789, 0x53A6C6, 0xE26E6F, 0x00EBEF, 0x584A9B, 0xB7DAC4,
84 0xBA66AA, 0xCFCF76, 0x1D02D1, 0x2DF1B1, 0xC1998C, 0x77ADC3,
85 0xDA4886, 0xA05DF7, 0xF480C6, 0x2FF0AC, 0x9AECDD, 0xBC5C3F,
86 0x6DDED0, 0x1FC790, 0xB6DB2A, 0x3A25A3, 0x9AAF00, 0x9353AD,
87 0x0457B6, 0xB42D29, 0x7E804B, 0xA707DA, 0x0EAA76, 0xA1597B,
88 0x2A1216, 0x2DB7DC, 0xFDE5FA, 0xFEDB89, 0xFDBE89, 0x6C76E4,
89 0xFCA906, 0x70803E, 0x156E85, 0xFF87FD, 0x073E28, 0x336761,
90 0x86182A, 0xEABD4D, 0xAFE7B3, 0x6E6D8F, 0x396795, 0x5BBF31,
91 0x48D784, 0x16DF30, 0x432DC7, 0x356125, 0xCE70C9, 0xB8CB30,
92 0xFD6CBF, 0xA200A4, 0xE46C05, 0xA0DD5A, 0x476F21, 0xD21262,
93 0x845CB9, 0x496170, 0xE0566B, 0x015299, 0x375550, 0xB7D51E,
94 0xC4F133, 0x5F6E13, 0xE4305D, 0xA92E85, 0xC3B21D, 0x3632A1,
95 0xA4B708, 0xD4B1EA, 0x21F716, 0xE4698F, 0x77FF27, 0x80030C,
96 0x2D408D, 0xA0CD4F, 0x99A520, 0xD3A2B3, 0x0A5D2F, 0x42F9B4,
97 0xCBDA11, 0xD0BE7D, 0xC1DB9B, 0xBD17AB, 0x81A2CA, 0x5C6A08,
98 0x17552E, 0x550027, 0xF0147F, 0x8607E1, 0x640B14, 0x8D4196,
99 0xDEBE87, 0x2AFDDA, 0xB6256B, 0x34897B, 0xFEF305, 0x9EBFB9,
100 0x4F6A68, 0xA82A4A, 0x5AC44F, 0xBCF82D, 0x985AD7, 0x95C7F4,
101 0x8D4D0D, 0xA63A20, 0x5F57A4, 0xB13F14, 0x953880, 0x0120CC,
102 0x86DD71, 0xB6DEC9, 0xF560BF, 0x11654D, 0x6B0701, 0xACB08C,
103 0xD0C0B2, 0x485551, 0x0EFB1E, 0xC37295, 0x3B06A3, 0x3540C0,
104 0x7BDC06, 0xCC45E0, 0xFA294E, 0xC8CAD6, 0x41F3E8, 0xDE647C,
105 0xD8649B, 0x31BED9, 0xC397A4, 0xD45877, 0xC5E369, 0x13DAF0,
106 0x3C3ABA, 0x461846, 0x5F7555, 0xF5BDD2, 0xC6926E, 0x5D2EAC,
107 0xED440E, 0x423E1C, 0x87C461, 0xE9FD29, 0xF3D6E7, 0xCA7C22,
108 0x35916F, 0xC5E008, 0x8DD7FF, 0xE26A6E, 0xC6FDB0, 0xC10893,
109 0x745D7C, 0xB2AD6B, 0x9D6ECD, 0x7B723E, 0x6A11C6, 0xA9CFF7,
110 0xDF7329, 0xBAC9B5, 0x5100B7, 0x0DB2E2, 0x24BA74, 0x607DE5,
111 0x8AD874, 0x2C150D, 0x0C1881, 0x94667E, 0x162901, 0x767A9F,
112 0xBEFDFD, 0xEF4556, 0x367ED9, 0x13D9EC, 0xB9BA8B, 0xFC97C4,
113 0x27A831, 0xC36EF1, 0x36C594, 0x56A8D8, 0xB5A8B4, 0x0ECCCF,
114 0x2D8912, 0x34576F, 0x89562C, 0xE3CE99, 0xB920D6, 0xAA5E6B,
115 0x9C2A3E, 0xCC5F11, 0x4A0BFD, 0xFBF4E1, 0x6D3B8E, 0x2C86E2,
116 0x84D4E9, 0xA9B4FC, 0xD1EEEF, 0xC9352E, 0x61392F, 0x442138,
117 0xC8D91B, 0x0AFC81, 0x6A4AFB, 0xD81C2F, 0x84B453, 0x8C994E,
118 0xCC2254, 0xDC552A, 0xD6C6C0, 0x96190B, 0xB8701A, 0x649569,
119 0x605A26, 0xEE523F, 0x0F117F, 0x11B5F4, 0xF5CBFC, 0x2DBC34,
120 0xEEBC34, 0xCC5DE8, 0x605EDD, 0x9B8E67, 0xEF3392, 0xB817C9,
121 0x9B5861, 0xBC57E1, 0xC68351, 0x103ED8, 0x4871DD, 0xDD1C2D,
122 0xA118AF, 0x462C21, 0xD7F359, 0x987AD9, 0xC0549E, 0xFA864F,
123 0xFC0656, 0xAE79E5, 0x362289, 0x22AD38, 0xDC9367, 0xAAE855,
124 0x382682, 0x9BE7CA, 0xA40D51, 0xB13399, 0x0ED7A9, 0x480569,
125 0xF0B265, 0xA7887F, 0x974C88, 0x36D1F9, 0xB39221, 0x4A827B,
126 0x21CF98, 0xDC9F40, 0x5547DC, 0x3A74E1, 0x42EB67, 0xDF9DFE,
127 0x5FD45E, 0xA4677B, 0x7AACBA, 0xA2F655, 0x23882B, 0x55BA41,
128 0x086E59, 0x862A21, 0x834739, 0xE6E389, 0xD49EE5, 0x40FB49,
129 0xE956FF, 0xCA0F1C, 0x8A59C5, 0x2BFA94, 0xC5C1D3, 0xCFC50F,
130 0xAE5ADB, 0x86C547, 0x624385, 0x3B8621, 0x94792C, 0x876110,
131 0x7B4C2A, 0x1A2C80, 0x12BF43, 0x902688, 0x893C78, 0xE4C4A8,
132 0x7BDBE5, 0xC23AC4, 0xEAF426, 0x8A67F7, 0xBF920D, 0x2BA365,
133 0xB1933D, 0x0B7CBD, 0xDC51A4, 0x63DD27, 0xDDE169, 0x19949A,
134 0x9529A8, 0x28CE68, 0xB4ED09, 0x209F44, 0xCA984E, 0x638270,
135 0x237C7E, 0x32B90F, 0x8EF5A7, 0xE75614, 0x08F121, 0x2A9DB5,
136 0x4D7E6F, 0x5119A5, 0xABF9B5, 0xD6DF82, 0x61DD96, 0x023616,
137 0x9F3AC4, 0xA1A283, 0x6DED72, 0x7A8D39, 0xA9B882, 0x5C326B,
138 0x5B2746, 0xED3400, 0x7700D2, 0x55F4FC, 0x4D5901, 0x8071E0,
139};
140
141const PIo2 = [_]f64{
142 1.57079625129699707031e+00, // 0x3FF921FB, 0x40000000
143 7.54978941586159635335e-08, // 0x3E74442D, 0x00000000
144 5.39030252995776476554e-15, // 0x3CF84698, 0x80000000
145 3.28200341580791294123e-22, // 0x3B78CC51, 0x60000000
146 1.27065575308067607349e-29, // 0x39F01B83, 0x80000000
147 1.22933308981111328932e-36, // 0x387A2520, 0x40000000
148 2.73370053816464559624e-44, // 0x36E38222, 0x80000000
149 2.16741683877804819444e-51, // 0x3569F31D, 0x00000000
150};
151
152fn U(x: anytype) usize {
153 return @intCast(usize, x);
154}
155
156/// Returns the last three digits of N with y = x - N*pi/2 so that |y| < pi/2.
157///
158/// The method is to compute the integer (mod 8) and fraction parts of
159/// (2/pi)*x without doing the full multiplication. In general we
160/// skip the part of the product that are known to be a huge integer (
161/// more accurately, = 0 mod 8 ). Thus the number of operations are
162/// independent of the exponent of the input.
163///
164/// (2/pi) is represented by an array of 24-bit integers in ipio2[].
165///
166/// Input parameters:
167/// x[] The input value (must be positive) is broken into nx
168/// pieces of 24-bit integers in double precision format.
169/// x[i] will be the i-th 24 bit of x. The scaled exponent
170/// of x[0] is given in input parameter e0 (i.e., x[0]*2^e0
171/// match x's up to 24 bits.
172///
173/// Example of breaking a double positive z into x[0]+x[1]+x[2]:
174/// e0 = ilogb(z)-23
175/// z = scalbn(z,-e0)
176/// for i = 0,1,2
177/// x[i] = floor(z)
178/// z = (z-x[i])*2**24
179///
180///
181/// y[] ouput result in an array of double precision numbers.
182/// The dimension of y[] is:
183/// 24-bit precision 1
184/// 53-bit precision 2
185/// 64-bit precision 2
186/// 113-bit precision 3
187/// The actual value is the sum of them. Thus for 113-bit
188/// precison, one may have to do something like:
189///
190/// long double t,w,r_head, r_tail;
191/// t = (long double)y[2] + (long double)y[1];
192/// w = (long double)y[0];
193/// r_head = t+w;
194/// r_tail = w - (r_head - t);
195///
196/// e0 The exponent of x[0]. Must be <= 16360 or you need to
197/// expand the ipio2 table.
198///
199/// nx dimension of x[]
200///
201/// prec an integer indicating the precision:
202/// 0 24 bits (single)
203/// 1 53 bits (double)
204/// 2 64 bits (extended)
205/// 3 113 bits (quad)
206///
207/// Here is the description of some local variables:
208///
209/// jk jk+1 is the initial number of terms of ipio2[] needed
210/// in the computation. The minimum and recommended value
211/// for jk is 3,4,4,6 for single, double, extended, and quad.
212/// jk+1 must be 2 larger than you might expect so that our
213/// recomputation test works. (Up to 24 bits in the integer
214/// part (the 24 bits of it that we compute) and 23 bits in
215/// the fraction part may be lost to cancelation before we
216/// recompute.)
217///
218/// jz local integer variable indicating the number of
219/// terms of ipio2[] used.
220///
221/// jx nx - 1
222///
223/// jv index for pointing to the suitable ipio2[] for the
224/// computation. In general, we want
225/// ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8
226/// is an integer. Thus
227/// e0-3-24*jv >= 0 or (e0-3)/24 >= jv
228/// Hence jv = max(0,(e0-3)/24).
229///
230/// jp jp+1 is the number of terms in PIo2[] needed, jp = jk.
231///
232/// q[] double array with integral value, representing the
233/// 24-bits chunk of the product of x and 2/pi.
234///
235/// q0 the corresponding exponent of q[0]. Note that the
236/// exponent for q[i] would be q0-24*i.
237///
238/// PIo2[] double precision array, obtained by cutting pi/2
239/// into 24 bits chunks.
240///
241/// f[] ipio2[] in floating point
242///
243/// iq[] integer array by breaking up q[] in 24-bits chunk.
244///
245/// fq[] final product of x*(2/pi) in fq[0],..,fq[jk]
246///
247/// ih integer. If >0 it indicates q[] is >= 0.5, hence
248/// it also indicates the *sign* of the result.
249///
250///
251///
252/// Constants:
253/// The hexadecimal values are the intended ones for the following
254/// constants. The decimal values may be used, provided that the
255/// compiler will convert from decimal to binary accurately enough
256/// to produce the hexadecimal values shown.
257///
258pub fn rem_pio2_large(x: []f64, y: []f64, e0: i32, nx: i32, prec: usize) i32 {
259 var jz: i32 = undefined;
260 var jx: i32 = undefined;
261 var jv: i32 = undefined;
262 var jp: i32 = undefined;
263 var jk: i32 = undefined;
264 var carry: i32 = undefined;
265 var n: i32 = undefined;
266 var iq: [20]i32 = undefined;
267 var i: i32 = undefined;
268 var j: i32 = undefined;
269 var k: i32 = undefined;
270 var m: i32 = undefined;
271 var q0: i32 = undefined;
272 var ih: i32 = undefined;
273
274 var z: f64 = undefined;
275 var fw: f64 = undefined;
276 var f: [20]f64 = undefined;
277 var fq: [20]f64 = undefined;
278 var q: [20]f64 = undefined;
279
280 // initialize jk
281 jk = init_jk[prec];
282 jp = jk;
283
284 // determine jx,jv,q0, note that 3>q0
285 jx = nx - 1;
286 jv = @divFloor(e0 - 3, 24);
287 if (jv < 0) jv = 0;
288 q0 = e0 - 24 * (jv + 1);
289
290 // set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk]
291 j = jv - jx;
292 m = jx + jk;
293 i = 0;
294 while (i <= m) : ({
295 i += 1;
296 j += 1;
297 }) {
298 f[U(i)] = if (j < 0) 0.0 else @intToFloat(f64, ipio2[U(j)]);
299 }
300
301 // compute q[0],q[1],...q[jk]
302 i = 0;
303 while (i <= jk) : (i += 1) {
304 j = 0;
305 fw = 0;
306 while (j <= jx) : (j += 1) {
307 fw += x[U(j)] * f[U(jx + i - j)];
308 }
309 q[U(i)] = fw;
310 }
311
312 jz = jk;
313
314 // This is to handle a non-trivial goto translation from C.
315 // An unconditional return statement is found at the end of this loop.
316 recompute: while (true) {
317 // distill q[] into iq[] reversingly
318 i = 0;
319 j = jz;
320 z = q[U(jz)];
321 while (j > 0) : ({
322 i += 1;
323 j -= 1;
324 }) {
325 fw = @intToFloat(f64, @floatToInt(i32, 0x1p-24 * z));
326 iq[U(i)] = @floatToInt(i32, z - 0x1p24 * fw);
327 z = q[U(j - 1)] + fw;
328 }
329
330 // compute n
331 z = math.scalbn(z, q0); // actual value of z
332 z -= 8.0 * @floor(z * 0.125); // trim off integer >= 8
333 n = @floatToInt(i32, z);
334 z -= @intToFloat(f64, n);
335 ih = 0;
336 if (q0 > 0) { // need iq[jz-1] to determine n
337 i = iq[U(jz - 1)] >> @intCast(u5, 24 - q0);
338 n += i;
339 iq[U(jz - 1)] -= i << @intCast(u5, 24 - q0);
340 ih = iq[U(jz - 1)] >> @intCast(u5, 23 - q0);
341 } else if (q0 == 0) {
342 ih = iq[U(jz - 1)] >> 23;
343 } else if (z >= 0.5) {
344 ih = 2;
345 }
346
347 if (ih > 0) { // q > 0.5
348 n += 1;
349 carry = 0;
350 i = 0;
351 while (i < jz) : (i += 1) { // compute 1-q
352 j = iq[U(i)];
353 if (carry == 0) {
354 if (j != 0) {
355 carry = 1;
356 iq[U(i)] = 0x1000000 - j;
357 }
358 } else {
359 iq[U(i)] = 0xffffff - j;
360 }
361 }
362 if (q0 > 0) { // rare case: chance is 1 in 12
363 switch (q0) {
364 1 => iq[U(jz - 1)] &= 0x7fffff,
365 2 => iq[U(jz - 1)] &= 0x3fffff,
366 else => unreachable,
367 }
368 }
369 if (ih == 2) {
370 z = 1.0 - z;
371 if (carry != 0) {
372 z -= math.scalbn(@as(f64, 1.0), q0);
373 }
374 }
375 }
376
377 // check if recomputation is needed
378 if (z == 0.0) {
379 j = 0;
380 i = jz - 1;
381 while (i >= jk) : (i -= 1) {
382 j |= iq[U(i)];
383 }
384
385 if (j == 0) { // need recomputation
386 k = 1;
387 while (iq[U(jk - k)] == 0) : (k += 1) {
388 // k = no. of terms needed
389 }
390
391 i = jz + 1;
392 while (i <= jz + k) : (i += 1) { // add q[jz+1] to q[jz+k]
393 f[U(jx + i)] = @intToFloat(f64, ipio2[U(jv + i)]);
394 j = 0;
395 fw = 0;
396 while (j <= jx) : (j += 1) {
397 fw += x[U(j)] * f[U(jx + i - j)];
398 }
399 q[U(i)] = fw;
400 }
401 jz += k;
402 continue :recompute; // mimic goto recompute
403 }
404 }
405
406 // chop off zero terms
407 if (z == 0.0) {
408 jz -= 1;
409 q0 -= 24;
410 while (iq[U(jz)] == 0) {
411 jz -= 1;
412 q0 -= 24;
413 }
414 } else { // break z into 24-bit if necessary
415 z = math.scalbn(z, -q0);
416 if (z >= 0x1p24) {
417 fw = @intToFloat(f64, @floatToInt(i32, 0x1p-24 * z));
418 iq[U(jz)] = @floatToInt(i32, z - 0x1p24 * fw);
419 jz += 1;
420 q0 += 24;
421 iq[U(jz)] = @floatToInt(i32, fw);
422 } else {
423 iq[U(jz)] = @floatToInt(i32, z);
424 }
425 }
426
427 // convert integer "bit" chunk to floating-point value
428 fw = math.scalbn(@as(f64, 1.0), q0);
429 i = jz;
430 while (i >= 0) : (i -= 1) {
431 q[U(i)] = fw * @intToFloat(f64, iq[U(i)]);
432 fw *= 0x1p-24;
433 }
434
435 // compute PIo2[0,...,jp]*q[jz,...,0]
436 i = jz;
437 while (i >= 0) : (i -= 1) {
438 fw = 0;
439 k = 0;
440 while (k <= jp and k <= jz - i) : (k += 1) {
441 fw += PIo2[U(k)] * q[U(i + k)];
442 }
443 fq[U(jz - i)] = fw;
444 }
445
446 // compress fq[] into y[]
447 switch (prec) {
448 0 => {
449 fw = 0.0;
450 i = jz;
451 while (i >= 0) : (i -= 1) {
452 fw += fq[U(i)];
453 }
454 y[0] = if (ih == 0) fw else -fw;
455 },
456
457 1, 2 => {
458 fw = 0.0;
459 i = jz;
460 while (i >= 0) : (i -= 1) {
461 fw += fq[U(i)];
462 }
463 // TODO: drop excess precision here once double_t is used
464 fw = fw;
465 y[0] = if (ih == 0) fw else -fw;
466 fw = fq[0] - fw;
467 i = 1;
468 while (i <= jz) : (i += 1) {
469 fw += fq[U(i)];
470 }
471 y[1] = if (ih == 0) fw else -fw;
472 },
473 3 => { // painful
474 i = jz;
475 while (i > 0) : (i -= 1) {
476 fw = fq[U(i - 1)] + fq[U(i)];
477 fq[U(i)] += fq[U(i - 1)] - fw;
478 fq[U(i - 1)] = fw;
479 }
480 i = jz;
481 while (i > 1) : (i -= 1) {
482 fw = fq[U(i - 1)] + fq[U(i)];
483 fq[U(i)] += fq[U(i - 1)] - fw;
484 fq[U(i - 1)] = fw;
485 }
486 fw = 0;
487 i = jz;
488 while (i >= 2) : (i -= 1) {
489 fw += fq[U(i)];
490 }
491 if (ih == 0) {
492 y[0] = fq[0];
493 y[1] = fq[1];
494 y[2] = fw;
495 } else {
496 y[0] = -fq[0];
497 y[1] = -fq[1];
498 y[2] = -fw;
499 }
500 },
501 else => unreachable,
502 }
503
504 return n & 7;
505 }
506}
lib/std/special/compiler_rt/rem_pio2f.zig created+70
......@@ -0,0 +1,70 @@
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/__rem_pio2f.c
5
6const std = @import("std");
7const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large;
8const math = std.math;
9
10const toint = 1.5 / math.floatEps(f64);
11// pi/4
12const pio4 = 0x1.921fb6p-1;
13// invpio2: 53 bits of 2/pi
14const invpio2 = 6.36619772367581382433e-01; // 0x3FE45F30, 0x6DC9C883
15// pio2_1: first 25 bits of pi/2
16const pio2_1 = 1.57079631090164184570e+00; // 0x3FF921FB, 0x50000000
17// pio2_1t: pi/2 - pio2_1
18const pio2_1t = 1.58932547735281966916e-08; // 0x3E5110b4, 0x611A6263
19
20// Returns the remainder of x rem pi/2 in *y
21// use double precision for everything except passing x
22// use rem_pio2_large() for large x
23pub fn rem_pio2f(x: f32, y: *f64) i32 {
24 var tx: [1]f64 = undefined;
25 var ty: [1]f64 = undefined;
26 var @"fn": f64 = undefined;
27 var ix: u32 = undefined;
28 var n: i32 = undefined;
29 var sign: bool = undefined;
30 var e0: u32 = undefined;
31 var ui: u32 = undefined;
32
33 ui = @bitCast(u32, x);
34 ix = ui & 0x7fffffff;
35
36 // 25+53 bit pi is good enough for medium size
37 if (ix < 0x4dc90fdb) { // |x| ~< 2^28*(pi/2), medium size
38 // Use a specialized rint() to get fn.
39 @"fn" = @floatCast(f64, x) * invpio2 + toint - toint;
40 n = @floatToInt(i32, @"fn");
41 y.* = x - @"fn" * pio2_1 - @"fn" * pio2_1t;
42 // Matters with directed rounding.
43 if (y.* < -pio4) {
44 n -= 1;
45 @"fn" -= 1;
46 y.* = x - @"fn" * pio2_1 - @"fn" * pio2_1t;
47 } else if (y.* > pio4) {
48 n += 1;
49 @"fn" += 1;
50 y.* = x - @"fn" * pio2_1 - @"fn" * pio2_1t;
51 }
52 return n;
53 }
54 if (ix >= 0x7f800000) { // x is inf or NaN
55 y.* = x - x;
56 return 0;
57 }
58 // scale x into [2^23, 2^24-1]
59 sign = ui >> 31 != 0;
60 e0 = (ix >> 23) - (0x7f + 23); // e0 = ilogb(|x|)-23, positive
61 ui = ix - (e0 << 23);
62 tx[0] = @bitCast(f32, ui);
63 n = rem_pio2_large(&tx, &ty, @intCast(i32, e0), 1, 0);
64 if (sign) {
65 y.* = -ty[0];
66 return -n;
67 }
68 y.* = ty[0];
69 return n;
70}
lib/std/special/compiler_rt/round.zig created+169
......@@ -0,0 +1,169 @@
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/roundf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10
11pub fn __roundh(x: f16) callconv(.C) f16 {
12 // TODO: more efficient implementation
13 return @floatCast(f16, roundf(x));
14}
15
16pub fn roundf(x_: f32) callconv(.C) f32 {
17 const f32_toint = 1.0 / math.floatEps(f32);
18
19 var x = x_;
20 const u = @bitCast(u32, x);
21 const e = (u >> 23) & 0xFF;
22 var y: f32 = undefined;
23
24 if (e >= 0x7F + 23) {
25 return x;
26 }
27 if (u >> 31 != 0) {
28 x = -x;
29 }
30 if (e < 0x7F - 1) {
31 math.doNotOptimizeAway(x + f32_toint);
32 return 0 * @bitCast(f32, u);
33 }
34
35 y = x + f32_toint - f32_toint - x;
36 if (y > 0.5) {
37 y = y + x - 1;
38 } else if (y <= -0.5) {
39 y = y + x + 1;
40 } else {
41 y = y + x;
42 }
43
44 if (u >> 31 != 0) {
45 return -y;
46 } else {
47 return y;
48 }
49}
50
51pub fn round(x_: f64) callconv(.C) f64 {
52 const f64_toint = 1.0 / math.floatEps(f64);
53
54 var x = x_;
55 const u = @bitCast(u64, x);
56 const e = (u >> 52) & 0x7FF;
57 var y: f64 = undefined;
58
59 if (e >= 0x3FF + 52) {
60 return x;
61 }
62 if (u >> 63 != 0) {
63 x = -x;
64 }
65 if (e < 0x3ff - 1) {
66 math.doNotOptimizeAway(x + f64_toint);
67 return 0 * @bitCast(f64, u);
68 }
69
70 y = x + f64_toint - f64_toint - x;
71 if (y > 0.5) {
72 y = y + x - 1;
73 } else if (y <= -0.5) {
74 y = y + x + 1;
75 } else {
76 y = y + x;
77 }
78
79 if (u >> 63 != 0) {
80 return -y;
81 } else {
82 return y;
83 }
84}
85
86pub fn __roundx(x: f80) callconv(.C) f80 {
87 // TODO: more efficient implementation
88 return @floatCast(f80, roundq(x));
89}
90
91pub fn roundq(x_: f128) callconv(.C) f128 {
92 const f128_toint = 1.0 / math.floatEps(f128);
93
94 var x = x_;
95 const u = @bitCast(u128, x);
96 const e = (u >> 112) & 0x7FFF;
97 var y: f128 = undefined;
98
99 if (e >= 0x3FFF + 112) {
100 return x;
101 }
102 if (u >> 127 != 0) {
103 x = -x;
104 }
105 if (e < 0x3FFF - 1) {
106 math.doNotOptimizeAway(x + f128_toint);
107 return 0 * @bitCast(f128, u);
108 }
109
110 y = x + f128_toint - f128_toint - x;
111 if (y > 0.5) {
112 y = y + x - 1;
113 } else if (y <= -0.5) {
114 y = y + x + 1;
115 } else {
116 y = y + x;
117 }
118
119 if (u >> 127 != 0) {
120 return -y;
121 } else {
122 return y;
123 }
124}
125
126test "round32" {
127 try expect(roundf(1.3) == 1.0);
128 try expect(roundf(-1.3) == -1.0);
129 try expect(roundf(0.2) == 0.0);
130 try expect(roundf(1.8) == 2.0);
131}
132
133test "round64" {
134 try expect(round(1.3) == 1.0);
135 try expect(round(-1.3) == -1.0);
136 try expect(round(0.2) == 0.0);
137 try expect(round(1.8) == 2.0);
138}
139
140test "round128" {
141 try expect(roundq(1.3) == 1.0);
142 try expect(roundq(-1.3) == -1.0);
143 try expect(roundq(0.2) == 0.0);
144 try expect(roundq(1.8) == 2.0);
145}
146
147test "round32.special" {
148 try expect(roundf(0.0) == 0.0);
149 try expect(roundf(-0.0) == -0.0);
150 try expect(math.isPositiveInf(roundf(math.inf(f32))));
151 try expect(math.isNegativeInf(roundf(-math.inf(f32))));
152 try expect(math.isNan(roundf(math.nan(f32))));
153}
154
155test "round64.special" {
156 try expect(round(0.0) == 0.0);
157 try expect(round(-0.0) == -0.0);
158 try expect(math.isPositiveInf(round(math.inf(f64))));
159 try expect(math.isNegativeInf(round(-math.inf(f64))));
160 try expect(math.isNan(round(math.nan(f64))));
161}
162
163test "round128.special" {
164 try expect(roundq(0.0) == 0.0);
165 try expect(roundq(-0.0) == -0.0);
166 try expect(math.isPositiveInf(roundq(math.inf(f128))));
167 try expect(math.isNegativeInf(roundq(-math.inf(f128))));
168 try expect(math.isNan(roundq(math.nan(f128))));
169}
lib/std/special/compiler_rt/sin.zig created+162
......@@ -0,0 +1,162 @@
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/sinf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/sin.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10
11const trig = @import("trig.zig");
12const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
13const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
14
15pub fn __sinh(x: f16) callconv(.C) f16 {
16 // TODO: more efficient implementation
17 return @floatCast(f16, sinf(x));
18}
19
20pub fn sinf(x: f32) callconv(.C) f32 {
21 // Small multiples of pi/2 rounded to double precision.
22 const s1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
23 const s2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
24 const s3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
25 const s4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
26
27 var ix = @bitCast(u32, x);
28 const sign = ix >> 31 != 0;
29 ix &= 0x7fffffff;
30
31 if (ix <= 0x3f490fda) { // |x| ~<= pi/4
32 if (ix < 0x39800000) { // |x| < 2**-12
33 // raise inexact if x!=0 and underflow if subnormal
34 math.doNotOptimizeAway(if (ix < 0x00800000) x / 0x1p120 else x + 0x1p120);
35 return x;
36 }
37 return trig.__sindf(x);
38 }
39 if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4
40 if (ix <= 0x4016cbe3) { // |x| ~<= 3pi/4
41 if (sign) {
42 return -trig.__cosdf(x + s1pio2);
43 } else {
44 return trig.__cosdf(x - s1pio2);
45 }
46 }
47 return trig.__sindf(if (sign) -(x + s2pio2) else -(x - s2pio2));
48 }
49 if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4
50 if (ix <= 0x40afeddf) { // |x| ~<= 7*pi/4
51 if (sign) {
52 return trig.__cosdf(x + s3pio2);
53 } else {
54 return -trig.__cosdf(x - s3pio2);
55 }
56 }
57 return trig.__sindf(if (sign) x + s4pio2 else x - s4pio2);
58 }
59
60 // sin(Inf or NaN) is NaN
61 if (ix >= 0x7f800000) {
62 return x - x;
63 }
64
65 var y: f64 = undefined;
66 const n = rem_pio2f(x, &y);
67 return switch (n & 3) {
68 0 => trig.__sindf(y),
69 1 => trig.__cosdf(y),
70 2 => trig.__sindf(-y),
71 else => -trig.__cosdf(y),
72 };
73}
74
75pub fn sin(x: f64) callconv(.C) f64 {
76 var ix = @bitCast(u64, x) >> 32;
77 ix &= 0x7fffffff;
78
79 // |x| ~< pi/4
80 if (ix <= 0x3fe921fb) {
81 if (ix < 0x3e500000) { // |x| < 2**-26
82 // raise inexact if x != 0 and underflow if subnormal
83 math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
84 return x;
85 }
86 return trig.__sin(x, 0.0, 0);
87 }
88
89 // sin(Inf or NaN) is NaN
90 if (ix >= 0x7ff00000) {
91 return x - x;
92 }
93
94 var y: [2]f64 = undefined;
95 const n = rem_pio2(x, &y);
96 return switch (n & 3) {
97 0 => trig.__sin(y[0], y[1], 1),
98 1 => trig.__cos(y[0], y[1]),
99 2 => -trig.__sin(y[0], y[1], 1),
100 else => -trig.__cos(y[0], y[1]),
101 };
102}
103
104pub fn __sinx(x: f80) callconv(.C) f80 {
105 // TODO: more efficient implementation
106 return @floatCast(f80, sinq(x));
107}
108
109pub fn sinq(x: f128) callconv(.C) f128 {
110 // TODO: more correct implementation
111 return sin(@floatCast(f64, x));
112}
113
114test "sin32" {
115 const epsilon = 0.00001;
116
117 try expect(math.approxEqAbs(f32, sinf(0.0), 0.0, epsilon));
118 try expect(math.approxEqAbs(f32, sinf(0.2), 0.198669, epsilon));
119 try expect(math.approxEqAbs(f32, sinf(0.8923), 0.778517, epsilon));
120 try expect(math.approxEqAbs(f32, sinf(1.5), 0.997495, epsilon));
121 try expect(math.approxEqAbs(f32, sinf(-1.5), -0.997495, epsilon));
122 try expect(math.approxEqAbs(f32, sinf(37.45), -0.246544, epsilon));
123 try expect(math.approxEqAbs(f32, sinf(89.123), 0.916166, epsilon));
124}
125
126test "sin64" {
127 const epsilon = 0.000001;
128
129 try expect(math.approxEqAbs(f64, sin(0.0), 0.0, epsilon));
130 try expect(math.approxEqAbs(f64, sin(0.2), 0.198669, epsilon));
131 try expect(math.approxEqAbs(f64, sin(0.8923), 0.778517, epsilon));
132 try expect(math.approxEqAbs(f64, sin(1.5), 0.997495, epsilon));
133 try expect(math.approxEqAbs(f64, sin(-1.5), -0.997495, epsilon));
134 try expect(math.approxEqAbs(f64, sin(37.45), -0.246543, epsilon));
135 try expect(math.approxEqAbs(f64, sin(89.123), 0.916166, epsilon));
136}
137
138test "sin32.special" {
139 try expect(sinf(0.0) == 0.0);
140 try expect(sinf(-0.0) == -0.0);
141 try expect(math.isNan(sinf(math.inf(f32))));
142 try expect(math.isNan(sinf(-math.inf(f32))));
143 try expect(math.isNan(sinf(math.nan(f32))));
144}
145
146test "sin64.special" {
147 try expect(sin(0.0) == 0.0);
148 try expect(sin(-0.0) == -0.0);
149 try expect(math.isNan(sin(math.inf(f64))));
150 try expect(math.isNan(sin(-math.inf(f64))));
151 try expect(math.isNan(sin(math.nan(f64))));
152}
153
154test "sin32 #9901" {
155 const float = @bitCast(f32, @as(u32, 0b11100011111111110000000000000000));
156 _ = sinf(float);
157}
158
159test "sin64 #9901" {
160 const float = @bitCast(f64, @as(u64, 0b1111111101000001000000001111110111111111100000000000000000000001));
161 _ = sin(float);
162}
lib/std/special/compiler_rt/sincos.zig created+242
......@@ -0,0 +1,242 @@
1const std = @import("std");
2const math = std.math;
3const sin = @import("sin.zig");
4const cos = @import("cos.zig");
5const trig = @import("trig.zig");
6const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
7const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
8
9pub fn __sincosh(x: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void {
10 // TODO: more efficient implementation
11 var big_sin: f32 = undefined;
12 var big_cos: f32 = undefined;
13 sincosf(x, &big_sin, &big_cos);
14 r_sin.* = @floatCast(f16, big_sin);
15 r_cos.* = @floatCast(f16, big_cos);
16}
17
18pub fn sincosf(x: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void {
19 const sc1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
20 const sc2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
21 const sc3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
22 const sc4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
23
24 const pre_ix = @bitCast(u32, x);
25 const sign = pre_ix >> 31 != 0;
26 const ix = pre_ix & 0x7fffffff;
27
28 // |x| ~<= pi/4
29 if (ix <= 0x3f490fda) {
30 // |x| < 2**-12
31 if (ix < 0x39800000) {
32 // raise inexact if x!=0 and underflow if subnormal
33 math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
34 r_sin.* = x;
35 r_cos.* = 1.0;
36 return;
37 }
38 r_sin.* = trig.__sindf(x);
39 r_cos.* = trig.__cosdf(x);
40 return;
41 }
42
43 // |x| ~<= 5*pi/4
44 if (ix <= 0x407b53d1) {
45 // |x| ~<= 3pi/4
46 if (ix <= 0x4016cbe3) {
47 if (sign) {
48 r_sin.* = -trig.__cosdf(x + sc1pio2);
49 r_cos.* = trig.__sindf(x + sc1pio2);
50 } else {
51 r_sin.* = trig.__cosdf(sc1pio2 - x);
52 r_cos.* = trig.__sindf(sc1pio2 - x);
53 }
54 return;
55 }
56 // -sin(x+c) is not correct if x+c could be 0: -0 vs +0
57 r_sin.* = -trig.__sindf(if (sign) x + sc2pio2 else x - sc2pio2);
58 r_cos.* = -trig.__cosdf(if (sign) x + sc2pio2 else x - sc2pio2);
59 return;
60 }
61
62 // |x| ~<= 9*pi/4
63 if (ix <= 0x40e231d5) {
64 // |x| ~<= 7*pi/4
65 if (ix <= 0x40afeddf) {
66 if (sign) {
67 r_sin.* = trig.__cosdf(x + sc3pio2);
68 r_cos.* = -trig.__sindf(x + sc3pio2);
69 } else {
70 r_sin.* = -trig.__cosdf(x - sc3pio2);
71 r_cos.* = trig.__sindf(x - sc3pio2);
72 }
73 return;
74 }
75 r_sin.* = trig.__sindf(if (sign) x + sc4pio2 else x - sc4pio2);
76 r_cos.* = trig.__cosdf(if (sign) x + sc4pio2 else x - sc4pio2);
77 return;
78 }
79
80 // sin(Inf or NaN) is NaN
81 if (ix >= 0x7f800000) {
82 const result = x - x;
83 r_sin.* = result;
84 r_cos.* = result;
85 return;
86 }
87
88 // general argument reduction needed
89 var y: f64 = undefined;
90 const n = rem_pio2f(x, &y);
91 const s = trig.__sindf(y);
92 const c = trig.__cosdf(y);
93 switch (n & 3) {
94 0 => {
95 r_sin.* = s;
96 r_cos.* = c;
97 },
98 1 => {
99 r_sin.* = c;
100 r_cos.* = -s;
101 },
102 2 => {
103 r_sin.* = -s;
104 r_cos.* = -c;
105 },
106 else => {
107 r_sin.* = -c;
108 r_cos.* = s;
109 },
110 }
111}
112
113pub fn sincos(x: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void {
114 const ix = @truncate(u32, @bitCast(u64, x) >> 32) & 0x7fffffff;
115
116 // |x| ~< pi/4
117 if (ix <= 0x3fe921fb) {
118 // if |x| < 2**-27 * sqrt(2)
119 if (ix < 0x3e46a09e) {
120 // raise inexact if x != 0 and underflow if subnormal
121 math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
122 r_sin.* = x;
123 r_cos.* = 1.0;
124 return;
125 }
126 r_sin.* = trig.__sin(x, 0.0, 0);
127 r_cos.* = trig.__cos(x, 0.0);
128 return;
129 }
130
131 // sincos(Inf or NaN) is NaN
132 if (ix >= 0x7ff00000) {
133 const result = x - x;
134 r_sin.* = result;
135 r_cos.* = result;
136 return;
137 }
138
139 // argument reduction needed
140 var y: [2]f64 = undefined;
141 const n = rem_pio2(x, &y);
142 const s = trig.__sin(y[0], y[1], 1);
143 const c = trig.__cos(y[0], y[1]);
144 switch (n & 3) {
145 0 => {
146 r_sin.* = s;
147 r_cos.* = c;
148 },
149 1 => {
150 r_sin.* = c;
151 r_cos.* = -s;
152 },
153 2 => {
154 r_sin.* = -s;
155 r_cos.* = -c;
156 },
157 else => {
158 r_sin.* = -c;
159 r_cos.* = s;
160 },
161 }
162}
163
164pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.C) void {
165 // TODO: more efficient implementation
166 //return sincos_generic(f80, x, r_sin, r_cos);
167 var big_sin: f128 = undefined;
168 var big_cos: f128 = undefined;
169 sincosq(x, &big_sin, &big_cos);
170 r_sin.* = @floatCast(f80, big_sin);
171 r_cos.* = @floatCast(f80, big_cos);
172}
173
174pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void {
175 // TODO: more correct implementation
176 //return sincos_generic(f128, x, r_sin, r_cos);
177 var small_sin: f64 = undefined;
178 var small_cos: f64 = undefined;
179 sincos(@floatCast(f64, x), &small_sin, &small_cos);
180 r_sin.* = small_sin;
181 r_cos.* = small_cos;
182}
183
184const rem_pio2_generic = @compileError("TODO");
185
186/// Ported from musl sincosl.c. Needs the following dependencies to be complete:
187/// * rem_pio2_generic ported from __rem_pio2l.c
188/// * trig.sin_generic ported from __sinl.c
189/// * trig.cos_generic ported from __cosl.c
190inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void {
191 const sc1pio4: F = 1.0 * math.pi / 4.0;
192 const bits = @typeInfo(F).Float.bits;
193 const I = std.meta.Int(.unsigned, bits);
194 const ix = @bitCast(I, x) & (math.maxInt(I) >> 1);
195 const se = @truncate(u16, ix >> (bits - 16));
196
197 if (se == 0x7fff) {
198 const result = x - x;
199 r_sin.* = result;
200 r_cos.* = result;
201 return;
202 }
203
204 if (@bitCast(F, ix) < sc1pio4) {
205 if (se < 0x3fff - math.floatFractionalBits(F) - 1) {
206 // raise underflow if subnormal
207 if (se == 0) {
208 math.doNotOptimizeAway(x * 0x1p-120);
209 }
210 r_sin.* = x;
211 // raise inexact if x!=0
212 r_cos.* = 1.0 + x;
213 return;
214 }
215 r_sin.* = trig.sin_generic(F, x, 0, 0);
216 r_cos.* = trig.cos_generic(F, x, 0);
217 return;
218 }
219
220 var y: [2]F = undefined;
221 const n = rem_pio2_generic(F, x, &y);
222 const s = trig.sin_generic(F, y[0], y[1], 1);
223 const c = trig.cos_generic(F, y[0], y[1]);
224 switch (n & 3) {
225 0 => {
226 r_sin.* = s;
227 r_cos.* = c;
228 },
229 1 => {
230 r_sin.* = c;
231 r_cos.* = -s;
232 },
233 2 => {
234 r_sin.* = -s;
235 r_cos.* = -c;
236 },
237 else => {
238 r_sin.* = -c;
239 r_cos.* = s;
240 },
241 }
242}
lib/std/special/compiler_rt/sqrt.zig created+284
......@@ -0,0 +1,284 @@
1const std = @import("std");
2const math = std.math;
3
4pub fn __sqrth(x: f16) callconv(.C) f16 {
5 // TODO: more efficient implementation
6 return @floatCast(f16, sqrtf(x));
7}
8
9pub fn sqrtf(x: f32) callconv(.C) f32 {
10 const tiny: f32 = 1.0e-30;
11 const sign: i32 = @bitCast(i32, @as(u32, 0x80000000));
12 var ix: i32 = @bitCast(i32, x);
13
14 if ((ix & 0x7F800000) == 0x7F800000) {
15 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
16 }
17
18 // zero
19 if (ix <= 0) {
20 if (ix & ~sign == 0) {
21 return x; // sqrt (+-0) = +-0
22 }
23 if (ix < 0) {
24 return math.snan(f32);
25 }
26 }
27
28 // normalize
29 var m = ix >> 23;
30 if (m == 0) {
31 // subnormal
32 var i: i32 = 0;
33 while (ix & 0x00800000 == 0) : (i += 1) {
34 ix <<= 1;
35 }
36 m -= i - 1;
37 }
38
39 m -= 127; // unbias exponent
40 ix = (ix & 0x007FFFFF) | 0x00800000;
41
42 if (m & 1 != 0) { // odd m, double x to even
43 ix += ix;
44 }
45
46 m >>= 1; // m = [m / 2]
47
48 // sqrt(x) bit by bit
49 ix += ix;
50 var q: i32 = 0; // q = sqrt(x)
51 var s: i32 = 0;
52 var r: i32 = 0x01000000; // r = moving bit right -> left
53
54 while (r != 0) {
55 const t = s + r;
56 if (t <= ix) {
57 s = t + r;
58 ix -= t;
59 q += r;
60 }
61 ix += ix;
62 r >>= 1;
63 }
64
65 // floating add to find rounding direction
66 if (ix != 0) {
67 var z = 1.0 - tiny; // inexact
68 if (z >= 1.0) {
69 z = 1.0 + tiny;
70 if (z > 1.0) {
71 q += 2;
72 } else {
73 if (q & 1 != 0) {
74 q += 1;
75 }
76 }
77 }
78 }
79
80 ix = (q >> 1) + 0x3f000000;
81 ix += m << 23;
82 return @bitCast(f32, ix);
83}
84
85/// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
86/// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
87/// potentially some edge cases remaining that are not handled in the same way.
88pub fn sqrt(x: f64) callconv(.C) f64 {
89 const tiny: f64 = 1.0e-300;
90 const sign: u32 = 0x80000000;
91 const u = @bitCast(u64, x);
92
93 var ix0 = @intCast(u32, u >> 32);
94 var ix1 = @intCast(u32, u & 0xFFFFFFFF);
95
96 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
97 if (ix0 & 0x7FF00000 == 0x7FF00000) {
98 return x * x + x;
99 }
100
101 // sqrt(+-0) = +-0
102 if (x == 0.0) {
103 return x;
104 }
105 // sqrt(-ve) = snan
106 if (ix0 & sign != 0) {
107 return math.snan(f64);
108 }
109
110 // normalize x
111 var m = @intCast(i32, ix0 >> 20);
112 if (m == 0) {
113 // subnormal
114 while (ix0 == 0) {
115 m -= 21;
116 ix0 |= ix1 >> 11;
117 ix1 <<= 21;
118 }
119
120 // subnormal
121 var i: u32 = 0;
122 while (ix0 & 0x00100000 == 0) : (i += 1) {
123 ix0 <<= 1;
124 }
125 m -= @intCast(i32, i) - 1;
126 ix0 |= ix1 >> @intCast(u5, 32 - i);
127 ix1 <<= @intCast(u5, i);
128 }
129
130 // unbias exponent
131 m -= 1023;
132 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
133 if (m & 1 != 0) {
134 ix0 += ix0 + (ix1 >> 31);
135 ix1 = ix1 +% ix1;
136 }
137 m >>= 1;
138
139 // sqrt(x) bit by bit
140 ix0 += ix0 + (ix1 >> 31);
141 ix1 = ix1 +% ix1;
142
143 var q: u32 = 0;
144 var q1: u32 = 0;
145 var s0: u32 = 0;
146 var s1: u32 = 0;
147 var r: u32 = 0x00200000;
148 var t: u32 = undefined;
149 var t1: u32 = undefined;
150
151 while (r != 0) {
152 t = s0 +% r;
153 if (t <= ix0) {
154 s0 = t + r;
155 ix0 -= t;
156 q += r;
157 }
158 ix0 = ix0 +% ix0 +% (ix1 >> 31);
159 ix1 = ix1 +% ix1;
160 r >>= 1;
161 }
162
163 r = sign;
164 while (r != 0) {
165 t1 = s1 +% r;
166 t = s0;
167 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
168 s1 = t1 +% r;
169 if (t1 & sign == sign and s1 & sign == 0) {
170 s0 += 1;
171 }
172 ix0 -= t;
173 if (ix1 < t1) {
174 ix0 -= 1;
175 }
176 ix1 = ix1 -% t1;
177 q1 += r;
178 }
179 ix0 = ix0 +% ix0 +% (ix1 >> 31);
180 ix1 = ix1 +% ix1;
181 r >>= 1;
182 }
183
184 // rounding direction
185 if (ix0 | ix1 != 0) {
186 var z = 1.0 - tiny; // raise inexact
187 if (z >= 1.0) {
188 z = 1.0 + tiny;
189 if (q1 == 0xFFFFFFFF) {
190 q1 = 0;
191 q += 1;
192 } else if (z > 1.0) {
193 if (q1 == 0xFFFFFFFE) {
194 q += 1;
195 }
196 q1 += 2;
197 } else {
198 q1 += q1 & 1;
199 }
200 }
201 }
202
203 ix0 = (q >> 1) + 0x3FE00000;
204 ix1 = q1 >> 1;
205 if (q & 1 != 0) {
206 ix1 |= 0x80000000;
207 }
208
209 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
210 // behaviour at least.
211 var iix0 = @intCast(i32, ix0);
212 iix0 = iix0 +% (m << 20);
213
214 const uz = (@intCast(u64, iix0) << 32) | ix1;
215 return @bitCast(f64, uz);
216}
217
218pub fn __sqrtx(x: f80) callconv(.C) f80 {
219 // TODO: more efficient implementation
220 return @floatCast(f80, sqrtq(x));
221}
222
223pub fn sqrtq(x: f128) callconv(.C) f128 {
224 // TODO: more correct implementation
225 return sqrt(@floatCast(f64, x));
226}
227
228test "sqrtf" {
229 const V = [_]f32{
230 0.0,
231 4.089288054930154,
232 7.538757127071935,
233 8.97780793672623,
234 5.304443821913729,
235 5.682408965311888,
236 0.5846878579110049,
237 3.650338664297043,
238 0.3178091951800732,
239 7.1505232436382835,
240 3.6589165881946464,
241 };
242
243 // Note that @sqrt will either generate the sqrt opcode (if supported by the
244 // target ISA) or a call to `sqrtf` otherwise.
245 for (V) |val|
246 try std.testing.expectEqual(@sqrt(val), sqrtf(val));
247}
248
249test "sqrtf special" {
250 try std.testing.expect(math.isPositiveInf(sqrtf(math.inf(f32))));
251 try std.testing.expect(sqrtf(0.0) == 0.0);
252 try std.testing.expect(sqrtf(-0.0) == -0.0);
253 try std.testing.expect(math.isNan(sqrtf(-1.0)));
254 try std.testing.expect(math.isNan(sqrtf(math.nan(f32))));
255}
256
257test "sqrt" {
258 const V = [_]f64{
259 0.0,
260 4.089288054930154,
261 7.538757127071935,
262 8.97780793672623,
263 5.304443821913729,
264 5.682408965311888,
265 0.5846878579110049,
266 3.650338664297043,
267 0.3178091951800732,
268 7.1505232436382835,
269 3.6589165881946464,
270 };
271
272 // Note that @sqrt will either generate the sqrt opcode (if supported by the
273 // target ISA) or a call to `sqrtf` otherwise.
274 for (V) |val|
275 try std.testing.expectEqual(@sqrt(val), sqrt(val));
276}
277
278test "sqrt special" {
279 try std.testing.expect(math.isPositiveInf(sqrt(math.inf(f64))));
280 try std.testing.expect(sqrt(0.0) == 0.0);
281 try std.testing.expect(sqrt(-0.0) == -0.0);
282 try std.testing.expect(math.isNan(sqrt(-1.0)));
283 try std.testing.expect(math.isNan(sqrt(math.nan(f64))));
284}
lib/std/special/compiler_rt/tan.zig created+140
......@@ -0,0 +1,140 @@
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/tanf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/tan.c
6// https://golang.org/src/math/tan.go
7
8const std = @import("std");
9const math = std.math;
10const expect = std.testing.expect;
11
12const kernel = @import("trig.zig");
13const rem_pio2 = @import("rem_pio2.zig").rem_pio2;
14const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f;
15
16pub fn __tanh(x: f16) callconv(.C) f16 {
17 // TODO: more efficient implementation
18 return @floatCast(f16, tanf(x));
19}
20
21pub fn tanf(x: f32) callconv(.C) f32 {
22 // Small multiples of pi/2 rounded to double precision.
23 const t1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18
24 const t2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18
25 const t3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2
26 const t4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18
27
28 var ix = @bitCast(u32, x);
29 const sign = ix >> 31 != 0;
30 ix &= 0x7fffffff;
31
32 if (ix <= 0x3f490fda) { // |x| ~<= pi/4
33 if (ix < 0x39800000) { // |x| < 2**-12
34 // raise inexact if x!=0 and underflow if subnormal
35 math.doNotOptimizeAway(if (ix < 0x00800000) x / 0x1p120 else x + 0x1p120);
36 return x;
37 }
38 return kernel.__tandf(x, false);
39 }
40 if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4
41 if (ix <= 0x4016cbe3) { // |x| ~<= 3pi/4
42 return kernel.__tandf((if (sign) x + t1pio2 else x - t1pio2), true);
43 } else {
44 return kernel.__tandf((if (sign) x + t2pio2 else x - t2pio2), false);
45 }
46 }
47 if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4
48 if (ix <= 0x40afeddf) { // |x| ~<= 7*pi/4
49 return kernel.__tandf((if (sign) x + t3pio2 else x - t3pio2), true);
50 } else {
51 return kernel.__tandf((if (sign) x + t4pio2 else x - t4pio2), false);
52 }
53 }
54
55 // tan(Inf or NaN) is NaN
56 if (ix >= 0x7f800000) {
57 return x - x;
58 }
59
60 var y: f64 = undefined;
61 const n = rem_pio2f(x, &y);
62 return kernel.__tandf(y, n & 1 != 0);
63}
64
65pub fn tan(x: f64) callconv(.C) f64 {
66 var ix = @bitCast(u64, x) >> 32;
67 ix &= 0x7fffffff;
68
69 // |x| ~< pi/4
70 if (ix <= 0x3fe921fb) {
71 if (ix < 0x3e400000) { // |x| < 2**-27
72 // raise inexact if x!=0 and underflow if subnormal
73 math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);
74 return x;
75 }
76 return kernel.__tan(x, 0.0, false);
77 }
78
79 // tan(Inf or NaN) is NaN
80 if (ix >= 0x7ff00000) {
81 return x - x;
82 }
83
84 var y: [2]f64 = undefined;
85 const n = rem_pio2(x, &y);
86 return kernel.__tan(y[0], y[1], n & 1 != 0);
87}
88
89pub fn __tanx(x: f80) callconv(.C) f80 {
90 // TODO: more efficient implementation
91 return @floatCast(f80, tanq(x));
92}
93
94pub fn tanq(x: f128) callconv(.C) f128 {
95 // TODO: more correct implementation
96 return tan(@floatCast(f64, x));
97}
98
99test "tan" {
100 try expect(tan(@as(f32, 0.0)) == tanf(0.0));
101 try expect(tan(@as(f64, 0.0)) == tan(0.0));
102}
103
104test "tan32" {
105 const epsilon = 0.00001;
106
107 try expect(math.approxEqAbs(f32, tanf(0.0), 0.0, epsilon));
108 try expect(math.approxEqAbs(f32, tanf(0.2), 0.202710, epsilon));
109 try expect(math.approxEqAbs(f32, tanf(0.8923), 1.240422, epsilon));
110 try expect(math.approxEqAbs(f32, tanf(1.5), 14.101420, epsilon));
111 try expect(math.approxEqAbs(f32, tanf(37.45), -0.254397, epsilon));
112 try expect(math.approxEqAbs(f32, tanf(89.123), 2.285852, epsilon));
113}
114
115test "tan64" {
116 const epsilon = 0.000001;
117
118 try expect(math.approxEqAbs(f64, tan(0.0), 0.0, epsilon));
119 try expect(math.approxEqAbs(f64, tan(0.2), 0.202710, epsilon));
120 try expect(math.approxEqAbs(f64, tan(0.8923), 1.240422, epsilon));
121 try expect(math.approxEqAbs(f64, tan(1.5), 14.101420, epsilon));
122 try expect(math.approxEqAbs(f64, tan(37.45), -0.254397, epsilon));
123 try expect(math.approxEqAbs(f64, tan(89.123), 2.2858376, epsilon));
124}
125
126test "tan32.special" {
127 try expect(tanf(0.0) == 0.0);
128 try expect(tanf(-0.0) == -0.0);
129 try expect(math.isNan(tanf(math.inf(f32))));
130 try expect(math.isNan(tanf(-math.inf(f32))));
131 try expect(math.isNan(tanf(math.nan(f32))));
132}
133
134test "tan64.special" {
135 try expect(tan(0.0) == 0.0);
136 try expect(tan(-0.0) == -0.0);
137 try expect(math.isNan(tan(math.inf(f64))));
138 try expect(math.isNan(tan(-math.inf(f64))));
139 try expect(math.isNan(tan(math.nan(f64))));
140}
lib/std/special/compiler_rt/trig.zig created+273
......@@ -0,0 +1,273 @@
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/__cos.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/__cosdf.c
6// https://git.musl-libc.org/cgit/musl/tree/src/math/__sin.c
7// https://git.musl-libc.org/cgit/musl/tree/src/math/__sindf.c
8// https://git.musl-libc.org/cgit/musl/tree/src/math/__tand.c
9// https://git.musl-libc.org/cgit/musl/tree/src/math/__tandf.c
10
11/// kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164
12/// Input x is assumed to be bounded by ~pi/4 in magnitude.
13/// Input y is the tail of x.
14///
15/// Algorithm
16/// 1. Since cos(-x) = cos(x), we need only to consider positive x.
17/// 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0.
18/// 3. cos(x) is approximated by a polynomial of degree 14 on
19/// [0,pi/4]
20/// 4 14
21/// cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x
22/// where the remez error is
23///
24/// | 2 4 6 8 10 12 14 | -58
25/// |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2
26/// | |
27///
28/// 4 6 8 10 12 14
29/// 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then
30/// cos(x) ~ 1 - x*x/2 + r
31/// since cos(x+y) ~ cos(x) - sin(x)*y
32/// ~ cos(x) - x*y,
33/// a correction term is necessary in cos(x) and hence
34/// cos(x+y) = 1 - (x*x/2 - (r - x*y))
35/// For better accuracy, rearrange to
36/// cos(x+y) ~ w + (tmp + (r-x*y))
37/// where w = 1 - x*x/2 and tmp is a tiny correction term
38/// (1 - x*x/2 == w + tmp exactly in infinite precision).
39/// The exactness of w + tmp in infinite precision depends on w
40/// and tmp having the same precision as x. If they have extra
41/// precision due to compiler bugs, then the extra precision is
42/// only good provided it is retained in all terms of the final
43/// expression for cos(). Retention happens in all cases tested
44/// under FreeBSD, so don't pessimize things by forcibly clipping
45/// any extra precision in w.
46pub fn __cos(x: f64, y: f64) f64 {
47 const C1 = 4.16666666666666019037e-02; // 0x3FA55555, 0x5555554C
48 const C2 = -1.38888888888741095749e-03; // 0xBF56C16C, 0x16C15177
49 const C3 = 2.48015872894767294178e-05; // 0x3EFA01A0, 0x19CB1590
50 const C4 = -2.75573143513906633035e-07; // 0xBE927E4F, 0x809C52AD
51 const C5 = 2.08757232129817482790e-09; // 0x3E21EE9E, 0xBDB4B1C4
52 const C6 = -1.13596475577881948265e-11; // 0xBDA8FAE9, 0xBE8838D4
53
54 const z = x * x;
55 const zs = z * z;
56 const r = z * (C1 + z * (C2 + z * C3)) + zs * zs * (C4 + z * (C5 + z * C6));
57 const hz = 0.5 * z;
58 const w = 1.0 - hz;
59 return w + (((1.0 - w) - hz) + (z * r - x * y));
60}
61
62pub fn __cosdf(x: f64) f32 {
63 // |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]).
64 const C0 = -0x1ffffffd0c5e81.0p-54; // -0.499999997251031003120
65 const C1 = 0x155553e1053a42.0p-57; // 0.0416666233237390631894
66 const C2 = -0x16c087e80f1e27.0p-62; // -0.00138867637746099294692
67 const C3 = 0x199342e0ee5069.0p-68; // 0.0000243904487962774090654
68
69 // Try to optimize for parallel evaluation as in __tandf.c.
70 const z = x * x;
71 const w = z * z;
72 const r = C2 + z * C3;
73 return @floatCast(f32, ((1.0 + z * C0) + w * C1) + (w * z) * r);
74}
75
76/// kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854
77/// Input x is assumed to be bounded by ~pi/4 in magnitude.
78/// Input y is the tail of x.
79/// Input iy indicates whether y is 0. (if iy=0, y assume to be 0).
80///
81/// Algorithm
82/// 1. Since sin(-x) = -sin(x), we need only to consider positive x.
83/// 2. Callers must return sin(-0) = -0 without calling here since our
84/// odd polynomial is not evaluated in a way that preserves -0.
85/// Callers may do the optimization sin(x) ~ x for tiny x.
86/// 3. sin(x) is approximated by a polynomial of degree 13 on
87/// [0,pi/4]
88/// 3 13
89/// sin(x) ~ x + S1*x + ... + S6*x
90/// where
91///
92/// |sin(x) 2 4 6 8 10 12 | -58
93/// |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2
94/// | x |
95///
96/// 4. sin(x+y) = sin(x) + sin'(x')*y
97/// ~ sin(x) + (1-x*x/2)*y
98/// For better accuracy, let
99/// 3 2 2 2 2
100/// r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6))))
101/// then 3 2
102/// sin(x) = x + (S1*x + (x *(r-y/2)+y))
103pub fn __sin(x: f64, y: f64, iy: i32) f64 {
104 const S1 = -1.66666666666666324348e-01; // 0xBFC55555, 0x55555549
105 const S2 = 8.33333333332248946124e-03; // 0x3F811111, 0x1110F8A6
106 const S3 = -1.98412698298579493134e-04; // 0xBF2A01A0, 0x19C161D5
107 const S4 = 2.75573137070700676789e-06; // 0x3EC71DE3, 0x57B1FE7D
108 const S5 = -2.50507602534068634195e-08; // 0xBE5AE5E6, 0x8A2B9CEB
109 const S6 = 1.58969099521155010221e-10; // 0x3DE5D93A, 0x5ACFD57C
110
111 const z = x * x;
112 const w = z * z;
113 const r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6);
114 const v = z * x;
115 if (iy == 0) {
116 return x + v * (S1 + z * r);
117 } else {
118 return x - ((z * (0.5 * y - v * r) - y) - v * S1);
119 }
120}
121
122pub fn __sindf(x: f64) f32 {
123 // |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]).
124 const S1 = -0x15555554cbac77.0p-55; // -0.166666666416265235595
125 const S2 = 0x111110896efbb2.0p-59; // 0.0083333293858894631756
126 const S3 = -0x1a00f9e2cae774.0p-65; // -0.000198393348360966317347
127 const S4 = 0x16cd878c3b46a7.0p-71; // 0.0000027183114939898219064
128
129 // Try to optimize for parallel evaluation as in __tandf.c.
130 const z = x * x;
131 const w = z * z;
132 const r = S3 + z * S4;
133 const s = z * x;
134 return @floatCast(f32, (x + s * (S1 + z * S2)) + s * w * r);
135}
136
137/// kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854
138/// Input x is assumed to be bounded by ~pi/4 in magnitude.
139/// Input y is the tail of x.
140/// Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned.
141///
142/// Algorithm
143/// 1. Since tan(-x) = -tan(x), we need only to consider positive x.
144/// 2. Callers must return tan(-0) = -0 without calling here since our
145/// odd polynomial is not evaluated in a way that preserves -0.
146/// Callers may do the optimization tan(x) ~ x for tiny x.
147/// 3. tan(x) is approximated by a odd polynomial of degree 27 on
148/// [0,0.67434]
149/// 3 27
150/// tan(x) ~ x + T1*x + ... + T13*x
151/// where
152///
153/// |tan(x) 2 4 26 | -59.2
154/// |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2
155/// | x |
156///
157/// Note: tan(x+y) = tan(x) + tan'(x)*y
158/// ~ tan(x) + (1+x*x)*y
159/// Therefore, for better accuracy in computing tan(x+y), let
160/// 3 2 2 2 2
161/// r = x *(T2+x *(T3+x *(...+x *(T12+x *T13))))
162/// then
163/// 3 2
164/// tan(x+y) = x + (T1*x + (x *(r+y)+y))
165///
166/// 4. For x in [0.67434,pi/4], let y = pi/4 - x, then
167/// tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y))
168/// = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y)))
169pub fn __tan(x_: f64, y_: f64, odd: bool) f64 {
170 var x = x_;
171 var y = y_;
172
173 const T = [_]f64{
174 3.33333333333334091986e-01, // 3FD55555, 55555563
175 1.33333333333201242699e-01, // 3FC11111, 1110FE7A
176 5.39682539762260521377e-02, // 3FABA1BA, 1BB341FE
177 2.18694882948595424599e-02, // 3F9664F4, 8406D637
178 8.86323982359930005737e-03, // 3F8226E3, E96E8493
179 3.59207910759131235356e-03, // 3F6D6D22, C9560328
180 1.45620945432529025516e-03, // 3F57DBC8, FEE08315
181 5.88041240820264096874e-04, // 3F4344D8, F2F26501
182 2.46463134818469906812e-04, // 3F3026F7, 1A8D1068
183 7.81794442939557092300e-05, // 3F147E88, A03792A6
184 7.14072491382608190305e-05, // 3F12B80F, 32F0A7E9
185 -1.85586374855275456654e-05, // BEF375CB, DB605373
186 2.59073051863633712884e-05, // 3EFB2A70, 74BF7AD4
187 };
188 const pio4 = 7.85398163397448278999e-01; // 3FE921FB, 54442D18
189 const pio4lo = 3.06161699786838301793e-17; // 3C81A626, 33145C07
190
191 var z: f64 = undefined;
192 var r: f64 = undefined;
193 var v: f64 = undefined;
194 var w: f64 = undefined;
195 var s: f64 = undefined;
196 var a: f64 = undefined;
197 var w0: f64 = undefined;
198 var a0: f64 = undefined;
199 var hx: u32 = undefined;
200 var sign: bool = undefined;
201
202 hx = @intCast(u32, @bitCast(u64, x) >> 32);
203 const big = (hx & 0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744
204 if (big) {
205 sign = hx >> 31 != 0;
206 if (sign) {
207 x = -x;
208 y = -y;
209 }
210 x = (pio4 - x) + (pio4lo - y);
211 y = 0.0;
212 }
213 z = x * x;
214 w = z * z;
215
216 // Break x^5*(T[1]+x^2*T[2]+...) into
217 // x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
218 // x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
219 r = T[1] + w * (T[3] + w * (T[5] + w * (T[7] + w * (T[9] + w * T[11]))));
220 v = z * (T[2] + w * (T[4] + w * (T[6] + w * (T[8] + w * (T[10] + w * T[12])))));
221 s = z * x;
222 r = y + z * (s * (r + v) + y) + s * T[0];
223 w = x + r;
224 if (big) {
225 s = 1 - 2 * @intToFloat(f64, @boolToInt(odd));
226 v = s - 2.0 * (x + (r - w * w / (w + s)));
227 return if (sign) -v else v;
228 }
229 if (!odd) {
230 return w;
231 }
232 // -1.0/(x+r) has up to 2ulp error, so compute it accurately
233 w0 = w;
234 w0 = @bitCast(f64, @bitCast(u64, w0) & 0xffffffff00000000);
235 v = r - (w0 - x); // w0+v = r+x
236 a = -1.0 / w;
237 a0 = a;
238 a0 = @bitCast(f64, @bitCast(u64, a0) & 0xffffffff00000000);
239 return a0 + a * (1.0 + a0 * w0 + a0 * v);
240}
241
242pub fn __tandf(x: f64, odd: bool) f32 {
243 // |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]).
244 const T = [_]f64{
245 0x15554d3418c99f.0p-54, // 0.333331395030791399758
246 0x1112fd38999f72.0p-55, // 0.133392002712976742718
247 0x1b54c91d865afe.0p-57, // 0.0533812378445670393523
248 0x191df3908c33ce.0p-58, // 0.0245283181166547278873
249 0x185dadfcecf44e.0p-61, // 0.00297435743359967304927
250 0x1362b9bf971bcd.0p-59, // 0.00946564784943673166728
251 };
252
253 const z = x * x;
254 // Split up the polynomial into small independent terms to give
255 // opportunities for parallel evaluation. The chosen splitting is
256 // micro-optimized for Athlons (XP, X64). It costs 2 multiplications
257 // relative to Horner's method on sequential machines.
258 //
259 // We add the small terms from lowest degree up for efficiency on
260 // non-sequential machines (the lowest degree terms tend to be ready
261 // earlier). Apart from this, we don't care about order of
262 // operations, and don't need to to care since we have precision to
263 // spare. However, the chosen splitting is good for accuracy too,
264 // and would give results as accurate as Horner's method if the
265 // small terms were added from highest degree down.
266 const r = T[4] + z * T[5];
267 const t = T[2] + z * T[3];
268 const w = z * z;
269 const s = z * x;
270 const u = T[0] + z * T[1];
271 const r0 = (x + s * u) + (s * w) * (t + w * r);
272 return @floatCast(f32, if (odd) -1.0 / r0 else r0);
273}
lib/std/special/compiler_rt/trunc.zig created+124
......@@ -0,0 +1,124 @@
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/truncf.c
5// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c
6
7const std = @import("std");
8const math = std.math;
9const expect = std.testing.expect;
10
11pub fn __trunch(x: f16) callconv(.C) f16 {
12 // TODO: more efficient implementation
13 return @floatCast(f16, truncf(x));
14}
15
16pub fn truncf(x: f32) callconv(.C) f32 {
17 const u = @bitCast(u32, x);
18 var e = @intCast(i32, ((u >> 23) & 0xFF)) - 0x7F + 9;
19 var m: u32 = undefined;
20
21 if (e >= 23 + 9) {
22 return x;
23 }
24 if (e < 9) {
25 e = 1;
26 }
27
28 m = @as(u32, math.maxInt(u32)) >> @intCast(u5, e);
29 if (u & m == 0) {
30 return x;
31 } else {
32 math.doNotOptimizeAway(x + 0x1p120);
33 return @bitCast(f32, u & ~m);
34 }
35}
36
37pub fn trunc(x: f64) callconv(.C) f64 {
38 const u = @bitCast(u64, x);
39 var e = @intCast(i32, ((u >> 52) & 0x7FF)) - 0x3FF + 12;
40 var m: u64 = undefined;
41
42 if (e >= 52 + 12) {
43 return x;
44 }
45 if (e < 12) {
46 e = 1;
47 }
48
49 m = @as(u64, math.maxInt(u64)) >> @intCast(u6, e);
50 if (u & m == 0) {
51 return x;
52 } else {
53 math.doNotOptimizeAway(x + 0x1p120);
54 return @bitCast(f64, u & ~m);
55 }
56}
57
58pub fn __truncx(x: f80) callconv(.C) f80 {
59 // TODO: more efficient implementation
60 return @floatCast(f80, truncq(x));
61}
62
63pub fn truncq(x: f128) callconv(.C) f128 {
64 const u = @bitCast(u128, x);
65 var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16;
66 var m: u128 = undefined;
67
68 if (e >= 112 + 16) {
69 return x;
70 }
71 if (e < 16) {
72 e = 1;
73 }
74
75 m = @as(u128, math.maxInt(u128)) >> @intCast(u7, e);
76 if (u & m == 0) {
77 return x;
78 } else {
79 math.doNotOptimizeAway(x + 0x1p120);
80 return @bitCast(f128, u & ~m);
81 }
82}
83
84test "trunc32" {
85 try expect(truncf(1.3) == 1.0);
86 try expect(truncf(-1.3) == -1.0);
87 try expect(truncf(0.2) == 0.0);
88}
89
90test "trunc64" {
91 try expect(trunc(1.3) == 1.0);
92 try expect(trunc(-1.3) == -1.0);
93 try expect(trunc(0.2) == 0.0);
94}
95
96test "trunc128" {
97 try expect(truncq(1.3) == 1.0);
98 try expect(truncq(-1.3) == -1.0);
99 try expect(truncq(0.2) == 0.0);
100}
101
102test "trunc32.special" {
103 try expect(truncf(0.0) == 0.0); // 0x3F800000
104 try expect(truncf(-0.0) == -0.0);
105 try expect(math.isPositiveInf(truncf(math.inf(f32))));
106 try expect(math.isNegativeInf(truncf(-math.inf(f32))));
107 try expect(math.isNan(truncf(math.nan(f32))));
108}
109
110test "trunc64.special" {
111 try expect(trunc(0.0) == 0.0);
112 try expect(trunc(-0.0) == -0.0);
113 try expect(math.isPositiveInf(trunc(math.inf(f64))));
114 try expect(math.isNegativeInf(trunc(-math.inf(f64))));
115 try expect(math.isNan(trunc(math.nan(f64))));
116}
117
118test "trunc128.special" {
119 try expect(truncq(0.0) == 0.0);
120 try expect(truncq(-0.0) == -0.0);
121 try expect(math.isPositiveInf(truncq(math.inf(f128))));
122 try expect(math.isNegativeInf(truncq(-math.inf(f128))));
123 try expect(math.isNan(truncq(math.nan(f128))));
124}
lib/std/testing.zig+1-1
......@@ -265,7 +265,7 @@ pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance
265265test "expectApproxEqRel" {
266266 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
267267 const eps_value = comptime math.epsilon(T);
268 const sqrt_eps_value = comptime math.sqrt(eps_value);
268 const sqrt_eps_value = comptime @sqrt(eps_value);
269269
270270 const pos_x: T = 12.0;
271271 const pos_y: T = pos_x + 2 * eps_value;
src/Air.zig+6-2
......@@ -249,12 +249,15 @@ pub const Inst = struct {
249249 /// Square root of a floating point number.
250250 /// Uses the `un_op` field.
251251 sqrt,
252 /// Sine a floating point number.
252 /// Sine function on a floating point number.
253253 /// Uses the `un_op` field.
254254 sin,
255 /// Cosine a floating point number.
255 /// Cosine function on a floating point number.
256256 /// Uses the `un_op` field.
257257 cos,
258 /// Tangent function on a floating point number.
259 /// Uses the `un_op` field.
260 tan,
258261 /// Base e exponential of a floating point number.
259262 /// Uses the `un_op` field.
260263 exp,
......@@ -921,6 +924,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
921924 .sqrt,
922925 .sin,
923926 .cos,
927 .tan,
924928 .exp,
925929 .exp2,
926930 .log,
src/AstGen.zig+4-2
......@@ -2237,7 +2237,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
22372237 .field_call_bind,
22382238 .field_ptr_named,
22392239 .field_val_named,
2240 .field_call_bind_named,
22412240 .func,
22422241 .func_inferred,
22432242 .int,
......@@ -2329,6 +2328,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
23292328 .sqrt,
23302329 .sin,
23312330 .cos,
2331 .tan,
23322332 .exp,
23332333 .exp2,
23342334 .log,
......@@ -7259,6 +7259,7 @@ fn builtinCall(
72597259 .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt),
72607260 .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin),
72617261 .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos),
7262 .tan => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tan),
72627263 .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp),
72637264 .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2),
72647265 .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log),
......@@ -7947,7 +7948,8 @@ fn calleeExpr(
79477948 if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {
79487949 const lhs = try expr(gz, scope, .ref, params[0]);
79497950 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
7950 return gz.addPlNode(.field_call_bind_named, node, Zir.Inst.FieldNamed{
7951 return gz.addExtendedPayload(.field_call_bind_named, Zir.Inst.FieldNamedNode{
7952 .node = gz.nodeIndexToRelative(node),
79517953 .lhs = lhs,
79527954 .field_name = field_name,
79537955 });
src/BuiltinFn.zig+8
......@@ -89,6 +89,7 @@ pub const Tag = enum {
8989 sqrt,
9090 sin,
9191 cos,
92 tan,
9293 exp,
9394 exp2,
9495 log,
......@@ -771,6 +772,13 @@ pub const list = list: {
771772 .param_count = 1,
772773 },
773774 },
775 .{
776 "@tan",
777 .{
778 .tag = .tan,
779 .param_count = 1,
780 },
781 },
774782 .{
775783 "@exp",
776784 .{
src/Liveness.zig+1
......@@ -422,6 +422,7 @@ fn analyzeInst(
422422 .sqrt,
423423 .sin,
424424 .cos,
425 .tan,
425426 .exp,
426427 .exp2,
427428 .log,
src/Sema.zig+37-37
......@@ -743,7 +743,6 @@ fn analyzeBodyInner(
743743 .field_val => try sema.zirFieldVal(block, inst),
744744 .field_val_named => try sema.zirFieldValNamed(block, inst),
745745 .field_call_bind => try sema.zirFieldCallBind(block, inst),
746 .field_call_bind_named => try sema.zirFieldCallBindNamed(block, inst),
747746 .func => try sema.zirFunc(block, inst, false),
748747 .func_inferred => try sema.zirFunc(block, inst, true),
749748 .import => try sema.zirImport(block, inst),
......@@ -855,6 +854,7 @@ fn analyzeBodyInner(
855854 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
856855 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin),
857856 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos),
857 .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan),
858858 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp),
859859 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),
860860 .log => try sema.zirUnaryMath(block, inst, .log, Value.log),
......@@ -910,35 +910,36 @@ fn analyzeBodyInner(
910910 const extended = datas[inst].extended;
911911 break :ext switch (extended.opcode) {
912912 // zig fmt: off
913 .func => try sema.zirFuncExtended( block, extended, inst),
914 .variable => try sema.zirVarExtended( block, extended),
915 .struct_decl => try sema.zirStructDecl( block, extended, inst),
916 .enum_decl => try sema.zirEnumDecl( block, extended),
917 .union_decl => try sema.zirUnionDecl( block, extended, inst),
918 .opaque_decl => try sema.zirOpaqueDecl( block, extended),
919 .ret_ptr => try sema.zirRetPtr( block, extended),
920 .ret_type => try sema.zirRetType( block, extended),
921 .this => try sema.zirThis( block, extended),
922 .ret_addr => try sema.zirRetAddr( block, extended),
923 .builtin_src => try sema.zirBuiltinSrc( block, extended),
924 .error_return_trace => try sema.zirErrorReturnTrace( block, extended),
925 .frame => try sema.zirFrame( block, extended),
926 .frame_address => try sema.zirFrameAddress( block, extended),
927 .alloc => try sema.zirAllocExtended( block, extended),
928 .builtin_extern => try sema.zirBuiltinExtern( block, extended),
929 .@"asm" => try sema.zirAsm( block, extended),
930 .typeof_peer => try sema.zirTypeofPeer( block, extended),
931 .compile_log => try sema.zirCompileLog( block, extended),
932 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
933 .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
934 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
935 .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
936 .c_undef => try sema.zirCUndef( block, extended),
937 .c_include => try sema.zirCInclude( block, extended),
938 .c_define => try sema.zirCDefine( block, extended),
939 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
940 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
941 .prefetch => try sema.zirPrefetch( block, extended),
913 .func => try sema.zirFuncExtended( block, extended, inst),
914 .variable => try sema.zirVarExtended( block, extended),
915 .struct_decl => try sema.zirStructDecl( block, extended, inst),
916 .enum_decl => try sema.zirEnumDecl( block, extended),
917 .union_decl => try sema.zirUnionDecl( block, extended, inst),
918 .opaque_decl => try sema.zirOpaqueDecl( block, extended),
919 .ret_ptr => try sema.zirRetPtr( block, extended),
920 .ret_type => try sema.zirRetType( block, extended),
921 .this => try sema.zirThis( block, extended),
922 .ret_addr => try sema.zirRetAddr( block, extended),
923 .builtin_src => try sema.zirBuiltinSrc( block, extended),
924 .error_return_trace => try sema.zirErrorReturnTrace( block, extended),
925 .frame => try sema.zirFrame( block, extended),
926 .frame_address => try sema.zirFrameAddress( block, extended),
927 .alloc => try sema.zirAllocExtended( block, extended),
928 .builtin_extern => try sema.zirBuiltinExtern( block, extended),
929 .@"asm" => try sema.zirAsm( block, extended),
930 .typeof_peer => try sema.zirTypeofPeer( block, extended),
931 .compile_log => try sema.zirCompileLog( block, extended),
932 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
933 .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
934 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
935 .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
936 .c_undef => try sema.zirCUndef( block, extended),
937 .c_include => try sema.zirCInclude( block, extended),
938 .c_define => try sema.zirCDefine( block, extended),
939 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
940 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
941 .prefetch => try sema.zirPrefetch( block, extended),
942 .field_call_bind_named => try sema.zirFieldCallBindNamed(block, extended),
942943 // zig fmt: on
943944 .dbg_block_begin => {
944945 dbg_block_begins += 1;
......@@ -6938,14 +6939,13 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
69386939 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
69396940}
69406941
6941fn zirFieldCallBindNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6942fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
69426943 const tracy = trace(@src());
69436944 defer tracy.end();
69446945
6945 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6946 const src = inst_data.src();
6947 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
6948 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
6946 const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data;
6947 const src: LazySrcLoc = .{ .node_offset = extra.node };
6948 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
69496949 const object_ptr = sema.resolveInst(extra.lhs);
69506950 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
69516951 return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src);
......@@ -14051,7 +14051,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1405114051 const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) {
1405214052 error.FloatCannotFit => {
1405314053 return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{
14054 std.math.floor(val.toFloat(f64)),
14054 @floor(val.toFloat(f64)),
1405514055 dest_ty.fmt(sema.mod),
1405614056 });
1405714057 },
......@@ -18371,7 +18371,7 @@ fn coerce(
1837118371 }
1837218372 const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) {
1837318373 error.FloatCannotFit => {
18374 return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty.fmt(sema.mod) });
18374 return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ @floor(val.toFloat(f64)), dest_ty.fmt(sema.mod) });
1837518375 },
1837618376 else => |e| return e,
1837718377 };
src/Zir.zig+21-12
......@@ -407,15 +407,6 @@ pub const Inst = struct {
407407 /// The field name is a comptime instruction. Used by @field.
408408 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
409409 field_val_named,
410 /// Given a pointer to a struct or object that contains virtual fields, returns the
411 /// named field. If there is no named field, searches in the type for a decl that
412 /// matches the field name. The decl is resolved and we ensure that it's a function
413 /// which can accept the object as the first parameter, with one pointer fixup. If
414 /// all of that works, this instruction produces a special "bound function" value
415 /// which contains both the function and the saved first parameter value.
416 /// Bound functions may only be used as the function parameter to a `call` or
417 /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler.
418 field_call_bind_named,
419410 /// Returns a function type, or a function instance, depending on whether
420411 /// the body_len is 0. Calling convention is auto.
421412 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
......@@ -797,6 +788,8 @@ pub const Inst = struct {
797788 sin,
798789 /// Implement builtin `@cos`. Uses `un_node`.
799790 cos,
791 /// Implement builtin `@tan`. Uses `un_node`.
792 tan,
800793 /// Implement builtin `@exp`. Uses `un_node`.
801794 exp,
802795 /// Implement builtin `@exp2`. Uses `un_node`.
......@@ -1069,7 +1062,6 @@ pub const Inst = struct {
10691062 .field_call_bind,
10701063 .field_ptr_named,
10711064 .field_val_named,
1072 .field_call_bind_named,
10731065 .func,
10741066 .func_inferred,
10751067 .has_decl,
......@@ -1179,6 +1171,7 @@ pub const Inst = struct {
11791171 .sqrt,
11801172 .sin,
11811173 .cos,
1174 .tan,
11821175 .exp,
11831176 .exp2,
11841177 .log,
......@@ -1358,7 +1351,6 @@ pub const Inst = struct {
13581351 .field_call_bind,
13591352 .field_ptr_named,
13601353 .field_val_named,
1361 .field_call_bind_named,
13621354 .func,
13631355 .func_inferred,
13641356 .has_decl,
......@@ -1451,6 +1443,7 @@ pub const Inst = struct {
14511443 .sqrt,
14521444 .sin,
14531445 .cos,
1446 .tan,
14541447 .exp,
14551448 .exp2,
14561449 .log,
......@@ -1607,7 +1600,6 @@ pub const Inst = struct {
16071600 .field_ptr_named = .pl_node,
16081601 .field_val_named = .pl_node,
16091602 .field_call_bind = .pl_node,
1610 .field_call_bind_named = .pl_node,
16111603 .func = .pl_node,
16121604 .func_inferred = .pl_node,
16131605 .import = .str_tok,
......@@ -1713,6 +1705,7 @@ pub const Inst = struct {
17131705 .sqrt = .un_node,
17141706 .sin = .un_node,
17151707 .cos = .un_node,
1708 .tan = .un_node,
17161709 .exp = .un_node,
17171710 .exp2 = .un_node,
17181711 .log = .un_node,
......@@ -1928,6 +1921,16 @@ pub const Inst = struct {
19281921 dbg_block_begin,
19291922 /// Marks the end of a semantic scope for debug info variables.
19301923 dbg_block_end,
1924 /// Given a pointer to a struct or object that contains virtual fields, returns the
1925 /// named field. If there is no named field, searches in the type for a decl that
1926 /// matches the field name. The decl is resolved and we ensure that it's a function
1927 /// which can accept the object as the first parameter, with one pointer fixup. If
1928 /// all of that works, this instruction produces a special "bound function" value
1929 /// which contains both the function and the saved first parameter value.
1930 /// Bound functions may only be used as the function parameter to a `call` or
1931 /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler.
1932 /// Uses `pl_node` field. The AST node is the `@field` builtin. Payload is FieldNamedNode.
1933 field_call_bind_named,
19311934
19321935 pub const InstData = struct {
19331936 opcode: Extended,
......@@ -2963,6 +2966,12 @@ pub const Inst = struct {
29632966 field_name: Ref,
29642967 };
29652968
2969 pub const FieldNamedNode = struct {
2970 node: i32,
2971 lhs: Ref,
2972 field_name: Ref,
2973 };
2974
29662975 pub const As = struct {
29672976 dest_type: Ref,
29682977 operand: Ref,
src/arch/aarch64/CodeGen.zig+1
......@@ -533,6 +533,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
533533 .sqrt,
534534 .sin,
535535 .cos,
536 .tan,
536537 .exp,
537538 .exp2,
538539 .log,
src/arch/arm/CodeGen.zig+1
......@@ -571,6 +571,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
571571 .sqrt,
572572 .sin,
573573 .cos,
574 .tan,
574575 .exp,
575576 .exp2,
576577 .log,
src/arch/riscv64/CodeGen.zig+1
......@@ -500,6 +500,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
500500 .sqrt,
501501 .sin,
502502 .cos,
503 .tan,
503504 .exp,
504505 .exp2,
505506 .log,
src/arch/sparcv9/CodeGen.zig+1
......@@ -451,6 +451,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
451451 .sqrt,
452452 .sin,
453453 .cos,
454 .tan,
454455 .exp,
455456 .exp2,
456457 .log,
src/arch/wasm/CodeGen.zig+1
......@@ -1559,6 +1559,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
15591559 .sqrt,
15601560 .sin,
15611561 .cos,
1562 .tan,
15621563 .exp,
15631564 .exp2,
15641565 .log,
src/arch/x86_64/CodeGen.zig+1
......@@ -656,6 +656,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
656656 .sqrt,
657657 .sin,
658658 .cos,
659 .tan,
659660 .exp,
660661 .exp2,
661662 .log,
src/codegen/c.zig+1
......@@ -1749,6 +1749,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
17491749 .sqrt,
17501750 .sin,
17511751 .cos,
1752 .tan,
17521753 .exp,
17531754 .exp2,
17541755 .log,
src/codegen/llvm.zig+298-145
......@@ -3518,19 +3518,20 @@ pub const FuncGen = struct {
35183518 .shr => try self.airShr(inst, false),
35193519 .shr_exact => try self.airShr(inst, true),
35203520
3521 .sqrt => try self.airUnaryOp(inst, "sqrt"),
3522 .sin => try self.airUnaryOp(inst, "sin"),
3523 .cos => try self.airUnaryOp(inst, "cos"),
3524 .exp => try self.airUnaryOp(inst, "exp"),
3525 .exp2 => try self.airUnaryOp(inst, "exp2"),
3526 .log => try self.airUnaryOp(inst, "log"),
3527 .log2 => try self.airUnaryOp(inst, "log2"),
3528 .log10 => try self.airUnaryOp(inst, "log10"),
3529 .fabs => try self.airUnaryOp(inst, "fabs"),
3530 .floor => try self.airUnaryOp(inst, "floor"),
3531 .ceil => try self.airUnaryOp(inst, "ceil"),
3532 .round => try self.airUnaryOp(inst, "round"),
3533 .trunc_float => try self.airUnaryOp(inst, "trunc"),
3521 .sqrt => try self.airUnaryOp(inst, .sqrt),
3522 .sin => try self.airUnaryOp(inst, .sin),
3523 .cos => try self.airUnaryOp(inst, .cos),
3524 .tan => try self.airUnaryOp(inst, .tan),
3525 .exp => try self.airUnaryOp(inst, .exp),
3526 .exp2 => try self.airUnaryOp(inst, .exp2),
3527 .log => try self.airUnaryOp(inst, .log),
3528 .log2 => try self.airUnaryOp(inst, .log2),
3529 .log10 => try self.airUnaryOp(inst, .log10),
3530 .fabs => try self.airUnaryOp(inst, .fabs),
3531 .floor => try self.airUnaryOp(inst, .floor),
3532 .ceil => try self.airUnaryOp(inst, .ceil),
3533 .round => try self.airUnaryOp(inst, .round),
3534 .trunc_float => try self.airUnaryOp(inst, .trunc),
35343535
35353536 .cmp_eq => try self.airCmp(inst, .eq),
35363537 .cmp_gt => try self.airCmp(inst, .gt),
......@@ -3905,7 +3906,7 @@ pub const FuncGen = struct {
39053906 rhs: *const llvm.Value,
39063907 operand_ty: Type,
39073908 op: math.CompareOperator,
3908 ) *const llvm.Value {
3909 ) Allocator.Error!*const llvm.Value {
39093910 var int_buffer: Type.Payload.Bits = undefined;
39103911 var opt_buffer: Type.Payload.ElemType = undefined;
39113912
......@@ -3947,7 +3948,7 @@ pub const FuncGen = struct {
39473948 self.builder.positionBuilderAtEnd(both_pl_block);
39483949 const lhs_payload = self.optPayloadHandle(lhs, is_by_ref);
39493950 const rhs_payload = self.optPayloadHandle(rhs, is_by_ref);
3950 const payload_cmp = self.cmp(lhs_payload, rhs_payload, payload_ty, op);
3951 const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op);
39513952 _ = self.builder.buildBr(end_block);
39523953 const both_pl_block_end = self.builder.getInsertBlock();
39533954
......@@ -3983,17 +3984,7 @@ pub const FuncGen = struct {
39833984 );
39843985 return phi_node;
39853986 },
3986 .Float => {
3987 const operation: llvm.RealPredicate = switch (op) {
3988 .eq => .OEQ,
3989 .neq => .UNE,
3990 .lt => .OLT,
3991 .lte => .OLE,
3992 .gt => .OGT,
3993 .gte => .OGE,
3994 };
3995 return self.builder.buildFCmp(operation, lhs, rhs, "");
3996 },
3987 .Float => return self.buildFloatCmp(op, operand_ty, .{ lhs, rhs }),
39973988 else => unreachable,
39983989 };
39993990 const is_signed = int_ty.isSignedInt();
......@@ -5221,7 +5212,7 @@ pub const FuncGen = struct {
52215212 const inst_ty = self.air.typeOfIndex(inst);
52225213 const scalar_ty = inst_ty.scalarType();
52235214
5224 if (scalar_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
5215 if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, 2, .{ lhs, rhs });
52255216 if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
52265217 return self.builder.buildNUWAdd(lhs, rhs, "");
52275218 }
......@@ -5260,7 +5251,7 @@ pub const FuncGen = struct {
52605251 const inst_ty = self.air.typeOfIndex(inst);
52615252 const scalar_ty = inst_ty.scalarType();
52625253
5263 if (scalar_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
5254 if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, 2, .{ lhs, rhs });
52645255 if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
52655256 return self.builder.buildNUWSub(lhs, rhs, "");
52665257 }
......@@ -5298,7 +5289,7 @@ pub const FuncGen = struct {
52985289 const inst_ty = self.air.typeOfIndex(inst);
52995290 const scalar_ty = inst_ty.scalarType();
53005291
5301 if (scalar_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");
5292 if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, 2, .{ lhs, rhs });
53025293 if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
53035294 return self.builder.buildNUWMul(lhs, rhs, "");
53045295 }
......@@ -5333,8 +5324,9 @@ pub const FuncGen = struct {
53335324 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
53345325 const lhs = try self.resolveInst(bin_op.lhs);
53355326 const rhs = try self.resolveInst(bin_op.rhs);
5327 const inst_ty = self.air.typeOfIndex(inst);
53365328
5337 return self.builder.buildFDiv(lhs, rhs, "");
5329 return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
53385330 }
53395331
53405332 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -5347,8 +5339,8 @@ pub const FuncGen = struct {
53475339 const scalar_ty = inst_ty.scalarType();
53485340
53495341 if (scalar_ty.isRuntimeFloat()) {
5350 const result = self.builder.buildFDiv(lhs, rhs, "");
5351 return self.callTrunc(result, inst_ty);
5342 const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
5343 return self.buildFloatOp(.trunc, inst_ty, 1, .{result});
53525344 }
53535345 if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
53545346 return self.builder.buildUDiv(lhs, rhs, "");
......@@ -5364,8 +5356,8 @@ pub const FuncGen = struct {
53645356 const scalar_ty = inst_ty.scalarType();
53655357
53665358 if (scalar_ty.isRuntimeFloat()) {
5367 const result = self.builder.buildFDiv(lhs, rhs, "");
5368 return try self.callFloor(result, inst_ty);
5359 const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
5360 return self.buildFloatOp(.floor, inst_ty, 1, .{result});
53695361 }
53705362 if (scalar_ty.isSignedInt()) {
53715363 // const d = @divTrunc(a, b);
......@@ -5395,7 +5387,7 @@ pub const FuncGen = struct {
53955387 const inst_ty = self.air.typeOfIndex(inst);
53965388 const scalar_ty = inst_ty.scalarType();
53975389
5398 if (scalar_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
5390 if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
53995391 if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");
54005392 return self.builder.buildExactUDiv(lhs, rhs, "");
54015393 }
......@@ -5409,7 +5401,7 @@ pub const FuncGen = struct {
54095401 const inst_ty = self.air.typeOfIndex(inst);
54105402 const scalar_ty = inst_ty.scalarType();
54115403
5412 if (scalar_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, "");
5404 if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
54135405 if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
54145406 return self.builder.buildURem(lhs, rhs, "");
54155407 }
......@@ -5425,11 +5417,11 @@ pub const FuncGen = struct {
54255417 const scalar_ty = inst_ty.scalarType();
54265418
54275419 if (scalar_ty.isRuntimeFloat()) {
5428 const a = self.builder.buildFRem(lhs, rhs, "");
5429 const b = self.builder.buildFAdd(a, rhs, "");
5430 const c = self.builder.buildFRem(b, rhs, "");
5420 const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
5421 const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs });
5422 const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs });
54315423 const zero = inst_llvm_ty.constNull();
5432 const ltz = self.builder.buildFCmp(.OLT, lhs, zero, "");
5424 const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero });
54335425 return self.builder.buildSelect(ltz, c, a, "");
54345426 }
54355427 if (scalar_ty.isSignedInt()) {
......@@ -5508,75 +5500,266 @@ pub const FuncGen = struct {
55085500 return result_struct;
55095501 }
55105502
5511 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
5512 if (self.liveness.isUnused(inst)) return null;
5503 fn buildElementwiseCall(
5504 self: *FuncGen,
5505 llvm_fn: *const llvm.Value,
5506 args_vectors: []const *const llvm.Value,
5507 result_vector: *const llvm.Value,
5508 vector_len: usize,
5509 ) !*const llvm.Value {
5510 const args_len = @intCast(c_uint, args_vectors.len);
5511 const llvm_i32 = self.context.intType(32);
5512 assert(args_len <= 3);
55135513
5514 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
5515 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
5514 var i: usize = 0;
5515 var result = result_vector;
5516 while (i < vector_len) : (i += 1) {
5517 const index_i32 = llvm_i32.constInt(i, .False);
55165518
5517 const mulend1 = try self.resolveInst(extra.lhs);
5518 const mulend2 = try self.resolveInst(extra.rhs);
5519 const addend = try self.resolveInst(pl_op.operand);
5519 var args: [3]*const llvm.Value = undefined;
5520 for (args_vectors) |arg_vector, k| {
5521 args[k] = self.builder.buildExtractElement(arg_vector, index_i32, "");
5522 }
5523 const result_elem = self.builder.buildCall(llvm_fn, &args, args_len, .C, .Auto, "");
5524 result = self.builder.buildInsertElement(result, result_elem, index_i32, "");
5525 }
5526 return result;
5527 }
55205528
5521 const ty = self.air.typeOfIndex(inst);
5522 const llvm_ty = try self.dg.llvmType(ty);
5523 const scalar_ty = ty.scalarType();
5524 const target = self.dg.module.getTarget();
5529 fn getLibcFunction(
5530 self: *FuncGen,
5531 fn_name: [:0]const u8,
5532 param_types: []const *const llvm.Type,
5533 return_type: *const llvm.Type,
5534 ) *const llvm.Value {
5535 return self.dg.object.llvm_module.getNamedFunction(fn_name.ptr) orelse b: {
5536 const alias = self.dg.object.llvm_module.getNamedGlobalAlias(fn_name.ptr, fn_name.len);
5537 break :b if (alias) |a| a.getAliasee() else null;
5538 } orelse b: {
5539 const params_len = @intCast(c_uint, param_types.len);
5540 const fn_type = llvm.functionType(return_type, param_types.ptr, params_len, .False);
5541 const f = self.dg.object.llvm_module.addFunction(fn_name, fn_type);
5542 break :b f;
5543 };
5544 }
5545
5546 fn libcFloatPrefix(float_bits: u16) []const u8 {
5547 return switch (float_bits) {
5548 16, 80 => "__",
5549 32, 64, 128 => "",
5550 else => unreachable,
5551 };
5552 }
55255553
5526 const Strat = union(enum) {
5527 intrinsic,
5528 libc: [*:0]const u8,
5554 fn libcFloatSuffix(float_bits: u16) []const u8 {
5555 return switch (float_bits) {
5556 16 => "h", // Non-standard
5557 32 => "f",
5558 64 => "",
5559 80 => "x", // Non-standard
5560 128 => "q", // Non-standard (mimics convention in GCC libquadmath)
5561 else => unreachable,
55295562 };
5563 }
55305564
5531 const strat: Strat = switch (scalar_ty.floatBits(target)) {
5532 16, 32, 64 => Strat.intrinsic,
5533 80 => if (CType.longdouble.sizeInBits(target) == 80) Strat{ .intrinsic = {} } else Strat{ .libc = "__fmax" },
5534 // LLVM always lowers the fma builtin for f128 to fmal, which is for `long double`.
5535 // On some targets this will be correct; on others it will be incorrect.
5536 128 => if (CType.longdouble.sizeInBits(target) == 128) Strat{ .intrinsic = {} } else Strat{ .libc = "fmaq" },
5565 fn compilerRtFloatAbbrev(float_bits: u16) []const u8 {
5566 return switch (float_bits) {
5567 16 => "h",
5568 32 => "s",
5569 64 => "d",
5570 80 => "x",
5571 128 => "t",
55375572 else => unreachable,
55385573 };
5574 }
55395575
5540 switch (strat) {
5541 .intrinsic => {
5542 const llvm_fn = self.getIntrinsic("llvm.fma", &.{llvm_ty});
5543 const params = [_]*const llvm.Value{ mulend1, mulend2, addend };
5544 return self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");
5545 },
5546 .libc => |fn_name| {
5547 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
5548 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(fn_name) orelse b: {
5549 const param_types = [_]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty };
5550 const fn_type = llvm.functionType(scalar_llvm_ty, &param_types, param_types.len, .False);
5551 break :b self.dg.object.llvm_module.addFunction(fn_name, fn_type);
5552 };
5576 /// Creates a floating point comparison by lowering to the appropriate
5577 /// hardware instruction or softfloat routine for the target
5578 fn buildFloatCmp(
5579 self: *FuncGen,
5580 pred: math.CompareOperator,
5581 ty: Type,
5582 params: [2]*const llvm.Value,
5583 ) !*const llvm.Value {
5584 const target = self.dg.module.getTarget();
5585 const scalar_ty = ty.scalarType();
5586 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
55535587
5554 if (ty.zigTypeTag() == .Vector) {
5555 const llvm_i32 = self.context.intType(32);
5556 const vector_llvm_ty = try self.dg.llvmType(ty);
5588 if (intrinsicsAllowed(scalar_ty, target)) {
5589 const llvm_predicate: llvm.RealPredicate = switch (pred) {
5590 .eq => .OEQ,
5591 .neq => .UNE,
5592 .lt => .OLT,
5593 .lte => .OLE,
5594 .gt => .OGT,
5595 .gte => .OGE,
5596 };
5597 return self.builder.buildFCmp(llvm_predicate, params[0], params[1], "");
5598 }
5599
5600 const float_bits = scalar_ty.floatBits(target);
5601 const compiler_rt_float_abbrev = compilerRtFloatAbbrev(float_bits);
5602 var fn_name_buf: [64]u8 = undefined;
5603 const fn_base_name = switch (pred) {
5604 .neq => "ne",
5605 .eq => "eq",
5606 .lt => "lt",
5607 .lte => "le",
5608 .gt => "gt",
5609 .gte => "ge",
5610 };
5611 const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f2", .{
5612 fn_base_name, compiler_rt_float_abbrev,
5613 }) catch unreachable;
55575614
5558 var i: usize = 0;
5559 var vector = vector_llvm_ty.getUndef();
5560 while (i < ty.vectorLen()) : (i += 1) {
5561 const index_i32 = llvm_i32.constInt(i, .False);
5615 const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty };
5616 const llvm_i32 = self.context.intType(32);
5617 const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32);
55625618
5563 const mulend1_elem = self.builder.buildExtractElement(mulend1, index_i32, "");
5564 const mulend2_elem = self.builder.buildExtractElement(mulend2, index_i32, "");
5565 const addend_elem = self.builder.buildExtractElement(addend, index_i32, "");
5619 const zero = llvm_i32.constInt(0, .False);
5620 const int_pred: llvm.IntPredicate = switch (pred) {
5621 .eq => .EQ,
5622 .neq => .NE,
5623 .lt => .SLT,
5624 .lte => .SLE,
5625 .gt => .SGT,
5626 .gte => .SGE,
5627 };
55665628
5567 const params = [_]*const llvm.Value{ mulend1_elem, mulend2_elem, addend_elem };
5568 const mul_add = self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");
5629 if (ty.zigTypeTag() == .Vector) {
5630 const vec_len = ty.vectorLen();
5631 const vector_result_ty = llvm_i32.vectorType(vec_len);
5632
5633 var result = vector_result_ty.getUndef();
5634 result = try self.buildElementwiseCall(libc_fn, &params, result, vec_len);
5635
5636 const zero_vector = self.builder.buildVectorSplat(vec_len, zero, "");
5637 return self.builder.buildICmp(int_pred, result, zero_vector, "");
5638 }
5639
5640 const result = self.builder.buildCall(libc_fn, &params, params.len, .C, .Auto, "");
5641 return self.builder.buildICmp(int_pred, result, zero, "");
5642 }
5643
5644 const FloatOp = enum {
5645 add,
5646 ceil,
5647 cos,
5648 div,
5649 exp,
5650 exp2,
5651 fabs,
5652 floor,
5653 fma,
5654 log,
5655 log10,
5656 log2,
5657 fmax,
5658 fmin,
5659 mul,
5660 fmod,
5661 round,
5662 sin,
5663 sqrt,
5664 sub,
5665 tan,
5666 trunc,
5667 };
55695668
5570 vector = self.builder.buildInsertElement(vector, mul_add, index_i32, "");
5571 }
5669 const FloatOpStrat = union(enum) {
5670 intrinsic: []const u8,
5671 libc: [:0]const u8,
5672 };
55725673
5573 return vector;
5574 } else {
5575 const params = [_]*const llvm.Value{ mulend1, mulend2, addend };
5576 return self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");
5674 /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.)
5675 /// by lowering to the appropriate hardware instruction or softfloat
5676 /// routine for the target
5677 fn buildFloatOp(
5678 self: *FuncGen,
5679 comptime op: FloatOp,
5680 ty: Type,
5681 comptime params_len: usize,
5682 params: [params_len]*const llvm.Value,
5683 ) !*const llvm.Value {
5684 const target = self.dg.module.getTarget();
5685 const scalar_ty = ty.scalarType();
5686 const llvm_ty = try self.dg.llvmType(ty);
5687 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
5688
5689 const intrinsics_allowed = op != .tan and intrinsicsAllowed(scalar_ty, target);
5690 var fn_name_buf: [64]u8 = undefined;
5691 const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) {
5692 // Some operations are dedicated LLVM instructions, not available as intrinsics
5693 .add => return self.builder.buildFAdd(params[0], params[1], ""),
5694 .sub => return self.builder.buildFSub(params[0], params[1], ""),
5695 .mul => return self.builder.buildFMul(params[0], params[1], ""),
5696 .div => return self.builder.buildFDiv(params[0], params[1], ""),
5697 .fmod => return self.builder.buildFRem(params[0], params[1], ""),
5698 .fmax => return self.builder.buildMaxNum(params[0], params[1], ""),
5699 .fmin => return self.builder.buildMinNum(params[0], params[1], ""),
5700 else => .{ .intrinsic = "llvm." ++ @tagName(op) },
5701 } else b: {
5702 const float_bits = scalar_ty.floatBits(target);
5703 break :b switch (op) {
5704 .add, .sub, .div, .mul => FloatOpStrat{
5705 .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{
5706 @tagName(op), compilerRtFloatAbbrev(float_bits),
5707 }) catch unreachable,
5708 },
5709 .ceil,
5710 .cos,
5711 .exp,
5712 .exp2,
5713 .fabs,
5714 .floor,
5715 .fma,
5716 .fmax,
5717 .fmin,
5718 .fmod,
5719 .log,
5720 .log10,
5721 .log2,
5722 .round,
5723 .sin,
5724 .sqrt,
5725 .tan,
5726 .trunc,
5727 => FloatOpStrat{
5728 .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}{s}", .{
5729 libcFloatPrefix(float_bits), @tagName(op), libcFloatSuffix(float_bits),
5730 }) catch unreachable,
5731 },
5732 };
5733 };
5734
5735 const llvm_fn: *const llvm.Value = switch (strat) {
5736 .intrinsic => |fn_name| self.getIntrinsic(fn_name, &.{llvm_ty}),
5737 .libc => |fn_name| b: {
5738 const param_types = [3]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty };
5739 const libc_fn = self.getLibcFunction(fn_name, param_types[0..params.len], scalar_llvm_ty);
5740 if (ty.zigTypeTag() == .Vector) {
5741 const result = llvm_ty.getUndef();
5742 return self.buildElementwiseCall(libc_fn, &params, result, ty.vectorLen());
55775743 }
5744
5745 break :b libc_fn;
55785746 },
5579 }
5747 };
5748 return self.builder.buildCall(llvm_fn, &params, params_len, .C, .Auto, "");
5749 }
5750
5751 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
5752 if (self.liveness.isUnused(inst)) return null;
5753
5754 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
5755 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
5756
5757 const mulend1 = try self.resolveInst(extra.lhs);
5758 const mulend2 = try self.resolveInst(extra.rhs);
5759 const addend = try self.resolveInst(pl_op.operand);
5760
5761 const ty = self.air.typeOfIndex(inst);
5762 return self.buildFloatOp(.fma, ty, 3, .{ mulend1, mulend2, addend });
55805763 }
55815764
55825765 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -6381,14 +6564,14 @@ pub const FuncGen = struct {
63816564 }
63826565 }
63836566
6384 fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value {
6567 fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*const llvm.Value {
63856568 if (self.liveness.isUnused(inst)) return null;
63866569
63876570 const un_op = self.air.instructions.items(.data)[inst].un_op;
63886571 const operand = try self.resolveInst(un_op);
63896572 const operand_ty = self.air.typeOf(un_op);
63906573
6391 return self.callFloatUnary(operand, operand_ty, llvm_fn_name);
6574 return self.buildFloatOp(op, operand_ty, 1, .{operand});
63926575 }
63936576
63946577 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value {
......@@ -6652,17 +6835,9 @@ pub const FuncGen = struct {
66526835
66536836 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
66546837 const scalar = try self.resolveInst(ty_op.operand);
6655 const scalar_ty = self.air.typeOf(ty_op.operand);
66566838 const vector_ty = self.air.typeOfIndex(inst);
66576839 const len = vector_ty.vectorLen();
6658 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
6659 const op_llvm_ty = scalar_llvm_ty.vectorType(1);
6660 const u32_llvm_ty = self.context.intType(32);
6661 const mask_llvm_ty = u32_llvm_ty.vectorType(len);
6662 const undef_vector = op_llvm_ty.getUndef();
6663 const u32_zero = u32_llvm_ty.constNull();
6664 const op_vector = self.builder.buildInsertElement(undef_vector, scalar, u32_zero, "");
6665 return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), "");
6840 return self.builder.buildVectorSplat(len, scalar, "");
66666841 }
66676842
66686843 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -7191,48 +7366,6 @@ pub const FuncGen = struct {
71917366 return self.builder.buildExtractValue(opt_handle, 0, "");
71927367 }
71937368
7194 fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
7195 return self.callFloatUnary(arg, ty, "floor");
7196 }
7197
7198 fn callCeil(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
7199 return self.callFloatUnary(arg, ty, "ceil");
7200 }
7201
7202 fn callTrunc(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
7203 return self.callFloatUnary(arg, ty, "trunc");
7204 }
7205
7206 fn callFloatUnary(
7207 self: *FuncGen,
7208 arg: *const llvm.Value,
7209 ty: Type,
7210 name: []const u8,
7211 ) !*const llvm.Value {
7212 const target = self.dg.module.getTarget();
7213
7214 var fn_name_buf: [100]u8 = undefined;
7215 const llvm_fn_name = switch (ty.zigTypeTag()) {
7216 .Vector => std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.v{d}f{d}", .{
7217 name, ty.vectorLen(), ty.childType().floatBits(target),
7218 }) catch unreachable,
7219 .Float => std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.f{d}", .{
7220 name, ty.floatBits(target),
7221 }) catch unreachable,
7222 else => unreachable,
7223 };
7224
7225 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
7226 const operand_llvm_ty = try self.dg.llvmType(ty);
7227 const param_types = [_]*const llvm.Type{operand_llvm_ty};
7228 const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False);
7229 break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
7230 };
7231
7232 const args: [1]*const llvm.Value = .{arg};
7233 return self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, "");
7234 }
7235
72367369 fn fieldPtr(
72377370 self: *FuncGen,
72387371 inst: Air.Inst.Index,
......@@ -8055,6 +8188,26 @@ fn backendSupportsF80(target: std.Target) bool {
80558188 };
80568189}
80578190
8191/// This function returns true if we expect LLVM to lower f16 correctly
8192/// and false if we expect LLVM to crash if it counters an f16 type or
8193/// if it produces miscompilations.
8194fn backendSupportsF16(target: std.Target) bool {
8195 return switch (target.cpu.arch) {
8196 else => true,
8197 };
8198}
8199
8200/// LLVM does not support all relevant intrinsics for all targets, so we
8201/// may need to manually generate a libc call
8202fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
8203 return switch (scalar_ty.tag()) {
8204 .f16 => backendSupportsF16(target),
8205 .f80 => target.longDoubleIs(f80) and backendSupportsF80(target),
8206 .f128 => target.longDoubleIs(f128),
8207 else => true,
8208 };
8209}
8210
80588211/// We need to insert extra padding if LLVM's isn't enough.
80598212/// However we don't want to ever call LLVMABIAlignmentOfType or
80608213/// LLVMABISizeOfType because these functions will trip assertions
src/codegen/llvm/bindings.zig+8
......@@ -675,6 +675,14 @@ pub const Builder = opaque {
675675 Name: [*:0]const u8,
676676 ) *const Value;
677677
678 pub const buildVectorSplat = LLVMBuildVectorSplat;
679 extern fn LLVMBuildVectorSplat(
680 *const Builder,
681 ElementCount: c_uint,
682 EltVal: *const Value,
683 Name: [*:0]const u8,
684 ) *const Value;
685
678686 pub const buildPtrToInt = LLVMBuildPtrToInt;
679687 extern fn LLVMBuildPtrToInt(
680688 *const Builder,
src/print_air.zig+1
......@@ -158,6 +158,7 @@ const Writer = struct {
158158 .sqrt,
159159 .sin,
160160 .cos,
161 .tan,
161162 .exp,
162163 .exp2,
163164 .log,
src/print_zir.zig+11-1
......@@ -207,6 +207,7 @@ const Writer = struct {
207207 .sqrt,
208208 .sin,
209209 .cos,
210 .tan,
210211 .exp,
211212 .exp2,
212213 .log,
......@@ -400,7 +401,6 @@ const Writer = struct {
400401
401402 .field_ptr_named,
402403 .field_val_named,
403 .field_call_bind_named,
404404 => try self.writePlNodeFieldNamed(stream, inst),
405405
406406 .as_node => try self.writeAs(stream, inst),
......@@ -509,6 +509,16 @@ const Writer = struct {
509509 try stream.writeAll(")) ");
510510 try self.writeSrc(stream, src);
511511 },
512
513 .field_call_bind_named => {
514 const extra = self.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data;
515 const src: LazySrcLoc = .{ .node_offset = extra.node };
516 try self.writeInstRef(stream, extra.lhs);
517 try stream.writeAll(", ");
518 try self.writeInstRef(stream, extra.field_name);
519 try stream.writeAll(") ");
520 try self.writeSrc(stream, src);
521 },
512522 }
513523 }
514524
src/stage1/all_types.hpp+1
......@@ -1768,6 +1768,7 @@ enum BuiltinFnId {
17681768 BuiltinFnIdSqrt,
17691769 BuiltinFnIdSin,
17701770 BuiltinFnIdCos,
1771 BuiltinFnIdTan,
17711772 BuiltinFnIdExp,
17721773 BuiltinFnIdExp2,
17731774 BuiltinFnIdLog,
src/stage1/analyze.cpp+6-2
......@@ -8928,7 +8928,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
89288928
89298929 assert(next_offset >= llvm_next_offset);
89308930 if (next_offset > llvm_next_offset) {
8931 size_t pad_bytes = next_offset - (field->offset + LLVMStoreSizeOfType(g->target_data_ref, llvm_type));
8931 size_t pad_bytes = next_offset - (field->offset + LLVMABISizeOfType(g->target_data_ref, llvm_type));
89328932 if (pad_bytes != 0) {
89338933 LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);
89348934 element_types[gen_field_index] = pad_llvm_type;
......@@ -10375,7 +10375,7 @@ void ZigValue::dump() {
1037510375
1037610376// float ops that take a single argument
1037710377//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, lround, llround, lrint, llrint
10378const char *float_op_to_name(BuiltinFnId op) {
10378const char *float_un_op_to_name(BuiltinFnId op) {
1037910379 switch (op) {
1038010380 case BuiltinFnIdSqrt:
1038110381 return "sqrt";
......@@ -10383,6 +10383,8 @@ const char *float_op_to_name(BuiltinFnId op) {
1038310383 return "sin";
1038410384 case BuiltinFnIdCos:
1038510385 return "cos";
10386 case BuiltinFnIdTan:
10387 return "tan";
1038610388 case BuiltinFnIdExp:
1038710389 return "exp";
1038810390 case BuiltinFnIdExp2:
......@@ -10405,6 +10407,8 @@ const char *float_op_to_name(BuiltinFnId op) {
1040510407 return "nearbyint";
1040610408 case BuiltinFnIdRound:
1040710409 return "round";
10410 case BuiltinFnIdMulAdd:
10411 return "fma";
1040810412 default:
1040910413 zig_unreachable();
1041010414 }
src/stage1/analyze.hpp+1-1
......@@ -307,7 +307,7 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src);
307307bool type_has_optional_repr(ZigType *ty);
308308bool is_opt_err_set(ZigType *ty);
309309bool type_is_numeric(ZigType *ty);
310const char *float_op_to_name(BuiltinFnId op);
310const char *float_un_op_to_name(BuiltinFnId op);
311311
312312#define src_assert(OK, SOURCE_NODE) src_assert_impl((OK), (SOURCE_NODE), __FILE__, __LINE__)
313313
src/stage1/astgen.cpp+1
......@@ -4497,6 +4497,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
44974497 case BuiltinFnIdSqrt:
44984498 case BuiltinFnIdSin:
44994499 case BuiltinFnIdCos:
4500 case BuiltinFnIdTan:
45004501 case BuiltinFnIdExp:
45014502 case BuiltinFnIdExp2:
45024503 case BuiltinFnIdLog:
src/stage1/codegen.cpp+401-324
......@@ -869,7 +869,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn
869869 name = "fma";
870870 num_args = 3;
871871 } else if (fn_id == ZigLLVMFnIdFloatOp) {
872 name = float_op_to_name(op);
872 name = float_un_op_to_name(op);
873873 num_args = 1;
874874 } else {
875875 zig_unreachable();
......@@ -1604,8 +1604,57 @@ static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *
16041604 return nullptr;
16051605}
16061606
1607static const char *get_compiler_rt_type_abbrev(ZigType *type) {
1608 uint16_t bits;
1609 if (type->id == ZigTypeIdFloat) {
1610 bits = type->data.floating.bit_count;
1611 } else if (type->id == ZigTypeIdInt) {
1612 bits = type->data.integral.bit_count;
1613 } else {
1614 zig_unreachable();
1615 }
1616 switch (bits) {
1617 case 16:
1618 return "h";
1619 case 32:
1620 return "s";
1621 case 64:
1622 return "d";
1623 case 80:
1624 return "x";
1625 case 128:
1626 return "t";
1627 default:
1628 zig_unreachable();
1629 }
1630}
16071631
1608static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_type,
1632static const char *libc_float_prefix(CodeGen *g, ZigType *float_type) {
1633 switch (float_type->data.floating.bit_count) {
1634 case 16:
1635 case 80:
1636 return "__";
1637 case 32:
1638 case 64:
1639 case 128:
1640 return "";
1641 default:
1642 zig_unreachable();
1643 }
1644}
1645
1646static const char *libc_float_suffix(CodeGen *g, ZigType *float_type) {
1647 switch (float_type->size_in_bits) {
1648 case 16: return "h"; // Non-standard
1649 case 32: return "f";
1650 case 64: return "";
1651 case 80: return "x"; // Non-standard
1652 case 128: return "q"; // Non-standard
1653 default: zig_unreachable();
1654 }
1655}
1656
1657static LLVMValueRef gen_soft_float_widen_or_shorten(CodeGen *g, ZigType *actual_type,
16091658 ZigType *wanted_type, LLVMValueRef expr_val)
16101659{
16111660 ZigType *scalar_actual_type = (actual_type->id == ZigTypeIdVector) ?
......@@ -1615,87 +1664,47 @@ static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_ty
16151664 uint64_t actual_bits = scalar_actual_type->data.floating.bit_count;
16161665 uint64_t wanted_bits = scalar_wanted_type->data.floating.bit_count;
16171666
1618
1619 LLVMTypeRef param_type;
1620 LLVMTypeRef return_type;
1621 const char *func_name;
1667 if (actual_bits == wanted_bits)
1668 return expr_val;
16221669
16231670 LLVMValueRef result;
16241671 bool castTruncatedToF16 = false;
16251672
1626 if (actual_bits == wanted_bits) {
1627 return expr_val;
1628 } else if (actual_bits == 80) {
1629 param_type = g->builtin_types.entry_f80->llvm_type;
1630 switch (wanted_bits) {
1631 case 16:
1632 // Only Arm has a native f16 type, other platforms soft-implement it
1633 // using u16 instead.
1634 if (target_is_arm(g->zig_target)) {
1635 return_type = g->builtin_types.entry_f16->llvm_type;
1636 } else {
1637 return_type = g->builtin_types.entry_u16->llvm_type;
1638 castTruncatedToF16 = true;
1639 }
1640 func_name = "__truncxfhf2";
1641 break;
1642 case 32:
1643 return_type = g->builtin_types.entry_f32->llvm_type;
1644 func_name = "__truncxfsf2";
1645 break;
1646 case 64:
1647 return_type = g->builtin_types.entry_f64->llvm_type;
1648 func_name = "__truncxfdf2";
1649 break;
1650 case 128:
1651 return_type = g->builtin_types.entry_f128->llvm_type;
1652 func_name = "__extendxftf2";
1653 break;
1654 default:
1655 zig_unreachable();
1673 char fn_name[64];
1674 if (wanted_bits < actual_bits) {
1675 sprintf(fn_name, "__trunc%sf%sf2",
1676 get_compiler_rt_type_abbrev(scalar_actual_type),
1677 get_compiler_rt_type_abbrev(scalar_wanted_type));
1678 } else {
1679 sprintf(fn_name, "__extend%sf%sf2",
1680 get_compiler_rt_type_abbrev(scalar_actual_type),
1681 get_compiler_rt_type_abbrev(scalar_wanted_type));
1682 }
1683
1684 LLVMTypeRef return_type = scalar_wanted_type->llvm_type;
1685 LLVMTypeRef param_type = scalar_actual_type->llvm_type;
1686
1687 if (!target_is_arm(g->zig_target)) {
1688 // Only Arm has a native f16 type, other platforms soft-implement it using u16 instead.
1689 if (scalar_wanted_type == g->builtin_types.entry_f16) {
1690 return_type = g->builtin_types.entry_u16->llvm_type;
1691 castTruncatedToF16 = true;
16561692 }
1657 } else if (wanted_bits == 80) {
1658 return_type = g->builtin_types.entry_f80->llvm_type;
1659 switch (actual_bits) {
1660 case 16:
1661 // Only Arm has a native f16 type, other platforms soft-implement it
1662 // using u16 instead.
1663 if (target_is_arm(g->zig_target)) {
1664 param_type = g->builtin_types.entry_f16->llvm_type;
1665 } else {
1666 param_type = g->builtin_types.entry_u16->llvm_type;
1667 expr_val = LLVMBuildBitCast(g->builder, expr_val, param_type, "");
1668 }
1669 func_name = "__extendhfxf2";
1670 break;
1671 case 32:
1672 param_type = g->builtin_types.entry_f32->llvm_type;
1673 func_name = "__extendsfxf2";
1674 break;
1675 case 64:
1676 param_type = g->builtin_types.entry_f64->llvm_type;
1677 func_name = "__extenddfxf2";
1678 break;
1679 case 128:
1680 param_type = g->builtin_types.entry_f128->llvm_type;
1681 func_name = "__trunctfxf2";
1682 break;
1683 default:
1684 zig_unreachable();
1693 if (scalar_actual_type == g->builtin_types.entry_f16) {
1694 param_type = g->builtin_types.entry_u16->llvm_type;
1695 expr_val = LLVMBuildBitCast(g->builder, expr_val, param_type, "");
16851696 }
1686 } else {
1687 zig_unreachable();
16881697 }
16891698
1690 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name);
1699 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name);
16911700 if (func_ref == nullptr) {
16921701 LLVMTypeRef fn_type = LLVMFunctionType(return_type, &param_type, 1, false);
1693 func_ref = LLVMAddFunction(g->module, func_name, fn_type);
1702 func_ref = LLVMAddFunction(g->module, fn_name, fn_type);
16941703 }
16951704
16961705 result = LLVMBuildCall(g->builder, func_ref, &expr_val, 1, "");
16971706
1698 // On non-Arm platforms we need to bitcast __truncxfhf2 result back to f16
1707 // On non-Arm platforms we need to bitcast __trunc<>fhf2 result back to f16
16991708 if (castTruncatedToF16) {
17001709 result = LLVMBuildBitCast(g->builder, result, g->builtin_types.entry_f16->llvm_type, "");
17011710 }
......@@ -1721,7 +1730,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
17211730 || scalar_wanted_type == g->builtin_types.entry_f80)
17221731 && !target_has_f80(g->zig_target))
17231732 {
1724 return gen_soft_f80_widen_or_shorten(g, actual_type, wanted_type, expr_val);
1733 return gen_soft_float_widen_or_shorten(g, actual_type, wanted_type, expr_val);
17251734 }
17261735 actual_bits = scalar_actual_type->data.floating.bit_count;
17271736 wanted_bits = scalar_wanted_type->data.floating.bit_count;
......@@ -2978,10 +2987,54 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type,
29782987 return result;
29792988}
29802989
2981static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {
2982 assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector);
2983 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);
2984 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
2990static LLVMValueRef get_soft_float_fn(CodeGen *g, const char *name, int param_count, LLVMTypeRef param_type, LLVMTypeRef return_type) {
2991 LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name);
2992 if (existing_llvm_fn != nullptr) return existing_llvm_fn;
2993 LLVMValueRef existing_llvm_alias = LLVMGetNamedGlobalAlias(g->module, name, strlen(name));
2994 if (existing_llvm_alias != nullptr) return LLVMAliasGetAliasee(existing_llvm_alias);
2995
2996 LLVMTypeRef param_types[3] = { param_type, param_type, param_type };
2997 LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, param_count, false);
2998 return LLVMAddFunction(g->module, name, fn_type);
2999}
3000
3001static LLVMValueRef gen_soft_float_un_op(CodeGen *g, LLVMValueRef op, ZigType *operand_type, BuiltinFnId op_id) {
3002 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
3003 ZigType *scalar_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type;
3004
3005 char fn_name[64];
3006 sprintf(fn_name, "%s%s%s", libc_float_prefix(g, scalar_type),
3007 float_un_op_to_name(op_id), libc_float_suffix(g, scalar_type));
3008 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, 1, scalar_type->llvm_type, scalar_type->llvm_type);
3009
3010 LLVMValueRef result;
3011 if (vector_len == 0) {
3012 return LLVMBuildCall(g->builder, func_ref, &op, 1, "");
3013 } else {
3014 result = build_alloca(g, operand_type, "", 0);
3015 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
3016 for (uint32_t i = 0; i < vector_len; i++) {
3017 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3018 LLVMValueRef param = LLVMBuildExtractElement(g->builder, op, index_value, "");
3019 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, &param, 1, "");
3020 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3021 call_result, index_value, "");
3022 }
3023 return LLVMBuildLoad(g->builder, result, "");
3024 }
3025}
3026
3027static LLVMValueRef gen_float_un_op(CodeGen *g, LLVMValueRef operand, ZigType *operand_type, BuiltinFnId op) {
3028 assert(operand_type->id == ZigTypeIdFloat || operand_type->id == ZigTypeIdVector);
3029 ZigType *elem_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type;
3030 if ((elem_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3031 (elem_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target)) ||
3032 op == BuiltinFnIdTan)
3033 {
3034 return gen_soft_float_un_op(g, operand, operand_type, op);
3035 }
3036 LLVMValueRef float_op_fn = get_float_fn(g, operand_type, ZigLLVMFnIdFloatOp, op);
3037 return LLVMBuildCall(g->builder, float_op_fn, &operand, 1, "");
29853038}
29863039
29873040enum DivKind {
......@@ -3088,7 +3141,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
30883141 case DivKindExact:
30893142 if (want_runtime_safety) {
30903143 // Safety check: a / b == floor(a / b)
3091 LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor);
3144 LLVMValueRef floored = gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor);
30923145
30933146 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
30943147 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
......@@ -3105,9 +3158,9 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
31053158 }
31063159 return result;
31073160 case DivKindTrunc:
3108 return gen_float_op(g, result, operand_type, BuiltinFnIdTrunc);
3161 return gen_float_un_op(g, result, operand_type, BuiltinFnIdTrunc);
31093162 case DivKindFloor:
3110 return gen_float_op(g, result, operand_type, BuiltinFnIdFloor);
3163 return gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor);
31113164 }
31123165 zig_unreachable();
31133166 }
......@@ -3269,17 +3322,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type
32693322 }
32703323}
32713324
3272static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int param_count, LLVMTypeRef return_type) {
3273 LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name);
3274 if (existing_llvm_fn != nullptr) return existing_llvm_fn;
3275
3276 LLVMTypeRef float_type_ref = g->builtin_types.entry_f80->llvm_type;
3277 LLVMTypeRef param_types[2] = { float_type_ref, float_type_ref };
3278 LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, param_count, false);
3279 return LLVMAddFunction(g->module, name, fn_type);
3280}
3281
3282enum SoftF80Icmp {
3325enum Icmp {
32833326 NONE,
32843327 EQ_ZERO,
32853328 NE_ZERO,
......@@ -3289,7 +3332,7 @@ enum SoftF80Icmp {
32893332 EQ_ONE,
32903333};
32913334
3292static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) {
3335static LLVMValueRef add_icmp(CodeGen *g, LLVMValueRef val, Icmp kind) {
32933336 switch (kind) {
32943337 case NONE:
32953338 return val;
......@@ -3322,22 +3365,124 @@ static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind)
33223365 }
33233366}
33243367
3325static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
3326 Stage1AirInstBinOp *bin_op_instruction)
3327{
3328 IrBinOp op_id = bin_op_instruction->op_id;
3329 Stage1AirInst *op1 = bin_op_instruction->op1;
3330 Stage1AirInst *op2 = bin_op_instruction->op2;
3331 uint32_t vector_len = op1->value->type->id == ZigTypeIdVector ? op1->value->type->data.vector.len : 0;
3368static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) {
3369 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
33323370
3333 LLVMValueRef op1_value = ir_llvm_value(g, op1);
3334 LLVMValueRef op2_value = ir_llvm_value(g, op2);
3371 // Handle integers of non-pot bitsize by widening them.
3372 const size_t bitsize = operand_type->data.integral.bit_count;
3373 const bool is_signed = operand_type->data.integral.is_signed;
3374 if (bitsize < 32 || !is_power_of_2(bitsize)) {
3375 const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize);
3376 ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize);
3377 value_ref = gen_widen_or_shorten(g, false, operand_type, wider_type, value_ref);
3378 operand_type = wider_type;
3379 }
3380 assert(bitsize <= 128);
33353381
3336 bool div_exact_safety_check = false;
3337 LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type;
3382 const char *int_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type);
3383 const char *float_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(result_type);
3384
3385 char fn_name[64];
3386 if (is_signed) {
3387 sprintf(fn_name, "__float%si%sf", int_compiler_rt_type_abbrev, float_compiler_rt_type_abbrev);
3388 } else {
3389 sprintf(fn_name, "__floatun%si%sf", int_compiler_rt_type_abbrev, float_compiler_rt_type_abbrev);
3390 }
3391
3392 int param_count = 1;
3393 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, result_type->llvm_type);
3394
3395 LLVMValueRef result;
3396 if (vector_len == 0) {
3397 LLVMValueRef params[1] = {value_ref};
3398 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3399 } else {
3400 ZigType *alloca_ty = operand_type;
3401 result = build_alloca(g, alloca_ty, "", 0);
3402
3403 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
3404 for (uint32_t i = 0; i < vector_len; i++) {
3405 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3406 LLVMValueRef params[1] = {
3407 LLVMBuildExtractElement(g->builder, value_ref, index_value, ""),
3408 };
3409 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3410 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3411 call_result, index_value, "");
3412 }
3413
3414 result = LLVMBuildLoad(g->builder, result, "");
3415 }
3416 return result;
3417}
3418
3419static LLVMValueRef gen_soft_float_to_int_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) {
3420 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
3421
3422 // Handle integers of non-pot bitsize by truncating a sufficiently wide pot integer
3423 const size_t bitsize = result_type->data.integral.bit_count;
3424 const bool is_signed = result_type->data.integral.is_signed;
3425 ZigType * wider_type = result_type;
3426 if (bitsize < 32 || !is_power_of_2(bitsize)) {
3427 const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize);
3428 wider_type = get_int_type(g, is_signed, wider_bitsize);
3429 }
3430 assert(bitsize <= 128);
3431
3432 const char *float_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type);
3433 const char *int_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(wider_type);
3434
3435 char fn_name[64];
3436 if (is_signed) {
3437 sprintf(fn_name, "__fix%sf%si", float_compiler_rt_type_abbrev, int_compiler_rt_type_abbrev);
3438 } else {
3439 sprintf(fn_name, "__fixuns%sf%si", float_compiler_rt_type_abbrev, int_compiler_rt_type_abbrev);
3440 }
3441
3442 int param_count = 1;
3443 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, wider_type->llvm_type);
3444
3445 LLVMValueRef result;
3446 if (vector_len == 0) {
3447 LLVMValueRef params[1] = {value_ref};
3448 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3449 } else {
3450 ZigType *alloca_ty = operand_type;
3451 result = build_alloca(g, alloca_ty, "", 0);
3452
3453 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
3454 for (uint32_t i = 0; i < vector_len; i++) {
3455 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3456 LLVMValueRef params[1] = {
3457 LLVMBuildExtractElement(g->builder, value_ref, index_value, ""),
3458 };
3459 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3460 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3461 call_result, index_value, "");
3462 }
3463
3464 result = LLVMBuildLoad(g->builder, result, "");
3465 }
3466
3467 // Handle integers of non-pot bitsize by shortening them on the output
3468 if (result_type != wider_type) {
3469 return gen_widen_or_shorten(g, false, wider_type, result_type, result);
3470 }
3471 return result;
3472}
3473
3474static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LLVMValueRef op2_value, ZigType *operand_type, IrBinOp op_id) {
3475 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
3476
3477 LLVMTypeRef return_type = operand_type->llvm_type;
33383478 int param_count = 2;
3339 const char *func_name;
3340 SoftF80Icmp res_icmp = NONE;
3479
3480 const char *compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type);
3481 const char *math_float_prefix = libc_float_prefix(g, operand_type);
3482 const char *math_float_suffix = libc_float_suffix(g, operand_type);
3483
3484 char fn_name[64];
3485 Icmp res_icmp = NONE;
33413486 switch (op_id) {
33423487 case IrBinOpInvalid:
33433488 case IrBinOpArrayCat:
......@@ -3362,152 +3507,129 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
33623507 zig_unreachable();
33633508 case IrBinOpCmpEq:
33643509 return_type = g->builtin_types.entry_i32->llvm_type;
3365 func_name = "__eqxf2";
3510 sprintf(fn_name, "__eq%sf2", compiler_rt_type_abbrev);
33663511 res_icmp = EQ_ZERO;
33673512 break;
33683513 case IrBinOpCmpNotEq:
33693514 return_type = g->builtin_types.entry_i32->llvm_type;
3370 func_name = "__nexf2";
3515 sprintf(fn_name, "__ne%sf2", compiler_rt_type_abbrev);
33713516 res_icmp = NE_ZERO;
33723517 break;
33733518 case IrBinOpCmpLessOrEq:
33743519 return_type = g->builtin_types.entry_i32->llvm_type;
3375 func_name = "__lexf2";
3520 sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev);
33763521 res_icmp = LE_ZERO;
33773522 break;
33783523 case IrBinOpCmpLessThan:
33793524 return_type = g->builtin_types.entry_i32->llvm_type;
3380 func_name = "__lexf2";
3525 sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev);
33813526 res_icmp = EQ_NEG;
33823527 break;
33833528 case IrBinOpCmpGreaterOrEq:
33843529 return_type = g->builtin_types.entry_i32->llvm_type;
3385 func_name = "__gexf2";
3530 sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev);
33863531 res_icmp = GE_ZERO;
33873532 break;
33883533 case IrBinOpCmpGreaterThan:
33893534 return_type = g->builtin_types.entry_i32->llvm_type;
3390 func_name = "__gexf2";
3535 sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev);
33913536 res_icmp = EQ_ONE;
33923537 break;
33933538 case IrBinOpMaximum:
3394 func_name = "__fmaxx";
3539 sprintf(fn_name, "%sfmax%s", math_float_prefix, math_float_suffix);
33953540 break;
33963541 case IrBinOpMinimum:
3397 func_name = "__fminx";
3542 sprintf(fn_name, "%sfmin%s", math_float_prefix, math_float_suffix);
33983543 break;
33993544 case IrBinOpMult:
3400 func_name = "__mulxf3";
3545 sprintf(fn_name, "__mul%sf3", compiler_rt_type_abbrev);
34013546 break;
34023547 case IrBinOpAdd:
3403 func_name = "__addxf3";
3548 sprintf(fn_name, "__add%sf3", compiler_rt_type_abbrev);
34043549 break;
34053550 case IrBinOpSub:
3406 func_name = "__subxf3";
3551 sprintf(fn_name, "__sub%sf3", compiler_rt_type_abbrev);
34073552 break;
34083553 case IrBinOpDivUnspecified:
3409 func_name = "__divxf3";
3410 break;
34113554 case IrBinOpDivExact:
3412 func_name = "__divxf3";
3413 div_exact_safety_check = bin_op_instruction->safety_check_on &&
3414 ir_want_runtime_safety(g, &bin_op_instruction->base);
3415 break;
34163555 case IrBinOpDivTrunc:
3417 param_count = 1;
3418 func_name = "__truncx";
3419 break;
34203556 case IrBinOpDivFloor:
3421 param_count = 1;
3422 func_name = "__floorx";
3557 sprintf(fn_name, "__div%sf3", compiler_rt_type_abbrev);
34233558 break;
34243559 case IrBinOpRemRem:
3425 param_count = 1;
3426 func_name = "__remx";
3427 break;
34283560 case IrBinOpRemMod:
3429 param_count = 1;
3430 func_name = "__modx";
3561 sprintf(fn_name, "%sfmod%s", math_float_prefix, math_float_suffix);
34313562 break;
34323563 default:
34333564 zig_unreachable();
34343565 }
34353566
3436 LLVMValueRef func_ref = get_soft_f80_bin_op_func(g, func_name, param_count, return_type);
3567 LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, return_type);
34373568
34383569 LLVMValueRef result;
34393570 if (vector_len == 0) {
34403571 LLVMValueRef params[2] = {op1_value, op2_value};
34413572 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3442 result = add_f80_icmp(g, result, res_icmp);
3573 result = add_icmp(g, result, res_icmp);
34433574 } else {
3444 ZigType *alloca_ty = op1->value->type;
3575 ZigType *alloca_ty = operand_type;
34453576 if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool);
34463577 result = build_alloca(g, alloca_ty, "", 0);
3447 }
3448
3449 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
3450 for (uint32_t i = 0; i < vector_len; i++) {
3451 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3452 LLVMValueRef params[2] = {
3453 LLVMBuildExtractElement(g->builder, op1_value, index_value, ""),
3454 LLVMBuildExtractElement(g->builder, op2_value, index_value, ""),
3455 };
3456 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3457 call_result = add_f80_icmp(g, call_result, res_icmp);
3458 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3459 call_result, index_value, "");
3460 }
3461
3462 if (div_exact_safety_check) {
3463 // Safety check: a / b == floor(a / b)
3464 LLVMValueRef floor_func = get_soft_f80_bin_op_func(g, "__floorx", 1, return_type);
3465 LLVMValueRef eq_func = get_soft_f80_bin_op_func(g, "__eqxf2", 2, g->builtin_types.entry_i32->llvm_type);
3466
3467 LLVMValueRef ok_bit;
3468 if (vector_len == 0) {
3469 LLVMValueRef floored = LLVMBuildCall(g->builder, floor_func, &result, 1, "");
3470
3471 LLVMValueRef params[2] = {result, floored};
3472 ok_bit = LLVMBuildCall(g->builder, eq_func, params, 2, "");
3473 } else {
3474 ZigType *bool_vec_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool);
3475 ok_bit = build_alloca(g, bool_vec_ty, "", 0);
3476 }
34773578
3579 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
34783580 for (uint32_t i = 0; i < vector_len; i++) {
34793581 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3480 LLVMValueRef div_res = LLVMBuildExtractElement(g->builder,
3481 LLVMBuildLoad(g->builder, result, ""), index_value, "");
3482
34833582 LLVMValueRef params[2] = {
3484 div_res,
3485 LLVMBuildCall(g->builder, floor_func, &div_res, 1, ""),
3583 LLVMBuildExtractElement(g->builder, op1_value, index_value, ""),
3584 LLVMBuildExtractElement(g->builder, op2_value, index_value, ""),
34863585 };
3487 LLVMValueRef cmp_res = LLVMBuildCall(g->builder, eq_func, params, 2, "");
3488 cmp_res = LLVMBuildTrunc(g->builder, cmp_res, g->builtin_types.entry_bool->llvm_type, "");
3489 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, ok_bit, ""),
3490 cmp_res, index_value, "");
3586 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3587 call_result = add_icmp(g, call_result, res_icmp);
3588 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3589 call_result, index_value, "");
34913590 }
34923591
3493 if (vector_len != 0) {
3494 ok_bit = ZigLLVMBuildAndReduce(g->builder, LLVMBuildLoad(g->builder, ok_bit, ""));
3495 }
3496 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
3497 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
3592 result = LLVMBuildLoad(g->builder, result, "");
3593 }
34983594
3499 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
3595 // Some operations are implemented as compound ops and require us to perform some
3596 // more operations before we obtain the final result
3597 switch (op_id) {
3598 case IrBinOpDivTrunc:
3599 return gen_float_un_op(g, result, operand_type, BuiltinFnIdTrunc);
3600 case IrBinOpDivFloor:
3601 return gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor);
3602 case IrBinOpRemMod:
3603 {
3604 LLVMValueRef b = gen_soft_float_bin_op(g, result, op2_value, operand_type, IrBinOpAdd);
3605 LLVMValueRef wrapped_result = gen_soft_float_bin_op(g, b, op2_value, operand_type, IrBinOpRemRem);
3606 LLVMValueRef zero = LLVMConstNull(operand_type->llvm_type);
3607 LLVMValueRef ltz = gen_soft_float_bin_op(g, op1_value, zero, operand_type, IrBinOpCmpLessThan);
35003608
3501 LLVMPositionBuilderAtEnd(g->builder, fail_block);
3502 gen_safety_crash(g, PanicMsgIdExactDivisionRemainder);
3609 return LLVMBuildSelect(g->builder, ltz, wrapped_result, result, "");
3610 }
3611 case IrBinOpDivExact:
3612 {
3613 LLVMValueRef floored = gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor);
3614 LLVMValueRef ok_bit = gen_soft_float_bin_op(g, result, floored, operand_type, IrBinOpCmpEq);
3615 if (vector_len != 0) {
3616 ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
3617 }
35033618
3504 LLVMPositionBuilderAtEnd(g->builder, ok_block);
3505 }
3619 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
3620 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
3621 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
35063622
3507 if (vector_len != 0) {
3508 result = LLVMBuildLoad(g->builder, result, "");
3623 LLVMPositionBuilderAtEnd(g->builder, fail_block);
3624 gen_safety_crash(g, PanicMsgIdExactDivisionRemainder);
3625
3626 LLVMPositionBuilderAtEnd(g->builder, ok_block);
3627 }
3628 return result;
3629 default:
3630 return result;
35093631 }
3510 return result;
3632 zig_unreachable();
35113633}
35123634
35133635static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
......@@ -3519,8 +3641,13 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
35193641
35203642 ZigType *operand_type = op1->value->type;
35213643 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
3522 if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) {
3523 return ir_render_soft_f80_bin_op(g, executable, bin_op_instruction);
3644 if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3645 (scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) {
3646 // LLVM incorrectly lowers the soft float calls for f128 as if they operated on `long double`.
3647 // On some targets this will be incorrect, so we manually lower the call ourselves.
3648 LLVMValueRef op1_value = ir_llvm_value(g, op1);
3649 LLVMValueRef op2_value = ir_llvm_value(g, op2);
3650 return gen_soft_float_bin_op(g, op1_value, op2_value, operand_type, op_id);
35243651 }
35253652
35263653
......@@ -3828,10 +3955,17 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable,
38283955 }
38293956 case CastOpIntToFloat:
38303957 assert(actual_type->id == ZigTypeIdInt);
3831 if (actual_type->data.integral.is_signed) {
3832 return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3833 } else {
3834 return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3958 {
3959 if ((wanted_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3960 (wanted_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) {
3961 return gen_soft_int_to_float_op(g, expr_val, actual_type, wanted_type);
3962 } else {
3963 if (actual_type->data.integral.is_signed) {
3964 return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3965 } else {
3966 return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3967 }
3968 }
38353969 }
38363970 case CastOpFloatToInt: {
38373971 assert(wanted_type->id == ZigTypeIdInt);
......@@ -3840,18 +3974,28 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable,
38403974 bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base);
38413975
38423976 LLVMValueRef result;
3843 if (wanted_type->data.integral.is_signed) {
3844 result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3977 if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3978 (actual_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) {
3979 result = gen_soft_float_to_int_op(g, expr_val, actual_type, wanted_type);
38453980 } else {
3846 result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3981 if (wanted_type->data.integral.is_signed) {
3982 result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3983 } else {
3984 result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), "");
3985 }
38473986 }
38483987
38493988 if (want_safety) {
38503989 LLVMValueRef back_to_float;
3851 if (wanted_type->data.integral.is_signed) {
3852 back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), "");
3990 if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3991 (actual_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) {
3992 back_to_float = gen_soft_int_to_float_op(g, result, wanted_type, actual_type);
38533993 } else {
3854 back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), "");
3994 if (wanted_type->data.integral.is_signed) {
3995 back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), "");
3996 } else {
3997 back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), "");
3998 }
38553999 }
38564000 LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, "");
38574001 LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f);
......@@ -4151,42 +4295,46 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable,
41514295 return LLVMBuildNot(g->builder, operand, "");
41524296}
41534297
4154static LLVMValueRef ir_gen_soft_f80_neg(CodeGen *g, ZigType *op_type, LLVMValueRef operand) {
4155 uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0;
4298static LLVMValueRef gen_soft_float_neg(CodeGen *g, ZigType *operand_type, LLVMValueRef operand) {
4299 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
4300 uint16_t num_bits = operand_type->data.floating.bit_count;
41564301
4157 LLVMTypeRef llvm_i80 = LLVMIntType(80);
4158 LLVMValueRef sign_mask = LLVMConstInt(llvm_i80, 1, false);
4159 sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(llvm_i80, 79, false));
4302 ZigType *iX_type = get_int_type(g, true, num_bits);
4303 LLVMValueRef sign_mask = LLVMConstInt(iX_type->llvm_type, 1, false);
4304 sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(iX_type->llvm_type, num_bits - 1, false));
41604305
4161 LLVMValueRef result;
41624306 if (vector_len == 0) {
4163 result = LLVMBuildXor(g->builder, operand, sign_mask, "");
4307 LLVMValueRef bitcasted_operand = LLVMBuildBitCast(g->builder, operand, iX_type->llvm_type, "");
4308 LLVMValueRef result = LLVMBuildXor(g->builder, bitcasted_operand, sign_mask, "");
4309
4310 return LLVMBuildBitCast(g->builder, result, operand_type->llvm_type, "");
41644311 } else {
4165 result = build_alloca(g, op_type, "", 0);
4166 }
4312 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
4313 ZigType *iX_vector_type = get_vector_type(g, vector_len, iX_type);
41674314
4168 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
4169 for (uint32_t i = 0; i < vector_len; i++) {
4170 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
4171 LLVMValueRef xor_operand = LLVMBuildExtractElement(g->builder, operand, index_value, "");
4172 LLVMValueRef xor_result = LLVMBuildXor(g->builder, xor_operand, sign_mask, "");
4173 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
4174 xor_result, index_value, "");
4175 }
4176 if (vector_len != 0) {
4177 result = LLVMBuildLoad(g->builder, result, "");
4315 LLVMValueRef result = build_alloca(g, iX_vector_type, "", 0);
4316 LLVMValueRef bitcasted_operand = LLVMBuildBitCast(g->builder, operand, iX_vector_type->llvm_type, "");
4317 for (uint32_t i = 0; i < vector_len; i++) {
4318 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
4319 LLVMValueRef elem = LLVMBuildExtractElement(g->builder, bitcasted_operand, index_value, "");
4320 LLVMValueRef result_elem = LLVMBuildXor(g->builder, elem, sign_mask, "");
4321 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
4322 result_elem, index_value, "");
4323 }
4324 return LLVMBuildBitCast(g->builder, LLVMBuildLoad(g->builder, result, ""), operand_type->llvm_type, "");
41784325 }
4179 return result;
41804326}
41814327
4182static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) {
4328static LLVMValueRef gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) {
41834329 LLVMValueRef llvm_operand = ir_llvm_value(g, operand);
41844330 ZigType *operand_type = operand->value->type;
41854331 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
41864332 operand_type->data.vector.elem_type : operand_type;
41874333
4188 if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target))
4189 return ir_gen_soft_f80_neg(g, operand_type, llvm_operand);
4334 if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
4335 (scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) {
4336 return gen_soft_float_neg(g, operand_type, llvm_operand);
4337 }
41904338
41914339 if (scalar_type->id == ZigTypeIdFloat) {
41924340 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst));
......@@ -4210,7 +4358,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirIn
42104358static LLVMValueRef ir_render_negation(CodeGen *g, Stage1Air *executable,
42114359 Stage1AirInstNegation *inst)
42124360{
4213 return ir_gen_negation(g, &inst->base, inst->operand, inst->wrapping);
4361 return gen_negation(g, &inst->base, inst->operand, inst->wrapping);
42144362}
42154363
42164364static LLVMValueRef ir_render_bool_not(CodeGen *g, Stage1Air *executable, Stage1AirInstBoolNot *instruction) {
......@@ -7024,110 +7172,34 @@ static LLVMValueRef ir_render_atomic_store(CodeGen *g, Stage1Air *executable,
70247172 return nullptr;
70257173}
70267174
7027static LLVMValueRef ir_render_soft_f80_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) {
7028 ZigType *op_type = instruction->operand->value->type;
7029 uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0;
7030
7031 const char *func_name;
7032 switch (instruction->fn_id) {
7033 case BuiltinFnIdSqrt:
7034 func_name = "__sqrtx";
7035 break;
7036 case BuiltinFnIdSin:
7037 func_name = "__sinx";
7038 break;
7039 case BuiltinFnIdCos:
7040 func_name = "__cosx";
7041 break;
7042 case BuiltinFnIdExp:
7043 func_name = "__expx";
7044 break;
7045 case BuiltinFnIdExp2:
7046 func_name = "__exp2x";
7047 break;
7048 case BuiltinFnIdLog:
7049 func_name = "__logx";
7050 break;
7051 case BuiltinFnIdLog2:
7052 func_name = "__log2x";
7053 break;
7054 case BuiltinFnIdLog10:
7055 func_name = "__log10x";
7056 break;
7057 case BuiltinFnIdFabs:
7058 func_name = "__fabsx";
7059 break;
7060 case BuiltinFnIdFloor:
7061 func_name = "__floorx";
7062 break;
7063 case BuiltinFnIdCeil:
7064 func_name = "__ceilx";
7065 break;
7066 case BuiltinFnIdTrunc:
7067 func_name = "__truncx";
7068 break;
7069 case BuiltinFnIdNearbyInt:
7070 func_name = "__nearbyintx";
7071 break;
7072 case BuiltinFnIdRound:
7073 func_name = "__roundx";
7074 break;
7075 default:
7076 zig_unreachable();
7077 }
7078
7079
7080 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name);
7081 if (func_ref == nullptr) {
7082 LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type;
7083 LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, &f80_ref, 1, false);
7084 func_ref = LLVMAddFunction(g->module, func_name, fn_type);
7085 }
7086
7087 LLVMValueRef operand = ir_llvm_value(g, instruction->operand);
7088 LLVMValueRef result;
7089 if (vector_len == 0) {
7090 result = LLVMBuildCall(g->builder, func_ref, &operand, 1, "");
7091 } else {
7092 result = build_alloca(g, instruction->operand->value->type, "", 0);
7093 }
7094
7095 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
7096 for (uint32_t i = 0; i < vector_len; i++) {
7097 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
7098 LLVMValueRef param = LLVMBuildExtractElement(g->builder, operand, index_value, "");
7099 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, &param, 1, "");
7100 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
7101 call_result, index_value, "");
7102 }
7103 if (vector_len != 0) {
7104 result = LLVMBuildLoad(g->builder, result, "");
7105 }
7106 return result;
7107}
7108
71097175static LLVMValueRef ir_render_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) {
7110 ZigType *op_type = instruction->operand->value->type;
7111 op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type;
7112 if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) {
7113 return ir_render_soft_f80_float_op(g, executable, instruction);
7114 }
71157176 LLVMValueRef operand = ir_llvm_value(g, instruction->operand);
7116 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id);
7117 return LLVMBuildCall(g->builder, fn_val, &operand, 1, "");
7177 ZigType *operand_type = instruction->operand->value->type;
7178 return gen_float_un_op(g, operand, operand_type, instruction->fn_id);
71187179}
71197180
7120static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) {
7121 ZigType *op_type = instruction->op1->value->type;
7122 uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0;
7181static LLVMValueRef ir_render_soft_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction, ZigType *float_type) {
7182 ZigType *operand_type = instruction->op1->value->type;
7183 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
7184
7185 const char *fn_name;
7186 if (float_type == g->builtin_types.entry_f32)
7187 fn_name = "fmaf";
7188 else if (float_type == g->builtin_types.entry_f64)
7189 fn_name = "fma";
7190 else if (float_type == g->builtin_types.entry_f80)
7191 fn_name = "__fmax";
7192 else if (float_type == g->builtin_types.entry_f128)
7193 fn_name = "fmaq";
7194 else
7195 zig_unreachable();
71237196
7124 const char *func_name = "__fmax";
7125 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name);
7197 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name);
71267198 if (func_ref == nullptr) {
7127 LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type;
7128 LLVMTypeRef params[3] = { f80_ref, f80_ref, f80_ref };
7129 LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, params, 3, false);
7130 func_ref = LLVMAddFunction(g->module, func_name, fn_type);
7199 LLVMTypeRef float_type_ref = float_type->llvm_type;
7200 LLVMTypeRef params[3] = { float_type_ref, float_type_ref, float_type_ref };
7201 LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, params, 3, false);
7202 func_ref = LLVMAddFunction(g->module, fn_name, fn_type);
71317203 }
71327204
71337205 LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);
......@@ -7161,10 +7233,11 @@ static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable
71617233}
71627234
71637235static LLVMValueRef ir_render_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) {
7164 ZigType *op_type = instruction->op1->value->type;
7165 op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type;
7166 if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) {
7167 return ir_render_soft_f80_mul_add(g, executable, instruction);
7236 ZigType *operand_type = instruction->op1->value->type;
7237 operand_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type;
7238 if ((operand_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
7239 (operand_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) {
7240 return ir_render_soft_mul_add(g, executable, instruction, operand_type);
71687241 }
71697242 LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);
71707243 LLVMValueRef op2 = ir_llvm_value(g, instruction->op2);
......@@ -9513,10 +9586,13 @@ static void define_builtin_types(CodeGen *g) {
95139586 switch (g->zig_target->arch) {
95149587 case ZigLLVM_x86:
95159588 case ZigLLVM_x86_64:
9516 if (g->zig_target->abi != ZigLLVM_MSVC)
9589 if (g->zig_target->abi != ZigLLVM_MSVC) {
95179590 add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble);
9518 else
9591 g->builtin_types.entry_c_longdouble->abi_size = g->builtin_types.entry_f80->abi_size;
9592 g->builtin_types.entry_c_longdouble->abi_align = g->builtin_types.entry_f80->abi_align;
9593 } else {
95199594 add_fp_entry(g, "c_longdouble", 64, LLVMDoubleType(), &g->builtin_types.entry_c_longdouble);
9595 }
95209596 break;
95219597 case ZigLLVM_arm:
95229598 case ZigLLVM_armeb:
......@@ -9750,6 +9826,7 @@ static void define_builtin_fns(CodeGen *g) {
97509826 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 1);
97519827 create_builtin_fn(g, BuiltinFnIdSin, "sin", 1);
97529828 create_builtin_fn(g, BuiltinFnIdCos, "cos", 1);
9829 create_builtin_fn(g, BuiltinFnIdTan, "tan", 1);
97539830 create_builtin_fn(g, BuiltinFnIdExp, "exp", 1);
97549831 create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 1);
97559832 create_builtin_fn(g, BuiltinFnIdLog, "log", 1);
src/stage1/ir.cpp+93-13
......@@ -24132,6 +24132,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
2413224132 case BuiltinFnIdCos:
2413324133 out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16)));
2413424134 break;
24135 case BuiltinFnIdTan:
24136 out_val->data.x_f16 = zig_double_to_f16(tan(zig_f16_to_double(op->data.x_f16)));
24137 break;
2413524138 case BuiltinFnIdExp:
2413624139 out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16)));
2413724140 break;
......@@ -24181,6 +24184,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
2418124184 case BuiltinFnIdCos:
2418224185 out_val->data.x_f32 = cosf(op->data.x_f32);
2418324186 break;
24187 case BuiltinFnIdTan:
24188 out_val->data.x_f32 = tanf(op->data.x_f32);
24189 break;
2418424190 case BuiltinFnIdExp:
2418524191 out_val->data.x_f32 = expf(op->data.x_f32);
2418624192 break;
......@@ -24230,6 +24236,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
2423024236 case BuiltinFnIdCos:
2423124237 out_val->data.x_f64 = cos(op->data.x_f64);
2423224238 break;
24239 case BuiltinFnIdTan:
24240 out_val->data.x_f64 = tan(op->data.x_f64);
24241 break;
2423324242 case BuiltinFnIdExp:
2423424243 out_val->data.x_f64 = exp(op->data.x_f64);
2423524244 break;
......@@ -24293,6 +24302,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
2429324302 case BuiltinFnIdNearbyInt:
2429424303 case BuiltinFnIdSin:
2429524304 case BuiltinFnIdCos:
24305 case BuiltinFnIdTan:
2429624306 case BuiltinFnIdExp:
2429724307 case BuiltinFnIdExp2:
2429824308 case BuiltinFnIdLog:
......@@ -24300,7 +24310,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
2430024310 case BuiltinFnIdLog2:
2430124311 return ir_add_error_node(ira, source_node,
2430224312 buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",
24303 float_op_to_name(fop), buf_ptr(&float_type->name)));
24313 float_un_op_to_name(fop), buf_ptr(&float_type->name)));
2430424314 default:
2430524315 zig_unreachable();
2430624316 }
......@@ -24327,24 +24337,94 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
2432724337 break;
2432824338 case BuiltinFnIdCeil:
2432924339 f128M_roundToInt(in, softfloat_round_max, false, out);
24330 break;
24340 break;
2433124341 case BuiltinFnIdTrunc:
2433224342 f128M_trunc(in, out);
2433324343 break;
2433424344 case BuiltinFnIdRound:
2433524345 f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);
2433624346 break;
24337 case BuiltinFnIdNearbyInt:
24338 case BuiltinFnIdSin:
24339 case BuiltinFnIdCos:
24340 case BuiltinFnIdExp:
24341 case BuiltinFnIdExp2:
24342 case BuiltinFnIdLog:
24343 case BuiltinFnIdLog10:
24344 case BuiltinFnIdLog2:
24345 return ir_add_error_node(ira, source_node,
24346 buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",
24347 float_op_to_name(fop), buf_ptr(&float_type->name)));
24347 case BuiltinFnIdNearbyInt: {
24348 float64_t f64_value = f128M_to_f64(in);
24349 double double_value;
24350 memcpy(&double_value, &f64_value, sizeof(double));
24351 double_value = nearbyint(double_value);
24352 memcpy(&f64_value, &double_value, sizeof(double));
24353 f64_to_f128M(f64_value, out);
24354 break;
24355 }
24356 case BuiltinFnIdSin: {
24357 float64_t f64_value = f128M_to_f64(in);
24358 double double_value;
24359 memcpy(&double_value, &f64_value, sizeof(double));
24360 double_value = sin(double_value);
24361 memcpy(&f64_value, &double_value, sizeof(double));
24362 f64_to_f128M(f64_value, out);
24363 break;
24364 }
24365 case BuiltinFnIdCos: {
24366 float64_t f64_value = f128M_to_f64(in);
24367 double double_value;
24368 memcpy(&double_value, &f64_value, sizeof(double));
24369 double_value = cos(double_value);
24370 memcpy(&f64_value, &double_value, sizeof(double));
24371 f64_to_f128M(f64_value, out);
24372 break;
24373 }
24374 case BuiltinFnIdTan: {
24375 float64_t f64_value = f128M_to_f64(in);
24376 double double_value;
24377 memcpy(&double_value, &f64_value, sizeof(double));
24378 double_value = tan(double_value);
24379 memcpy(&f64_value, &double_value, sizeof(double));
24380 f64_to_f128M(f64_value, out);
24381 break;
24382 }
24383 case BuiltinFnIdExp: {
24384 float64_t f64_value = f128M_to_f64(in);
24385 double double_value;
24386 memcpy(&double_value, &f64_value, sizeof(double));
24387 double_value = exp(double_value);
24388 memcpy(&f64_value, &double_value, sizeof(double));
24389 f64_to_f128M(f64_value, out);
24390 break;
24391 }
24392 case BuiltinFnIdExp2: {
24393 float64_t f64_value = f128M_to_f64(in);
24394 double double_value;
24395 memcpy(&double_value, &f64_value, sizeof(double));
24396 double_value = exp2(double_value);
24397 memcpy(&f64_value, &double_value, sizeof(double));
24398 f64_to_f128M(f64_value, out);
24399 break;
24400 }
24401 case BuiltinFnIdLog: {
24402 float64_t f64_value = f128M_to_f64(in);
24403 double double_value;
24404 memcpy(&double_value, &f64_value, sizeof(double));
24405 double_value = log(double_value);
24406 memcpy(&f64_value, &double_value, sizeof(double));
24407 f64_to_f128M(f64_value, out);
24408 break;
24409 }
24410 case BuiltinFnIdLog10: {
24411 float64_t f64_value = f128M_to_f64(in);
24412 double double_value;
24413 memcpy(&double_value, &f64_value, sizeof(double));
24414 double_value = log10(double_value);
24415 memcpy(&f64_value, &double_value, sizeof(double));
24416 f64_to_f128M(f64_value, out);
24417 break;
24418 }
24419 case BuiltinFnIdLog2: {
24420 float64_t f64_value = f128M_to_f64(in);
24421 double double_value;
24422 memcpy(&double_value, &f64_value, sizeof(double));
24423 double_value = log2(double_value);
24424 memcpy(&f64_value, &double_value, sizeof(double));
24425 f64_to_f128M(f64_value, out);
24426 break;
24427 }
2434824428 default:
2434924429 zig_unreachable();
2435024430 }
src/stage1/ir_print.cpp+2-2
......@@ -2558,13 +2558,13 @@ static void ir_print_add_implicit_return_type(IrPrintSrc *irp, Stage1ZirInstAddI
25582558}
25592559
25602560static void ir_print_float_op(IrPrintSrc *irp, Stage1ZirInstFloatOp *instruction) {
2561 fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id));
2561 fprintf(irp->f, "@%s(", float_un_op_to_name(instruction->fn_id));
25622562 ir_print_other_inst_src(irp, instruction->operand);
25632563 fprintf(irp->f, ")");
25642564}
25652565
25662566static void ir_print_float_op(IrPrintGen *irp, Stage1AirInstFloatOp *instruction) {
2567 fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id));
2567 fprintf(irp->f, "@%s(", float_un_op_to_name(instruction->fn_id));
25682568 ir_print_other_inst_gen(irp, instruction->operand);
25692569 fprintf(irp->f, ")");
25702570}
src/translate_c.zig+1-1
......@@ -3998,7 +3998,7 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingL
39983998 var dbl = expr.getValueAsApproximateDouble();
39993999 const is_negative = dbl < 0;
40004000 if (is_negative) dbl = -dbl;
4001 const str = if (dbl == std.math.floor(dbl))
4001 const str = if (dbl == @floor(dbl))
40024002 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
40034003 else
40044004 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
src/value.zig+50-99
......@@ -1155,6 +1155,7 @@ pub const Value = extern union {
11551155 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),
11561156 32 => return floatWriteToMemory(f32, val.toFloat(f32), target, buffer),
11571157 64 => return floatWriteToMemory(f64, val.toFloat(f64), target, buffer),
1158 80 => return floatWriteToMemory(f80, val.toFloat(f80), target, buffer),
11581159 128 => return floatWriteToMemory(f128, val.toFloat(f128), target, buffer),
11591160 else => unreachable,
11601161 },
......@@ -1379,25 +1380,21 @@ pub const Value = extern union {
13791380 }
13801381
13811382 fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {
1383 const endian = target.cpu.arch.endian();
13821384 if (F == f80) {
1383 switch (target.cpu.arch) {
1384 .i386, .x86_64 => {
1385 const repr = std.math.break_f80(f);
1386 std.mem.writeIntLittle(u64, buffer[0..8], repr.fraction);
1387 std.mem.writeIntLittle(u16, buffer[8..10], repr.exp);
1388 // TODO set the rest of the bytes to undefined. should we use 0xaa
1389 // or is there a different way?
1390 return;
1391 },
1392 else => {},
1393 }
1385 const repr = std.math.break_f80(f);
1386 std.mem.writeInt(u64, buffer[0..8], repr.fraction, endian);
1387 std.mem.writeInt(u16, buffer[8..10], repr.exp, endian);
1388 // TODO set the rest of the bytes to undefined. should we use 0xaa
1389 // or is there a different way?
1390 return;
13941391 }
13951392 const Int = @Type(.{ .Int = .{
13961393 .signedness = .unsigned,
13971394 .bits = @typeInfo(F).Float.bits,
13981395 } });
13991396 const int = @bitCast(Int, f);
1400 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], int, target.cpu.arch.endian());
1397 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], int, endian);
14011398 }
14021399
14031400 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
......@@ -2869,9 +2866,7 @@ pub const Value = extern union {
28692866 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)),
28702867 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)),
28712868 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)),
2872 // We can't lower this properly on non-x86 llvm backends yet
2873 //80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)),
2874 80 => @panic("TODO f80 intToFloat"),
2869 80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)),
28752870 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)),
28762871 else => unreachable,
28772872 }
......@@ -2908,9 +2903,9 @@ pub const Value = extern union {
29082903 }
29092904
29102905 const isNegative = std.math.signbit(value);
2911 value = std.math.fabs(value);
2906 value = @fabs(value);
29122907
2913 const floored = std.math.floor(value);
2908 const floored = @floor(value);
29142909
29152910 var rational = try std.math.big.Rational.init(arena);
29162911 defer rational.deinit();
......@@ -2941,7 +2936,7 @@ pub const Value = extern union {
29412936 return 1;
29422937 }
29432938
2944 const w_value = std.math.fabs(scalar);
2939 const w_value = @fabs(scalar);
29452940 return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1;
29462941 }
29472942
......@@ -3737,9 +3732,6 @@ pub const Value = extern union {
37373732 return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val));
37383733 },
37393734 80 => {
3740 if (true) {
3741 @panic("TODO implement compiler_rt __remx");
3742 }
37433735 const lhs_val = lhs.toFloat(f80);
37443736 const rhs_val = rhs.toFloat(f80);
37453737 return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val));
......@@ -3782,9 +3774,6 @@ pub const Value = extern union {
37823774 return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val));
37833775 },
37843776 80 => {
3785 if (true) {
3786 @panic("TODO implement compiler_rt __modx");
3787 }
37883777 const lhs_val = lhs.toFloat(f80);
37893778 const rhs_val = rhs.toFloat(f80);
37903779 return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val));
......@@ -4198,9 +4187,6 @@ pub const Value = extern union {
41984187 return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
41994188 },
42004189 80 => {
4201 if (true) {
4202 @panic("TODO implement compiler_rt __divxf3");
4203 }
42044190 const lhs_val = lhs.toFloat(f80);
42054191 const rhs_val = rhs.toFloat(f80);
42064192 return Value.Tag.float_80.create(arena, lhs_val / rhs_val);
......@@ -4255,9 +4241,6 @@ pub const Value = extern union {
42554241 return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val));
42564242 },
42574243 80 => {
4258 if (true) {
4259 @panic("TODO implement compiler_rt __floorx");
4260 }
42614244 const lhs_val = lhs.toFloat(f80);
42624245 const rhs_val = rhs.toFloat(f80);
42634246 return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val));
......@@ -4312,9 +4295,6 @@ pub const Value = extern union {
43124295 return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val));
43134296 },
43144297 80 => {
4315 if (true) {
4316 @panic("TODO implement compiler_rt __truncx");
4317 }
43184298 const lhs_val = lhs.toFloat(f80);
43194299 const rhs_val = rhs.toFloat(f80);
43204300 return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val));
......@@ -4369,9 +4349,6 @@ pub const Value = extern union {
43694349 return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
43704350 },
43714351 80 => {
4372 if (true) {
4373 @panic("TODO implement compiler_rt __mulxf3");
4374 }
43754352 const lhs_val = lhs.toFloat(f80);
43764353 const rhs_val = rhs.toFloat(f80);
43774354 return Value.Tag.float_80.create(arena, lhs_val * rhs_val);
......@@ -4411,16 +4388,10 @@ pub const Value = extern union {
44114388 return Value.Tag.float_64.create(arena, @sqrt(f));
44124389 },
44134390 80 => {
4414 if (true) {
4415 @panic("TODO implement compiler_rt __sqrtx");
4416 }
44174391 const f = val.toFloat(f80);
44184392 return Value.Tag.float_80.create(arena, @sqrt(f));
44194393 },
44204394 128 => {
4421 if (true) {
4422 @panic("TODO implement compiler_rt sqrtq");
4423 }
44244395 const f = val.toFloat(f128);
44254396 return Value.Tag.float_128.create(arena, @sqrt(f));
44264397 },
......@@ -4454,16 +4425,10 @@ pub const Value = extern union {
44544425 return Value.Tag.float_64.create(arena, @sin(f));
44554426 },
44564427 80 => {
4457 if (true) {
4458 @panic("TODO implement compiler_rt sin for f80");
4459 }
44604428 const f = val.toFloat(f80);
44614429 return Value.Tag.float_80.create(arena, @sin(f));
44624430 },
44634431 128 => {
4464 if (true) {
4465 @panic("TODO implement compiler_rt sin for f128");
4466 }
44674432 const f = val.toFloat(f128);
44684433 return Value.Tag.float_128.create(arena, @sin(f));
44694434 },
......@@ -4497,16 +4462,10 @@ pub const Value = extern union {
44974462 return Value.Tag.float_64.create(arena, @cos(f));
44984463 },
44994464 80 => {
4500 if (true) {
4501 @panic("TODO implement compiler_rt cos for f80");
4502 }
45034465 const f = val.toFloat(f80);
45044466 return Value.Tag.float_80.create(arena, @cos(f));
45054467 },
45064468 128 => {
4507 if (true) {
4508 @panic("TODO implement compiler_rt cos for f128");
4509 }
45104469 const f = val.toFloat(f128);
45114470 return Value.Tag.float_128.create(arena, @cos(f));
45124471 },
......@@ -4514,6 +4473,43 @@ pub const Value = extern union {
45144473 }
45154474 }
45164475
4476 pub fn tan(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
4477 if (float_type.zigTypeTag() == .Vector) {
4478 const result_data = try arena.alloc(Value, float_type.vectorLen());
4479 for (result_data) |*scalar, i| {
4480 scalar.* = try tanScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target);
4481 }
4482 return Value.Tag.aggregate.create(arena, result_data);
4483 }
4484 return tanScalar(val, float_type, arena, target);
4485 }
4486
4487 pub fn tanScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
4488 switch (float_type.floatBits(target)) {
4489 16 => {
4490 const f = val.toFloat(f16);
4491 return Value.Tag.float_16.create(arena, @tan(f));
4492 },
4493 32 => {
4494 const f = val.toFloat(f32);
4495 return Value.Tag.float_32.create(arena, @tan(f));
4496 },
4497 64 => {
4498 const f = val.toFloat(f64);
4499 return Value.Tag.float_64.create(arena, @tan(f));
4500 },
4501 80 => {
4502 const f = val.toFloat(f80);
4503 return Value.Tag.float_80.create(arena, @tan(f));
4504 },
4505 128 => {
4506 const f = val.toFloat(f128);
4507 return Value.Tag.float_128.create(arena, @tan(f));
4508 },
4509 else => unreachable,
4510 }
4511 }
4512
45174513 pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
45184514 if (float_type.zigTypeTag() == .Vector) {
45194515 const result_data = try arena.alloc(Value, float_type.vectorLen());
......@@ -4540,16 +4536,10 @@ pub const Value = extern union {
45404536 return Value.Tag.float_64.create(arena, @exp(f));
45414537 },
45424538 80 => {
4543 if (true) {
4544 @panic("TODO implement compiler_rt exp for f80");
4545 }
45464539 const f = val.toFloat(f80);
45474540 return Value.Tag.float_80.create(arena, @exp(f));
45484541 },
45494542 128 => {
4550 if (true) {
4551 @panic("TODO implement compiler_rt exp for f128");
4552 }
45534543 const f = val.toFloat(f128);
45544544 return Value.Tag.float_128.create(arena, @exp(f));
45554545 },
......@@ -4583,16 +4573,10 @@ pub const Value = extern union {
45834573 return Value.Tag.float_64.create(arena, @exp2(f));
45844574 },
45854575 80 => {
4586 if (true) {
4587 @panic("TODO implement compiler_rt exp2 for f80");
4588 }
45894576 const f = val.toFloat(f80);
45904577 return Value.Tag.float_80.create(arena, @exp2(f));
45914578 },
45924579 128 => {
4593 if (true) {
4594 @panic("TODO implement compiler_rt exp2 for f128");
4595 }
45964580 const f = val.toFloat(f128);
45974581 return Value.Tag.float_128.create(arena, @exp2(f));
45984582 },
......@@ -4626,16 +4610,10 @@ pub const Value = extern union {
46264610 return Value.Tag.float_64.create(arena, @log(f));
46274611 },
46284612 80 => {
4629 if (true) {
4630 @panic("TODO implement compiler_rt log for f80");
4631 }
46324613 const f = val.toFloat(f80);
46334614 return Value.Tag.float_80.create(arena, @log(f));
46344615 },
46354616 128 => {
4636 if (true) {
4637 @panic("TODO implement compiler_rt log for f128");
4638 }
46394617 const f = val.toFloat(f128);
46404618 return Value.Tag.float_128.create(arena, @log(f));
46414619 },
......@@ -4669,16 +4647,10 @@ pub const Value = extern union {
46694647 return Value.Tag.float_64.create(arena, @log2(f));
46704648 },
46714649 80 => {
4672 if (true) {
4673 @panic("TODO implement compiler_rt log2 for f80");
4674 }
46754650 const f = val.toFloat(f80);
46764651 return Value.Tag.float_80.create(arena, @log2(f));
46774652 },
46784653 128 => {
4679 if (true) {
4680 @panic("TODO implement compiler_rt log2 for f128");
4681 }
46824654 const f = val.toFloat(f128);
46834655 return Value.Tag.float_128.create(arena, @log2(f));
46844656 },
......@@ -4712,16 +4684,10 @@ pub const Value = extern union {
47124684 return Value.Tag.float_64.create(arena, @log10(f));
47134685 },
47144686 80 => {
4715 if (true) {
4716 @panic("TODO implement compiler_rt log10 for f80");
4717 }
47184687 const f = val.toFloat(f80);
47194688 return Value.Tag.float_80.create(arena, @log10(f));
47204689 },
47214690 128 => {
4722 if (true) {
4723 @panic("TODO implement compiler_rt log10 for f128");
4724 }
47254691 const f = val.toFloat(f128);
47264692 return Value.Tag.float_128.create(arena, @log10(f));
47274693 },
......@@ -4755,9 +4721,6 @@ pub const Value = extern union {
47554721 return Value.Tag.float_64.create(arena, @fabs(f));
47564722 },
47574723 80 => {
4758 if (true) {
4759 @panic("TODO implement compiler_rt fabs for f80 (__fabsx)");
4760 }
47614724 const f = val.toFloat(f80);
47624725 return Value.Tag.float_80.create(arena, @fabs(f));
47634726 },
......@@ -4795,9 +4758,6 @@ pub const Value = extern union {
47954758 return Value.Tag.float_64.create(arena, @floor(f));
47964759 },
47974760 80 => {
4798 if (true) {
4799 @panic("TODO implement compiler_rt floor for f80 (__floorx)");
4800 }
48014761 const f = val.toFloat(f80);
48024762 return Value.Tag.float_80.create(arena, @floor(f));
48034763 },
......@@ -4835,9 +4795,6 @@ pub const Value = extern union {
48354795 return Value.Tag.float_64.create(arena, @ceil(f));
48364796 },
48374797 80 => {
4838 if (true) {
4839 @panic("TODO implement compiler_rt ceil for f80");
4840 }
48414798 const f = val.toFloat(f80);
48424799 return Value.Tag.float_80.create(arena, @ceil(f));
48434800 },
......@@ -4875,9 +4832,6 @@ pub const Value = extern union {
48754832 return Value.Tag.float_64.create(arena, @round(f));
48764833 },
48774834 80 => {
4878 if (true) {
4879 @panic("TODO implement compiler_rt round for f80");
4880 }
48814835 const f = val.toFloat(f80);
48824836 return Value.Tag.float_80.create(arena, @round(f));
48834837 },
......@@ -4915,9 +4869,6 @@ pub const Value = extern union {
49154869 return Value.Tag.float_64.create(arena, @trunc(f));
49164870 },
49174871 80 => {
4918 if (true) {
4919 @panic("TODO implement compiler_rt trunc for f80");
4920 }
49214872 const f = val.toFloat(f80);
49224873 return Value.Tag.float_80.create(arena, @trunc(f));
49234874 },
src/zig_llvm.cpp+4
......@@ -541,6 +541,10 @@ LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRe
541541 return wrap(call_inst);
542542}
543543
544LLVMValueRef LLVMBuildVectorSplat(LLVMBuilderRef B, unsigned elem_count, LLVMValueRef V, const char *Name) {
545 return wrap(unwrap(B)->CreateVectorSplat(elem_count, unwrap(V), Name));
546}
547
544548void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {
545549 assert( isa<Function>(unwrap(fn)) );
546550 Function *unwrapped_function = reinterpret_cast<Function*>(unwrap(fn));
src/zig_llvm.h+1
......@@ -149,6 +149,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMulFixSat(LLVMBuilderRef B, LLVMValueRef
149149ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMulFixSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name);
150150ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
151151ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSShlSat(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
152ZIG_EXTERN_C LLVMValueRef LLVMBuildVectorSplat(LLVMBuilderRef B, unsigned elem_count, LLVMValueRef V, const char *Name);
152153
153154
154155ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
test/behavior/bugs/920.zig+10-4
......@@ -1,5 +1,4 @@
11const std = @import("std");
2const math = std.math;
32const Random = std.rand.Random;
43
54const ZigTable = struct {
......@@ -40,10 +39,10 @@ const norm_r = 3.6541528853610088;
4039const norm_v = 0.00492867323399;
4140
4241fn norm_f(x: f64) f64 {
43 return math.exp(-x * x / 2.0);
42 return @exp(-x * x / 2.0);
4443}
4544fn norm_f_inv(y: f64) f64 {
46 return math.sqrt(-2.0 * math.ln(y));
45 return @sqrt(-2.0 * @log(y));
4746}
4847fn norm_zero_case(random: *Random, u: f64) f64 {
4948 _ = random;
......@@ -64,6 +63,13 @@ test "bug 920 fixed" {
6463 };
6564
6665 for (NormalDist1.f) |_, i| {
67 try std.testing.expectEqual(NormalDist1.f[i], NormalDist.f[i]);
66 // Here we use `expectApproxEqAbs` instead of `expectEqual` to account for the small
67 // differences in math functions of different libcs. For example, if the compiler
68 // links against glibc, but the target is musl libc, then these values might be
69 // slightly different.
70 // Arguably, this is a bug in the compiler because comptime should emulate the target,
71 // including rounding errors in libc math functions. However that behavior is not
72 // what this particular test is intended to cover.
73 try std.testing.expectApproxEqAbs(NormalDist1.f[i], NormalDist.f[i], @sqrt(std.math.floatEps(f64)));
6874 }
6975}
test/behavior/floatop.zig+5-15
......@@ -609,16 +609,11 @@ test "negation f64" {
609609}
610610
611611test "negation f80" {
612 if (builtin.zig_backend != .stage1) {
613 // This test case exercises @intToFloat f80 in the compiler implementation.
614 // https://github.com/ziglang/zig/issues/11030
615 return error.SkipZigTest;
616 }
617
618 if (builtin.os.tag == .freebsd) {
619 // TODO file issue to track this failure
620 return error.SkipZigTest;
621 }
612 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
613 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
614 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
615 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
616 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
622617
623618 const S = struct {
624619 fn doTheTest() !void {
......@@ -641,11 +636,6 @@ test "negation f128" {
641636 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
642637 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
643638
644 if (builtin.os.tag == .freebsd) {
645 // TODO file issue to track this failure
646 return error.SkipZigTest;
647 }
648
649639 const S = struct {
650640 fn doTheTest() !void {
651641 var a: f128 = 1;
test/behavior/math.zig+184-75
......@@ -6,6 +6,7 @@ const expectEqualSlices = std.testing.expectEqualSlices;
66const maxInt = std.math.maxInt;
77const minInt = std.math.minInt;
88const mem = std.mem;
9const math = std.math;
910
1011test "assignment operators" {
1112 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
......@@ -909,32 +910,52 @@ test "comptime float rem int" {
909910}
910911
911912test "remainder division" {
912 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
913 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
914 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
915 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
916 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
917 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
913918
914919 comptime try remdiv(f16);
915920 comptime try remdiv(f32);
916921 comptime try remdiv(f64);
922 comptime try remdiv(f80);
917923 comptime try remdiv(f128);
918924 try remdiv(f16);
919925 try remdiv(f64);
926 try remdiv(f80);
920927 try remdiv(f128);
921928}
922929
923930fn remdiv(comptime T: type) !void {
924931 try expect(@as(T, 1) == @as(T, 1) % @as(T, 2));
932 try remdivOne(T, 1, 1, 2);
933
925934 try expect(@as(T, 1) == @as(T, 7) % @as(T, 3));
935 try remdivOne(T, 1, 7, 3);
936}
937
938fn remdivOne(comptime T: type, a: T, b: T, c: T) !void {
939 try expect(a == @rem(b, c));
940 try expect(a == @mod(b, c));
926941}
927942
928943test "float remainder division using @rem" {
929 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
944 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
945 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
946 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
947 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
948 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
930949
931950 comptime try frem(f16);
932951 comptime try frem(f32);
933952 comptime try frem(f64);
953 comptime try frem(f80);
934954 comptime try frem(f128);
935955 try frem(f16);
936956 try frem(f32);
937957 try frem(f64);
958 try frem(f80);
938959 try frem(f128);
939960}
940961
......@@ -943,29 +964,40 @@ fn frem(comptime T: type) !void {
943964 f16 => 1.0,
944965 f32 => 0.001,
945966 f64 => 0.00001,
967 f80 => 0.000001,
946968 f128 => 0.0000001,
947969 else => unreachable,
948970 };
949971
950 try expect(std.math.fabs(@rem(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon);
951 try expect(std.math.fabs(@rem(@as(T, -6.9), @as(T, 4.0)) - @as(T, -2.9)) < epsilon);
952 try expect(std.math.fabs(@rem(@as(T, -5.0), @as(T, 3.0)) - @as(T, -2.0)) < epsilon);
953 try expect(std.math.fabs(@rem(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);
954 try expect(std.math.fabs(@rem(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);
955 try expect(std.math.fabs(@rem(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon);
956 try expect(std.math.fabs(@rem(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon);
972 try fremOne(T, 6.9, 4.0, 2.9, epsilon);
973 try fremOne(T, -6.9, 4.0, -2.9, epsilon);
974 try fremOne(T, -5.0, 3.0, -2.0, epsilon);
975 try fremOne(T, 3.0, 2.0, 1.0, epsilon);
976 try fremOne(T, 1.0, 2.0, 1.0, epsilon);
977 try fremOne(T, 0.0, 1.0, 0.0, epsilon);
978 try fremOne(T, -0.0, 1.0, -0.0, epsilon);
979}
980
981fn fremOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void {
982 try expect(@fabs(@rem(a, b) - c) < epsilon);
957983}
958984
959985test "float modulo division using @mod" {
960 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
986 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
987 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
988 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
989 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
990 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
961991
962992 comptime try fmod(f16);
963993 comptime try fmod(f32);
964994 comptime try fmod(f64);
995 comptime try fmod(f80);
965996 comptime try fmod(f128);
966997 try fmod(f16);
967998 try fmod(f32);
968999 try fmod(f64);
1000 try fmod(f80);
9691001 try fmod(f128);
9701002}
9711003
......@@ -974,17 +1006,22 @@ fn fmod(comptime T: type) !void {
9741006 f16 => 1.0,
9751007 f32 => 0.001,
9761008 f64 => 0.00001,
1009 f80 => 0.000001,
9771010 f128 => 0.0000001,
9781011 else => unreachable,
9791012 };
9801013
981 try expect(std.math.fabs(@mod(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon);
982 try expect(std.math.fabs(@mod(@as(T, -6.9), @as(T, 4.0)) - @as(T, 1.1)) < epsilon);
983 try expect(std.math.fabs(@mod(@as(T, -5.0), @as(T, 3.0)) - @as(T, 1.0)) < epsilon);
984 try expect(std.math.fabs(@mod(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);
985 try expect(std.math.fabs(@mod(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);
986 try expect(std.math.fabs(@mod(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon);
987 try expect(std.math.fabs(@mod(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon);
1014 try fmodOne(T, 6.9, 4.0, 2.9, epsilon);
1015 try fmodOne(T, -6.9, 4.0, 1.1, epsilon);
1016 try fmodOne(T, -5.0, 3.0, 1.0, epsilon);
1017 try fmodOne(T, 3.0, 2.0, 1.0, epsilon);
1018 try fmodOne(T, 1.0, 2.0, 1.0, epsilon);
1019 try fmodOne(T, 0.0, 1.0, 0.0, epsilon);
1020 try fmodOne(T, -0.0, 1.0, -0.0, epsilon);
1021}
1022
1023fn fmodOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void {
1024 try expect(@fabs(@mod(@as(T, a), @as(T, b)) - @as(T, c)) < epsilon);
9881025}
9891026
9901027test "@sqrt" {
......@@ -1036,10 +1073,11 @@ test "@fabs" {
10361073}
10371074
10381075test "@fabs f80" {
1039 if (true) {
1040 // https://github.com/ziglang/zig/issues/11030
1041 return error.SkipZigTest;
1042 }
1076 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1077 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1078 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1079 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1080 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10431081
10441082 try testFabs(f80, 12.0);
10451083 comptime try testFabs(f80, 12.0);
......@@ -1052,7 +1090,11 @@ fn testFabs(comptime T: type, x: T) !void {
10521090}
10531091
10541092test "@floor" {
1055 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1093 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1094 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1095 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1096 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1097 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10561098
10571099 try testFloor(f64, 12.0);
10581100 comptime try testFloor(f64, 12.0);
......@@ -1068,23 +1110,24 @@ test "@floor" {
10681110}
10691111
10701112test "@floor f80" {
1071 if (true) {
1072 // https://github.com/ziglang/zig/issues/11030
1073 return error.SkipZigTest;
1074 }
1113 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1114 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1115 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1116 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1117 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1118
10751119 try testFloor(f80, 12.0);
10761120 comptime try testFloor(f80, 12.0);
10771121}
10781122
10791123test "@floor f128" {
1080 if (builtin.zig_backend == .stage1) {
1081 // Fails because it incorrectly lowers to a floorl function call.
1082 return error.SkipZigTest;
1083 }
1084
1085 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1124 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1125 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1126 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1127 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1128 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10861129
1087 testFloor(f128, 12.0);
1130 try testFloor(f128, 12.0);
10881131 comptime try testFloor(f128, 12.0);
10891132}
10901133
......@@ -1095,7 +1138,11 @@ fn testFloor(comptime T: type, x: T) !void {
10951138}
10961139
10971140test "@ceil" {
1098 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1141 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1142 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1143 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1144 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1145 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10991146
11001147 try testCeil(f64, 12.0);
11011148 comptime try testCeil(f64, 12.0);
......@@ -1111,24 +1158,24 @@ test "@ceil" {
11111158}
11121159
11131160test "@ceil f80" {
1114 if (true) {
1115 // https://github.com/ziglang/zig/issues/11030
1116 return error.SkipZigTest;
1117 }
1161 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1162 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1164 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1165 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11181166
11191167 try testCeil(f80, 12.0);
11201168 comptime try testCeil(f80, 12.0);
11211169}
11221170
11231171test "@ceil f128" {
1124 if (builtin.zig_backend == .stage1) {
1125 // Fails because it incorrectly lowers to a ceill function call.
1126 return error.SkipZigTest;
1127 }
1128
1129 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1172 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1173 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1174 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1175 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1176 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11301177
1131 testCeil(f128, 12.0);
1178 try testCeil(f128, 12.0);
11321179 comptime try testCeil(f128, 12.0);
11331180}
11341181
......@@ -1139,7 +1186,11 @@ fn testCeil(comptime T: type, x: T) !void {
11391186}
11401187
11411188test "@trunc" {
1142 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1189 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1190 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1191 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1192 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1193 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11431194
11441195 try testTrunc(f64, 12.0);
11451196 comptime try testTrunc(f64, 12.0);
......@@ -1155,10 +1206,11 @@ test "@trunc" {
11551206}
11561207
11571208test "@trunc f80" {
1158 if (true) {
1159 // https://github.com/ziglang/zig/issues/11030
1160 return error.SkipZigTest;
1161 }
1209 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1210 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1211 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1212 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1213 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11621214
11631215 try testTrunc(f80, 12.0);
11641216 comptime try testTrunc(f80, 12.0);
......@@ -1171,14 +1223,13 @@ test "@trunc f80" {
11711223}
11721224
11731225test "@trunc f128" {
1174 if (builtin.zig_backend == .stage1) {
1175 // Fails because it incorrectly lowers to a truncl function call.
1176 return error.SkipZigTest;
1177 }
1178
1179 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1226 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1227 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1228 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1229 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1230 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11801231
1181 testTrunc(f128, 12.0);
1232 try testTrunc(f128, 12.0);
11821233 comptime try testTrunc(f128, 12.0);
11831234}
11841235
......@@ -1197,7 +1248,11 @@ fn testTrunc(comptime T: type, x: T) !void {
11971248}
11981249
11991250test "@round" {
1200 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1251 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1252 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1253 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1254 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1255 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12011256
12021257 try testRound(f64, 12.0);
12031258 comptime try testRound(f64, 12.0);
......@@ -1213,24 +1268,24 @@ test "@round" {
12131268}
12141269
12151270test "@round f80" {
1216 if (true) {
1217 // https://github.com/ziglang/zig/issues/11030
1218 return error.SkipZigTest;
1219 }
1271 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1272 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1273 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1274 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1275 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12201276
12211277 try testRound(f80, 12.0);
12221278 comptime try testRound(f80, 12.0);
12231279}
12241280
12251281test "@round f128" {
1226 if (builtin.zig_backend == .stage1) {
1227 // Fails because it incorrectly lowers to a roundl function call.
1228 return error.SkipZigTest;
1229 }
1230
1231 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1282 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1283 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1284 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1285 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1286 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12321287
1233 testRound(f128, 12.0);
1288 try testRound(f128, 12.0);
12341289 comptime try testRound(f128, 12.0);
12351290}
12361291
......@@ -1279,17 +1334,19 @@ test "NaN comparison" {
12791334}
12801335
12811336test "NaN comparison f80" {
1282 if (true) {
1283 // https://github.com/ziglang/zig/issues/11030
1284 return error.SkipZigTest;
1285 }
1337 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1338 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1341 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1342
12861343 try testNanEqNan(f80);
12871344 comptime try testNanEqNan(f80);
12881345}
12891346
12901347fn testNanEqNan(comptime F: type) !void {
1291 var nan1 = std.math.nan(F);
1292 var nan2 = std.math.nan(F);
1348 var nan1 = math.nan(F);
1349 var nan2 = math.nan(F);
12931350 try expect(nan1 != nan2);
12941351 try expect(!(nan1 == nan2));
12951352 try expect(!(nan1 > nan2));
......@@ -1346,3 +1403,55 @@ test "signed zeros are represented properly" {
13461403 try S.doTheTest();
13471404 comptime try S.doTheTest();
13481405}
1406
1407test "comptime sin and ln" {
1408 const v = comptime (@sin(@as(f32, 1)) + @log(@as(f32, 5)));
1409 try expect(v == @sin(@as(f32, 1)) + @log(@as(f32, 5)));
1410}
1411
1412test "fabs" {
1413 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1414 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1415 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1416 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1417 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1418
1419 inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
1420 // normals
1421 try expect(@fabs(@as(T, 1.0)) == 1.0);
1422 try expect(@fabs(@as(T, -1.0)) == 1.0);
1423 try expect(@fabs(math.floatMin(T)) == math.floatMin(T));
1424 try expect(@fabs(-math.floatMin(T)) == math.floatMin(T));
1425 try expect(@fabs(math.floatMax(T)) == math.floatMax(T));
1426 try expect(@fabs(-math.floatMax(T)) == math.floatMax(T));
1427
1428 // subnormals
1429 try expect(@fabs(@as(T, 0.0)) == 0.0);
1430 try expect(@fabs(@as(T, -0.0)) == 0.0);
1431 try expect(@fabs(math.floatTrueMin(T)) == math.floatTrueMin(T));
1432 try expect(@fabs(-math.floatTrueMin(T)) == math.floatTrueMin(T));
1433
1434 // non-finite numbers
1435 try expect(math.isPositiveInf(@fabs(math.inf(T))));
1436 try expect(math.isPositiveInf(@fabs(-math.inf(T))));
1437 try expect(math.isNan(@fabs(math.nan(T))));
1438 }
1439}
1440
1441test "absFloat" {
1442 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1443 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1444 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1445 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1446 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1447
1448 try testAbsFloat();
1449 comptime try testAbsFloat();
1450}
1451fn testAbsFloat() !void {
1452 try testAbsFloatOne(-10.05, 10.05);
1453 try testAbsFloatOne(10.05, 10.05);
1454}
1455fn testAbsFloatOne(in: f32, out: f32) !void {
1456 try expect(@fabs(@as(f32, in)) == @as(f32, out));
1457}
test/behavior/muladd.zig+13-8
......@@ -32,6 +32,7 @@ test "@mulAdd f16" {
3232 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
3333 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
3434 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
35
3536 comptime try testMulAdd16();
3637 try testMulAdd16();
3738}
......@@ -44,10 +45,12 @@ fn testMulAdd16() !void {
4445}
4546
4647test "@mulAdd f80" {
47 if (true) {
48 // https://github.com/ziglang/zig/issues/11030
49 return error.SkipZigTest;
50 }
48 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
50 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
52 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
53 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
5154
5255 comptime try testMulAdd80();
5356 try testMulAdd80();
......@@ -173,10 +176,12 @@ fn vector80() !void {
173176}
174177
175178test "vector f80" {
176 if (true) {
177 // https://github.com/ziglang/zig/issues/11030
178 return error.SkipZigTest;
179 }
179 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
180 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
181 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
182 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
183 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
184 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
180185
181186 comptime try vector80();
182187 try vector80();