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...@@ -445,7 +445,6 @@ set(ZIG_STAGE2_SOURCES
445 "${CMAKE_SOURCE_DIR}/lib/std/math/big.zig"445 "${CMAKE_SOURCE_DIR}/lib/std/math/big.zig"
446 "${CMAKE_SOURCE_DIR}/lib/std/math/big/int.zig"446 "${CMAKE_SOURCE_DIR}/lib/std/math/big/int.zig"
447 "${CMAKE_SOURCE_DIR}/lib/std/math/float.zig"447 "${CMAKE_SOURCE_DIR}/lib/std/math/float.zig"
448 "${CMAKE_SOURCE_DIR}/lib/std/math/floor.zig"
449 "${CMAKE_SOURCE_DIR}/lib/std/math/frexp.zig"448 "${CMAKE_SOURCE_DIR}/lib/std/math/frexp.zig"
450 "${CMAKE_SOURCE_DIR}/lib/std/math/isinf.zig"449 "${CMAKE_SOURCE_DIR}/lib/std/math/isinf.zig"
451 "${CMAKE_SOURCE_DIR}/lib/std/math/isnan.zig"450 "${CMAKE_SOURCE_DIR}/lib/std/math/isnan.zig"
...@@ -482,20 +481,40 @@ set(ZIG_STAGE2_SOURCES...@@ -482,20 +481,40 @@ set(ZIG_STAGE2_SOURCES
482 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig"481 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig"
483 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/absv.zig"482 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/absv.zig"
484 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/addXf3.zig"483 "${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"
485 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/atomics.zig"486 "${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"
486 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/bswap.zig"489 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/bswap.zig"
490 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/ceil.zig"
487 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/clear_cache.zig"491 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/clear_cache.zig"
488 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cmp.zig"492 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cmp.zig"
489 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/compareXf2.zig"493 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/compareXf2.zig"
494 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cos.zig"
490 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/count0bits.zig"495 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/count0bits.zig"
491 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divdf3.zig"496 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divdf3.zig"
492 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divsf3.zig"497 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divsf3.zig"
493 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divtf3.zig"498 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divtf3.zig"
494 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divti3.zig"499 "${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"
495 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extendXfYf2.zig"504 "${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"
496 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixXfYi.zig"507 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixXfYi.zig"
497 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXiYf.zig"508 "${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"
498 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/int.zig"514 "${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"
499 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig"518 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig"
500 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig"519 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig"
501 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/muldi3.zig"520 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/muldi3.zig"
...@@ -507,9 +526,22 @@ set(ZIG_STAGE2_SOURCES...@@ -507,9 +526,22 @@ set(ZIG_STAGE2_SOURCES
507 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/os_version_check.zig"526 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/os_version_check.zig"
508 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/parity.zig"527 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/parity.zig"
509 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/popcount.zig"528 "${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"
510 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/shift.zig"533 "${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"
511 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/stack_probe.zig"538 "${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"
512 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/truncXfYf2.zig"543 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/truncXfYf2.zig"
544 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trunc_f80.zig"
513 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmod.zig"545 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmod.zig"
514 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig"546 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig"
515 "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig"547 "${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 {...@@ -8026,7 +8026,7 @@ fn func(y: *i32) void {
8026 only rounds once, and is thus more accurate.8026 only rounds once, and is thus more accurate.
8027 </p>8027 </p>
8028 <p>8028 <p>
8029 Supports Floats and Vectors of floats.8029 Supports {#link|Floats#} and {#link|Vectors#} of floats.
8030 </p>8030 </p>
8031 {#header_close#}8031 {#header_close#}
80328032
...@@ -9440,6 +9440,7 @@ fn doTheTest() !void {...@@ -9440,6 +9440,7 @@ fn doTheTest() !void {
9440 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.9440 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9441 </p>9441 </p>
9442 {#header_close#}9442 {#header_close#}
9443
9443 {#header_open|@cos#}9444 {#header_open|@cos#}
9444 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>9445 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>
9445 <p>9446 <p>
...@@ -9451,6 +9452,19 @@ fn doTheTest() !void {...@@ -9451,6 +9452,19 @@ fn doTheTest() !void {
9451 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.9452 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9452 </p>9453 </p>
9453 {#header_close#}9454 {#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
9454 {#header_open|@exp#}9468 {#header_open|@exp#}
9455 <pre>{#syntax#}@exp(value: anytype) @TypeOf(value){#endsyntax#}</pre>9469 <pre>{#syntax#}@exp(value: anytype) @TypeOf(value){#endsyntax#}</pre>
9456 <p>9470 <p>
lib/std/fmt/errol.zig+4-4
...@@ -113,7 +113,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {...@@ -113,7 +113,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {
113 // normalize the midpoint113 // normalize the midpoint
114114
115 const e = math.frexp(val).exponent;115 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));
117 if (exp < 20) {117 if (exp < 20) {
118 exp = 20;118 exp = 20;
119 } else if (@intCast(usize, exp) >= lookup_table.len) {119 } else if (@intCast(usize, exp) >= lookup_table.len) {
...@@ -170,10 +170,10 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {...@@ -170,10 +170,10 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {
170 // digit generation170 // digit generation
171 var buf_index: usize = 0;171 var buf_index: usize = 0;
172 while (true) {172 while (true) {
173 var hdig = @floatToInt(u8, math.floor(high.val));173 var hdig = @floatToInt(u8, @floor(high.val));
174 if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1;174 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));
177 if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1;177 if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1;
178178
179 if (ldig != hdig) break;179 if (ldig != hdig) break;
...@@ -187,7 +187,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {...@@ -187,7 +187,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal {
187 }187 }
188188
189 const tmp = (high.val + low.val) / 2.0;189 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));
191 if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1;191 if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1;
192192
193 buffer[buf_index] = mdig + '0';193 buffer[buf_index] = mdig + '0';
lib/std/json.zig+1-1
...@@ -1655,7 +1655,7 @@ fn parseInternal(...@@ -1655,7 +1655,7 @@ fn parseInternal(
1655 if (numberToken.is_integer)1655 if (numberToken.is_integer)
1656 return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10);1656 return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10);
1657 const float = try std.fmt.parseFloat(f128, numberToken.slice(tokens.slice, tokens.i - 1));1657 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;
1659 if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow;1659 if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow;
1660 return @floatToInt(T, float);1660 return @floatToInt(T, float);
1661 },1661 },
lib/std/math.zig+2-29
...@@ -138,7 +138,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool {...@@ -138,7 +138,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool {
138 if (isNan(x) or isNan(y))138 if (isNan(x) or isNan(y))
139 return false;139 return false;
140140
141 return fabs(x - y) <= tolerance;141 return @fabs(x - y) <= tolerance;
142}142}
143143
144/// Performs an approximate comparison of two floating point values `x` and `y`.144/// 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 {...@@ -166,7 +166,7 @@ pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool {
166 if (isNan(x) or isNan(y))166 if (isNan(x) or isNan(y))
167 return false;167 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;
170}170}
171171
172pub fn approxEq(comptime T: type, x: T, y: T, tolerance: T) bool {172pub fn approxEq(comptime T: type, x: T, y: T, tolerance: T) bool {
...@@ -233,11 +233,6 @@ pub fn raiseDivByZero() void {...@@ -233,11 +233,6 @@ pub fn raiseDivByZero() void {
233233
234pub const isNan = @import("math/isnan.zig").isNan;234pub const isNan = @import("math/isnan.zig").isNan;
235pub const isSignalNan = @import("math/isnan.zig").isSignalNan;235pub 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;
241pub const frexp = @import("math/frexp.zig").frexp;236pub const frexp = @import("math/frexp.zig").frexp;
242pub const Frexp = @import("math/frexp.zig").Frexp;237pub const Frexp = @import("math/frexp.zig").Frexp;
243pub const modf = @import("math/modf.zig").modf;238pub const modf = @import("math/modf.zig").modf;
...@@ -261,8 +256,6 @@ pub const asin = @import("math/asin.zig").asin;...@@ -261,8 +256,6 @@ pub const asin = @import("math/asin.zig").asin;
261pub const atan = @import("math/atan.zig").atan;256pub const atan = @import("math/atan.zig").atan;
262pub const atan2 = @import("math/atan2.zig").atan2;257pub const atan2 = @import("math/atan2.zig").atan2;
263pub const hypot = @import("math/hypot.zig").hypot;258pub const hypot = @import("math/hypot.zig").hypot;
264pub const exp = @import("math/exp.zig").exp;
265pub const exp2 = @import("math/exp2.zig").exp2;
266pub const expm1 = @import("math/expm1.zig").expm1;259pub const expm1 = @import("math/expm1.zig").expm1;
267pub const ilogb = @import("math/ilogb.zig").ilogb;260pub const ilogb = @import("math/ilogb.zig").ilogb;
268pub const ln = @import("math/ln.zig").ln;261pub const ln = @import("math/ln.zig").ln;
...@@ -270,16 +263,12 @@ pub const log = @import("math/log.zig").log;...@@ -270,16 +263,12 @@ pub const log = @import("math/log.zig").log;
270pub const log2 = @import("math/log2.zig").log2;263pub const log2 = @import("math/log2.zig").log2;
271pub const log10 = @import("math/log10.zig").log10;264pub const log10 = @import("math/log10.zig").log10;
272pub const log1p = @import("math/log1p.zig").log1p;265pub const log1p = @import("math/log1p.zig").log1p;
273pub const fma = @import("math/fma.zig").fma;
274pub const asinh = @import("math/asinh.zig").asinh;266pub const asinh = @import("math/asinh.zig").asinh;
275pub const acosh = @import("math/acosh.zig").acosh;267pub const acosh = @import("math/acosh.zig").acosh;
276pub const atanh = @import("math/atanh.zig").atanh;268pub const atanh = @import("math/atanh.zig").atanh;
277pub const sinh = @import("math/sinh.zig").sinh;269pub const sinh = @import("math/sinh.zig").sinh;
278pub const cosh = @import("math/cosh.zig").cosh;270pub const cosh = @import("math/cosh.zig").cosh;
279pub const tanh = @import("math/tanh.zig").tanh;271pub 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
284pub const complex = @import("math/complex.zig");273pub const complex = @import("math/complex.zig");
285pub const Complex = complex.Complex;274pub const Complex = complex.Complex;
...@@ -716,17 +705,6 @@ fn testAbsInt() !void {...@@ -716,17 +705,6 @@ fn testAbsInt() !void {
716 try testing.expect((absInt(@as(i32, 10)) catch unreachable) == 10);705 try testing.expect((absInt(@as(i32, 10)) catch unreachable) == 10);
717}706}
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
730/// Divide numerator by denominator, rounding toward zero. Returns an708/// Divide numerator by denominator, rounding toward zero. Returns an
731/// error on overflow or when denominator is zero.709/// error on overflow or when denominator is zero.
732pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {710pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T {
...@@ -1400,11 +1378,6 @@ test "order.compare" {...@@ -1400,11 +1378,6 @@ test "order.compare" {
1400 try testing.expect(order(1, 0).compare(.neq));1378 try testing.expect(order(1, 0).compare(.neq));
1401}1379}
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
1408/// Returns a mask of all ones if value is true,1381/// Returns a mask of all ones if value is true,
1409/// and a mask of all zeroes if value is false.1382/// and a mask of all zeroes if value is false.
1410/// Compiles to one instruction for register sized integers.1383/// 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 {...@@ -64,14 +64,14 @@ fn acos32(x: f32) f32 {
64 // x < -0.564 // x < -0.5
65 if (hx >> 31 != 0) {65 if (hx >> 31 != 0) {
66 const z = (1 + x) * 0.5;66 const z = (1 + x) * 0.5;
67 const s = math.sqrt(z);67 const s = @sqrt(z);
68 const w = r32(z) * s - pio2_lo;68 const w = r32(z) * s - pio2_lo;
69 return 2 * (pio2_hi - (s + w));69 return 2 * (pio2_hi - (s + w));
70 }70 }
7171
72 // x > 0.572 // x > 0.5
73 const z = (1.0 - x) * 0.5;73 const z = (1.0 - x) * 0.5;
74 const s = math.sqrt(z);74 const s = @sqrt(z);
75 const jx = @bitCast(u32, s);75 const jx = @bitCast(u32, s);
76 const df = @bitCast(f32, jx & 0xFFFFF000);76 const df = @bitCast(f32, jx & 0xFFFFF000);
77 const c = (z - df * df) / (s + df);77 const c = (z - df * df) / (s + df);
...@@ -133,14 +133,14 @@ fn acos64(x: f64) f64 {...@@ -133,14 +133,14 @@ fn acos64(x: f64) f64 {
133 // x < -0.5133 // x < -0.5
134 if (hx >> 31 != 0) {134 if (hx >> 31 != 0) {
135 const z = (1.0 + x) * 0.5;135 const z = (1.0 + x) * 0.5;
136 const s = math.sqrt(z);136 const s = @sqrt(z);
137 const w = r64(z) * s - pio2_lo;137 const w = r64(z) * s - pio2_lo;
138 return 2 * (pio2_hi - (s + w));138 return 2 * (pio2_hi - (s + w));
139 }139 }
140140
141 // x > 0.5141 // x > 0.5
142 const z = (1.0 - x) * 0.5;142 const z = (1.0 - x) * 0.5;
143 const s = math.sqrt(z);143 const s = @sqrt(z);
144 const jx = @bitCast(u64, s);144 const jx = @bitCast(u64, s);
145 const df = @bitCast(f64, jx & 0xFFFFFFFF00000000);145 const df = @bitCast(f64, jx & 0xFFFFFFFF00000000);
146 const c = (z - df * df) / (s + df);146 const c = (z - df * df) / (s + df);
lib/std/math/acosh.zig+6-6
...@@ -29,15 +29,15 @@ fn acosh32(x: f32) f32 {...@@ -29,15 +29,15 @@ fn acosh32(x: f32) f32 {
2929
30 // |x| < 2, invalid if x < 1 or nan30 // |x| < 2, invalid if x < 1 or nan
31 if (i < 0x3F800000 + (1 << 23)) {31 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)));
33 }33 }
34 // |x| < 0x1p1234 // |x| < 0x1p12
35 else if (i < 0x3F800000 + (12 << 23)) {35 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)));
37 }37 }
38 // |x| >= 0x1p1238 // |x| >= 0x1p12
39 else {39 else {
40 return math.ln(x) + 0.693147180559945309417232121458176568;40 return @log(x) + 0.693147180559945309417232121458176568;
41 }41 }
42}42}
4343
...@@ -47,15 +47,15 @@ fn acosh64(x: f64) f64 {...@@ -47,15 +47,15 @@ fn acosh64(x: f64) f64 {
4747
48 // |x| < 2, invalid if x < 1 or nan48 // |x| < 2, invalid if x < 1 or nan
49 if (e < 0x3FF + 1) {49 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)));
51 }51 }
52 // |x| < 0x1p2652 // |x| < 0x1p26
53 else if (e < 0x3FF + 26) {53 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)));
55 }55 }
56 // |x| >= 0x1p26 or nan56 // |x| >= 0x1p26 or nan
57 else {57 else {
58 return math.ln(x) + 0.693147180559945309417232121458176568;58 return @log(x) + 0.693147180559945309417232121458176568;
59 }59 }
60}60}
6161
lib/std/math/asin.zig+4-4
...@@ -60,8 +60,8 @@ fn asin32(x: f32) f32 {...@@ -60,8 +60,8 @@ fn asin32(x: f32) f32 {
60 }60 }
6161
62 // 1 > |x| >= 0.562 // 1 > |x| >= 0.5
63 const z = (1 - math.fabs(x)) * 0.5;63 const z = (1 - @fabs(x)) * 0.5;
64 const s = math.sqrt(z);64 const s = @sqrt(z);
65 const fx = pio2 - 2 * (s + s * r32(z));65 const fx = pio2 - 2 * (s + s * r32(z));
6666
67 if (hx >> 31 != 0) {67 if (hx >> 31 != 0) {
...@@ -119,8 +119,8 @@ fn asin64(x: f64) f64 {...@@ -119,8 +119,8 @@ fn asin64(x: f64) f64 {
119 }119 }
120120
121 // 1 > |x| >= 0.5121 // 1 > |x| >= 0.5
122 const z = (1 - math.fabs(x)) * 0.5;122 const z = (1 - @fabs(x)) * 0.5;
123 const s = math.sqrt(z);123 const s = @sqrt(z);
124 const r = r64(z);124 const r = r64(z);
125 var fx: f64 = undefined;125 var fx: f64 = undefined;
126126
lib/std/math/asinh.zig+6-6
...@@ -39,15 +39,15 @@ fn asinh32(x: f32) f32 {...@@ -39,15 +39,15 @@ fn asinh32(x: f32) f32 {
3939
40 // |x| >= 0x1p12 or inf or nan40 // |x| >= 0x1p12 or inf or nan
41 if (i >= 0x3F800000 + (12 << 23)) {41 if (i >= 0x3F800000 + (12 << 23)) {
42 rx = math.ln(rx) + 0.69314718055994530941723212145817656;42 rx = @log(rx) + 0.69314718055994530941723212145817656;
43 }43 }
44 // |x| >= 244 // |x| >= 2
45 else if (i >= 0x3F800000 + (1 << 23)) {45 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));
47 }47 }
48 // |x| >= 0x1p-12, up to 1.6ulp error48 // |x| >= 0x1p-12, up to 1.6ulp error
49 else if (i >= 0x3F800000 - (12 << 23)) {49 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));
51 }51 }
52 // |x| < 0x1p-12, inexact if x != 052 // |x| < 0x1p-12, inexact if x != 0
53 else {53 else {
...@@ -70,15 +70,15 @@ fn asinh64(x: f64) f64 {...@@ -70,15 +70,15 @@ fn asinh64(x: f64) f64 {
7070
71 // |x| >= 0x1p26 or inf or nan71 // |x| >= 0x1p26 or inf or nan
72 if (e >= 0x3FF + 26) {72 if (e >= 0x3FF + 26) {
73 rx = math.ln(rx) + 0.693147180559945309417232121458176568;73 rx = @log(rx) + 0.693147180559945309417232121458176568;
74 }74 }
75 // |x| >= 275 // |x| >= 2
76 else if (e >= 0x3FF + 1) {76 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));
78 }78 }
79 // |x| >= 0x1p-12, up to 1.6ulp error79 // |x| >= 0x1p-12, up to 1.6ulp error
80 else if (e >= 0x3FF - 26) {80 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));
82 }82 }
83 // |x| < 0x1p-12, inexact if x != 083 // |x| < 0x1p-12, inexact if x != 0
84 else {84 else {
lib/std/math/atan.zig+2-2
...@@ -73,7 +73,7 @@ fn atan32(x_: f32) f32 {...@@ -73,7 +73,7 @@ fn atan32(x_: f32) f32 {
73 }73 }
74 id = null;74 id = null;
75 } else {75 } else {
76 x = math.fabs(x);76 x = @fabs(x);
77 // |x| < 1.187577 // |x| < 1.1875
78 if (ix < 0x3F980000) {78 if (ix < 0x3F980000) {
79 // 7/16 <= |x| < 11/1679 // 7/16 <= |x| < 11/16
...@@ -171,7 +171,7 @@ fn atan64(x_: f64) f64 {...@@ -171,7 +171,7 @@ fn atan64(x_: f64) f64 {
171 }171 }
172 id = null;172 id = null;
173 } else {173 } else {
174 x = math.fabs(x);174 x = @fabs(x);
175 // |x| < 1.1875175 // |x| < 1.1875
176 if (ix < 0x3FF30000) {176 if (ix < 0x3FF30000) {
177 // 7/16 <= |x| < 11/16177 // 7/16 <= |x| < 11/16
lib/std/math/atan2.zig+2-2
...@@ -108,7 +108,7 @@ fn atan2_32(y: f32, x: f32) f32 {...@@ -108,7 +108,7 @@ fn atan2_32(y: f32, x: f32) f32 {
108 if ((m & 2) != 0 and iy + (26 << 23) < ix) {108 if ((m & 2) != 0 and iy + (26 << 23) < ix) {
109 break :z 0.0;109 break :z 0.0;
110 } else {110 } else {
111 break :z math.atan(math.fabs(y / x));111 break :z math.atan(@fabs(y / x));
112 }112 }
113 };113 };
114114
...@@ -198,7 +198,7 @@ fn atan2_64(y: f64, x: f64) f64 {...@@ -198,7 +198,7 @@ fn atan2_64(y: f64, x: f64) f64 {
198 if ((m & 2) != 0 and iy +% (64 << 20) < ix) {198 if ((m & 2) != 0 and iy +% (64 << 20) < ix) {
199 break :z 0.0;199 break :z 0.0;
200 } else {200 } else {
201 break :z math.atan(math.fabs(y / x));201 break :z math.atan(@fabs(y / x));
202 }202 }
203 };203 };
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 {...@@ -115,7 +115,7 @@ pub fn Complex(comptime T: type) type {
115115
116 /// Returns the magnitude of a complex number.116 /// Returns the magnitude of a complex number.
117 pub fn magnitude(self: Self) T {117 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);
119 }119 }
120 };120 };
121}121}
lib/std/math/complex/atan.zig+2-2
...@@ -66,7 +66,7 @@ fn atan32(z: Complex(f32)) Complex(f32) {...@@ -66,7 +66,7 @@ fn atan32(z: Complex(f32)) Complex(f32) {
6666
67 t = y + 1.0;67 t = y + 1.0;
68 a = (x2 + (t * t)) / a;68 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));
70}70}
7171
72fn redupif64(x: f64) f64 {72fn redupif64(x: f64) f64 {
...@@ -115,7 +115,7 @@ fn atan64(z: Complex(f64)) Complex(f64) {...@@ -115,7 +115,7 @@ fn atan64(z: Complex(f64)) Complex(f64) {
115115
116 t = y + 1.0;116 t = y + 1.0;
117 a = (x2 + (t * t)) / a;117 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));
119}119}
120120
121const epsilon = 0.0001;121const epsilon = 0.0001;
lib/std/math/complex/cosh.zig+12-12
...@@ -38,25 +38,25 @@ fn cosh32(z: Complex(f32)) Complex(f32) {...@@ -38,25 +38,25 @@ fn cosh32(z: Complex(f32)) Complex(f32) {
38 }38 }
39 // small x: normal case39 // small x: normal case
40 if (ix < 0x41100000) {40 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));
42 }42 }
4343
44 // |x|>= 9, so cosh(x) ~= exp(|x|)44 // |x|>= 9, so cosh(x) ~= exp(|x|)
45 if (ix < 0x42b17218) {45 if (ix < 0x42b17218) {
46 // x < 88.7: exp(|x|) won't overflow46 // x < 88.7: exp(|x|) won't overflow
47 const h = math.exp(math.fabs(x)) * 0.5;47 const h = @exp(@fabs(x)) * 0.5;
48 return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));48 return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y));
49 }49 }
50 // x < 192.7: scale to avoid overflow50 // x < 192.7: scale to avoid overflow
51 else if (ix < 0x4340b1e7) {51 else if (ix < 0x4340b1e7) {
52 const v = Complex(f32).init(math.fabs(x), y);52 const v = Complex(f32).init(@fabs(x), y);
53 const r = ldexp_cexp(v, -1);53 const r = ldexp_cexp(v, -1);
54 return Complex(f32).init(r.re, r.im * math.copysign(f32, 1, x));54 return Complex(f32).init(r.re, r.im * math.copysign(f32, 1, x));
55 }55 }
56 // x >= 192.7: result always overflows56 // x >= 192.7: result always overflows
57 else {57 else {
58 const h = 0x1p127 * x;58 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));
60 }60 }
61 }61 }
6262
...@@ -79,7 +79,7 @@ fn cosh32(z: Complex(f32)) Complex(f32) {...@@ -79,7 +79,7 @@ fn cosh32(z: Complex(f32)) Complex(f32) {
79 if (iy >= 0x7f800000) {79 if (iy >= 0x7f800000) {
80 return Complex(f32).init(x * x, x * (y - y));80 return Complex(f32).init(x * x, x * (y - y));
81 }81 }
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));
83 }83 }
8484
85 return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y));85 return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y));
...@@ -106,25 +106,25 @@ fn cosh64(z: Complex(f64)) Complex(f64) {...@@ -106,25 +106,25 @@ fn cosh64(z: Complex(f64)) Complex(f64) {
106 }106 }
107 // small x: normal case107 // small x: normal case
108 if (ix < 0x40360000) {108 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));
110 }110 }
111111
112 // |x|>= 22, so cosh(x) ~= exp(|x|)112 // |x|>= 22, so cosh(x) ~= exp(|x|)
113 if (ix < 0x40862e42) {113 if (ix < 0x40862e42) {
114 // x < 710: exp(|x|) won't overflow114 // x < 710: exp(|x|) won't overflow
115 const h = math.exp(math.fabs(x)) * 0.5;115 const h = @exp(@fabs(x)) * 0.5;
116 return Complex(f64).init(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y));116 return Complex(f64).init(h * @cos(y), math.copysign(f64, h, x) * @sin(y));
117 }117 }
118 // x < 1455: scale to avoid overflow118 // x < 1455: scale to avoid overflow
119 else if (ix < 0x4096bbaa) {119 else if (ix < 0x4096bbaa) {
120 const v = Complex(f64).init(math.fabs(x), y);120 const v = Complex(f64).init(@fabs(x), y);
121 const r = ldexp_cexp(v, -1);121 const r = ldexp_cexp(v, -1);
122 return Complex(f64).init(r.re, r.im * math.copysign(f64, 1, x));122 return Complex(f64).init(r.re, r.im * math.copysign(f64, 1, x));
123 }123 }
124 // x >= 1455: result always overflows124 // x >= 1455: result always overflows
125 else {125 else {
126 const h = 0x1p1023;126 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));
128 }128 }
129 }129 }
130130
...@@ -147,7 +147,7 @@ fn cosh64(z: Complex(f64)) Complex(f64) {...@@ -147,7 +147,7 @@ fn cosh64(z: Complex(f64)) Complex(f64) {
147 if (iy >= 0x7ff00000) {147 if (iy >= 0x7ff00000) {
148 return Complex(f64).init(x * x, x * (y - y));148 return Complex(f64).init(x * x, x * (y - y));
149 }149 }
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));
151 }151 }
152152
153 return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y));153 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) {...@@ -33,13 +33,13 @@ fn exp32(z: Complex(f32)) Complex(f32) {
33 const hy = @bitCast(u32, y) & 0x7fffffff;33 const hy = @bitCast(u32, y) & 0x7fffffff;
34 // cexp(x + i0) = exp(x) + i034 // cexp(x + i0) = exp(x) + i0
35 if (hy == 0) {35 if (hy == 0) {
36 return Complex(f32).init(math.exp(x), y);36 return Complex(f32).init(@exp(x), y);
37 }37 }
3838
39 const hx = @bitCast(u32, x);39 const hx = @bitCast(u32, x);
40 // cexp(0 + iy) = cos(y) + isin(y)40 // cexp(0 + iy) = cos(y) + isin(y)
41 if ((hx & 0x7fffffff) == 0) {41 if ((hx & 0x7fffffff) == 0) {
42 return Complex(f32).init(math.cos(y), math.sin(y));42 return Complex(f32).init(@cos(y), @sin(y));
43 }43 }
4444
45 if (hy >= 0x7f800000) {45 if (hy >= 0x7f800000) {
...@@ -63,8 +63,8 @@ fn exp32(z: Complex(f32)) Complex(f32) {...@@ -63,8 +63,8 @@ fn exp32(z: Complex(f32)) Complex(f32) {
63 // - x = +-inf63 // - x = +-inf
64 // - x = nan64 // - x = nan
65 else {65 else {
66 const exp_x = math.exp(x);66 const exp_x = @exp(x);
67 return Complex(f32).init(exp_x * math.cos(y), exp_x * math.sin(y));67 return Complex(f32).init(exp_x * @cos(y), exp_x * @sin(y));
68 }68 }
69}69}
7070
...@@ -81,7 +81,7 @@ fn exp64(z: Complex(f64)) Complex(f64) {...@@ -81,7 +81,7 @@ fn exp64(z: Complex(f64)) Complex(f64) {
8181
82 // cexp(x + i0) = exp(x) + i082 // cexp(x + i0) = exp(x) + i0
83 if (hy | ly == 0) {83 if (hy | ly == 0) {
84 return Complex(f64).init(math.exp(x), y);84 return Complex(f64).init(@exp(x), y);
85 }85 }
8686
87 const fx = @bitCast(u64, x);87 const fx = @bitCast(u64, x);
...@@ -90,7 +90,7 @@ fn exp64(z: Complex(f64)) Complex(f64) {...@@ -90,7 +90,7 @@ fn exp64(z: Complex(f64)) Complex(f64) {
9090
91 // cexp(0 + iy) = cos(y) + isin(y)91 // cexp(0 + iy) = cos(y) + isin(y)
92 if ((hx & 0x7fffffff) | lx == 0) {92 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));
94 }94 }
9595
96 if (hy >= 0x7ff00000) {96 if (hy >= 0x7ff00000) {
...@@ -114,13 +114,13 @@ fn exp64(z: Complex(f64)) Complex(f64) {...@@ -114,13 +114,13 @@ fn exp64(z: Complex(f64)) Complex(f64) {
114 // - x = +-inf114 // - x = +-inf
115 // - x = nan115 // - x = nan
116 else {116 else {
117 const exp_x = math.exp(x);117 const exp_x = @exp(x);
118 return Complex(f64).init(exp_x * math.cos(y), exp_x * math.sin(y));118 return Complex(f64).init(exp_x * @cos(y), exp_x * @sin(y));
119 }119 }
120}120}
121121
122test "complex.cexp32" {122test "complex.cexp32" {
123 const tolerance_f32 = math.sqrt(math.floatEps(f32));123 const tolerance_f32 = @sqrt(math.floatEps(f32));
124124
125 {125 {
126 const a = Complex(f32).init(5, 3);126 const a = Complex(f32).init(5, 3);
...@@ -140,7 +140,7 @@ test "complex.cexp32" {...@@ -140,7 +140,7 @@ test "complex.cexp32" {
140}140}
141141
142test "complex.cexp64" {142test "complex.cexp64" {
143 const tolerance_f64 = math.sqrt(math.floatEps(f64));143 const tolerance_f64 = @sqrt(math.floatEps(f64));
144144
145 {145 {
146 const a = Complex(f64).init(5, 3);146 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 {...@@ -26,7 +26,7 @@ fn frexp_exp32(x: f32, expt: *i32) f32 {
26 const k = 235; // reduction constant26 const k = 235; // reduction constant
27 const kln2 = 162.88958740; // k * ln227 const kln2 = 162.88958740; // k * ln2
2828
29 const exp_x = math.exp(x - kln2);29 const exp_x = @exp(x - kln2);
30 const hx = @bitCast(u32, exp_x);30 const hx = @bitCast(u32, exp_x);
31 // TODO zig should allow this cast implicitly because it should know the value is in range31 // TODO zig should allow this cast implicitly because it should know the value is in range
32 expt.* = @intCast(i32, hx >> 23) - (0x7f + 127) + k;32 expt.* = @intCast(i32, hx >> 23) - (0x7f + 127) + k;
...@@ -45,8 +45,8 @@ fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) {...@@ -45,8 +45,8 @@ fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) {
45 const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);45 const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);
4646
47 return Complex(f32).init(47 return Complex(f32).init(
48 math.cos(z.im) * exp_x * scale1 * scale2,48 @cos(z.im) * exp_x * scale1 * scale2,
49 math.sin(z.im) * exp_x * scale1 * scale2,49 @sin(z.im) * exp_x * scale1 * scale2,
50 );50 );
51}51}
5252
...@@ -54,7 +54,7 @@ fn frexp_exp64(x: f64, expt: *i32) f64 {...@@ -54,7 +54,7 @@ fn frexp_exp64(x: f64, expt: *i32) f64 {
54 const k = 1799; // reduction constant54 const k = 1799; // reduction constant
55 const kln2 = 1246.97177782734161156; // k * ln255 const kln2 = 1246.97177782734161156; // k * ln2
5656
57 const exp_x = math.exp(x - kln2);57 const exp_x = @exp(x - kln2);
5858
59 const fx = @bitCast(u64, exp_x);59 const fx = @bitCast(u64, exp_x);
60 const hx = @intCast(u32, fx >> 32);60 const hx = @intCast(u32, fx >> 32);
...@@ -78,7 +78,7 @@ fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) {...@@ -78,7 +78,7 @@ fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) {
78 const scale2 = @bitCast(f64, (0x3ff + half_expt2) << (20 + 32));78 const scale2 = @bitCast(f64, (0x3ff + half_expt2) << (20 + 32));
7979
80 return Complex(f64).init(80 return Complex(f64).init(
81 math.cos(z.im) * exp_x * scale1 * scale2,81 @cos(z.im) * exp_x * scale1 * scale2,
82 math.sin(z.im) * exp_x * scale1 * scale2,82 @sin(z.im) * exp_x * scale1 * scale2,
83 );83 );
84}84}
lib/std/math/complex/log.zig+1-1
...@@ -10,7 +10,7 @@ pub fn log(z: anytype) Complex(@TypeOf(z.re)) {...@@ -10,7 +10,7 @@ pub fn log(z: anytype) Complex(@TypeOf(z.re)) {
10 const r = cmath.abs(z);10 const r = cmath.abs(z);
11 const phi = cmath.arg(z);11 const phi = cmath.arg(z);
1212
13 return Complex(T).init(math.ln(r), phi);13 return Complex(T).init(@log(r), phi);
14}14}
1515
16const epsilon = 0.0001;16const epsilon = 0.0001;
lib/std/math/complex/sinh.zig+12-12
...@@ -38,25 +38,25 @@ fn sinh32(z: Complex(f32)) Complex(f32) {...@@ -38,25 +38,25 @@ fn sinh32(z: Complex(f32)) Complex(f32) {
38 }38 }
39 // small x: normal case39 // small x: normal case
40 if (ix < 0x41100000) {40 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));
42 }42 }
4343
44 // |x|>= 9, so cosh(x) ~= exp(|x|)44 // |x|>= 9, so cosh(x) ~= exp(|x|)
45 if (ix < 0x42b17218) {45 if (ix < 0x42b17218) {
46 // x < 88.7: exp(|x|) won't overflow46 // x < 88.7: exp(|x|) won't overflow
47 const h = math.exp(math.fabs(x)) * 0.5;47 const h = @exp(@fabs(x)) * 0.5;
48 return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));48 return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y));
49 }49 }
50 // x < 192.7: scale to avoid overflow50 // x < 192.7: scale to avoid overflow
51 else if (ix < 0x4340b1e7) {51 else if (ix < 0x4340b1e7) {
52 const v = Complex(f32).init(math.fabs(x), y);52 const v = Complex(f32).init(@fabs(x), y);
53 const r = ldexp_cexp(v, -1);53 const r = ldexp_cexp(v, -1);
54 return Complex(f32).init(r.re * math.copysign(f32, 1, x), r.im);54 return Complex(f32).init(r.re * math.copysign(f32, 1, x), r.im);
55 }55 }
56 // x >= 192.7: result always overflows56 // x >= 192.7: result always overflows
57 else {57 else {
58 const h = 0x1p127 * x;58 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));
60 }60 }
61 }61 }
6262
...@@ -79,7 +79,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) {...@@ -79,7 +79,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) {
79 if (iy >= 0x7f800000) {79 if (iy >= 0x7f800000) {
80 return Complex(f32).init(x * x, x * (y - y));80 return Complex(f32).init(x * x, x * (y - y));
81 }81 }
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));
83 }83 }
8484
85 return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y));85 return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y));
...@@ -105,25 +105,25 @@ fn sinh64(z: Complex(f64)) Complex(f64) {...@@ -105,25 +105,25 @@ fn sinh64(z: Complex(f64)) Complex(f64) {
105 }105 }
106 // small x: normal case106 // small x: normal case
107 if (ix < 0x40360000) {107 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));
109 }109 }
110110
111 // |x|>= 22, so cosh(x) ~= exp(|x|)111 // |x|>= 22, so cosh(x) ~= exp(|x|)
112 if (ix < 0x40862e42) {112 if (ix < 0x40862e42) {
113 // x < 710: exp(|x|) won't overflow113 // x < 710: exp(|x|) won't overflow
114 const h = math.exp(math.fabs(x)) * 0.5;114 const h = @exp(@fabs(x)) * 0.5;
115 return Complex(f64).init(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));115 return Complex(f64).init(math.copysign(f64, h, x) * @cos(y), h * @sin(y));
116 }116 }
117 // x < 1455: scale to avoid overflow117 // x < 1455: scale to avoid overflow
118 else if (ix < 0x4096bbaa) {118 else if (ix < 0x4096bbaa) {
119 const v = Complex(f64).init(math.fabs(x), y);119 const v = Complex(f64).init(@fabs(x), y);
120 const r = ldexp_cexp(v, -1);120 const r = ldexp_cexp(v, -1);
121 return Complex(f64).init(r.re * math.copysign(f64, 1, x), r.im);121 return Complex(f64).init(r.re * math.copysign(f64, 1, x), r.im);
122 }122 }
123 // x >= 1455: result always overflows123 // x >= 1455: result always overflows
124 else {124 else {
125 const h = 0x1p1023 * x;125 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));
127 }127 }
128 }128 }
129129
...@@ -146,7 +146,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) {...@@ -146,7 +146,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) {
146 if (iy >= 0x7ff00000) {146 if (iy >= 0x7ff00000) {
147 return Complex(f64).init(x * x, x * (y - y));147 return Complex(f64).init(x * x, x * (y - y));
148 }148 }
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));
150 }150 }
151151
152 return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y));152 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) {...@@ -43,7 +43,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) {
43 // sqrt(-inf + i nan) = nan +- inf i43 // sqrt(-inf + i nan) = nan +- inf i
44 // sqrt(-inf + iy) = 0 + inf i44 // sqrt(-inf + iy) = 0 + inf i
45 if (math.signbit(x)) {45 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));
47 } else {47 } else {
48 return Complex(f32).init(x, math.copysign(f32, y - y, y));48 return Complex(f32).init(x, math.copysign(f32, y - y, y));
49 }49 }
...@@ -56,15 +56,15 @@ fn sqrt32(z: Complex(f32)) Complex(f32) {...@@ -56,15 +56,15 @@ fn sqrt32(z: Complex(f32)) Complex(f32) {
56 const dy = @as(f64, y);56 const dy = @as(f64, y);
5757
58 if (dx >= 0) {58 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);
60 return Complex(f32).init(60 return Complex(f32).init(
61 @floatCast(f32, t),61 @floatCast(f32, t),
62 @floatCast(f32, dy / (2.0 * t)),62 @floatCast(f32, dy / (2.0 * t)),
63 );63 );
64 } else {64 } 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);
66 return Complex(f32).init(66 return Complex(f32).init(
67 @floatCast(f32, math.fabs(y) / (2.0 * t)),67 @floatCast(f32, @fabs(y) / (2.0 * t)),
68 @floatCast(f32, math.copysign(f64, t, y)),68 @floatCast(f32, math.copysign(f64, t, y)),
69 );69 );
70 }70 }
...@@ -94,7 +94,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {...@@ -94,7 +94,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
94 // sqrt(-inf + i nan) = nan +- inf i94 // sqrt(-inf + i nan) = nan +- inf i
95 // sqrt(-inf + iy) = 0 + inf i95 // sqrt(-inf + iy) = 0 + inf i
96 if (math.signbit(x)) {96 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));
98 } else {98 } else {
99 return Complex(f64).init(x, math.copysign(f64, y - y, y));99 return Complex(f64).init(x, math.copysign(f64, y - y, y));
100 }100 }
...@@ -104,7 +104,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {...@@ -104,7 +104,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
104104
105 // scale to avoid overflow105 // scale to avoid overflow
106 var scale = false;106 var scale = false;
107 if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) {107 if (@fabs(x) >= threshold or @fabs(y) >= threshold) {
108 x *= 0.25;108 x *= 0.25;
109 y *= 0.25;109 y *= 0.25;
110 scale = true;110 scale = true;
...@@ -112,11 +112,11 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {...@@ -112,11 +112,11 @@ fn sqrt64(z: Complex(f64)) Complex(f64) {
112112
113 var result: Complex(f64) = undefined;113 var result: Complex(f64) = undefined;
114 if (x >= 0) {114 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);
116 result = Complex(f64).init(t, y / (2.0 * t));116 result = Complex(f64).init(t, y / (2.0 * t));
117 } else {117 } else {
118 const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5);118 const t = @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));119 result = Complex(f64).init(@fabs(y) / (2.0 * t), math.copysign(f64, t, y));
120 }120 }
121121
122 if (scale) {122 if (scale) {
lib/std/math/complex/tanh.zig+10-10
...@@ -33,7 +33,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) {...@@ -33,7 +33,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) {
33 return Complex(f32).init(x, r);33 return Complex(f32).init(x, r);
34 }34 }
35 const xx = @bitCast(f32, hx - 0x40000000);35 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);
37 return Complex(f32).init(xx, math.copysign(f32, 0, r));37 return Complex(f32).init(xx, math.copysign(f32, 0, r));
38 }38 }
3939
...@@ -44,15 +44,15 @@ fn tanh32(z: Complex(f32)) Complex(f32) {...@@ -44,15 +44,15 @@ fn tanh32(z: Complex(f32)) Complex(f32) {
4444
45 // x >= 1145 // x >= 11
46 if (ix >= 0x41300000) {46 if (ix >= 0x41300000) {
47 const exp_mx = math.exp(-math.fabs(x));47 const exp_mx = @exp(-@fabs(x));
48 return Complex(f32).init(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);48 return Complex(f32).init(math.copysign(f32, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx);
49 }49 }
5050
51 // Kahan's algorithm51 // Kahan's algorithm
52 const t = math.tan(y);52 const t = @tan(y);
53 const beta = 1.0 + t * t;53 const beta = 1.0 + t * t;
54 const s = math.sinh(x);54 const s = math.sinh(x);
55 const rho = math.sqrt(1 + s * s);55 const rho = @sqrt(1 + s * s);
56 const den = 1 + beta * s * s;56 const den = 1 + beta * s * s;
5757
58 return Complex(f32).init((beta * rho * s) / den, t / den);58 return Complex(f32).init((beta * rho * s) / den, t / den);
...@@ -76,7 +76,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) {...@@ -76,7 +76,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) {
76 }76 }
7777
78 const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx);78 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);
80 return Complex(f64).init(xx, math.copysign(f64, 0, r));80 return Complex(f64).init(xx, math.copysign(f64, 0, r));
81 }81 }
8282
...@@ -87,15 +87,15 @@ fn tanh64(z: Complex(f64)) Complex(f64) {...@@ -87,15 +87,15 @@ fn tanh64(z: Complex(f64)) Complex(f64) {
8787
88 // x >= 2288 // x >= 22
89 if (ix >= 0x40360000) {89 if (ix >= 0x40360000) {
90 const exp_mx = math.exp(-math.fabs(x));90 const exp_mx = @exp(-@fabs(x));
91 return Complex(f64).init(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);91 return Complex(f64).init(math.copysign(f64, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx);
92 }92 }
9393
94 // Kahan's algorithm94 // Kahan's algorithm
95 const t = math.tan(y);95 const t = @tan(y);
96 const beta = 1.0 + t * t;96 const beta = 1.0 + t * t;
97 const s = math.sinh(x);97 const s = math.sinh(x);
98 const rho = math.sqrt(1 + s * s);98 const rho = @sqrt(1 + s * s);
99 const den = 1 + beta * s * s;99 const den = 1 + beta * s * s;
100100
101 return Complex(f64).init((beta * rho * s) / den, t / den);101 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 {...@@ -45,7 +45,7 @@ fn cosh32(x: f32) f32 {
4545
46 // |x| < log(FLT_MAX)46 // |x| < log(FLT_MAX)
47 if (ux < 0x42B17217) {47 if (ux < 0x42B17217) {
48 const t = math.exp(ax);48 const t = @exp(ax);
49 return 0.5 * (t + 1 / t);49 return 0.5 * (t + 1 / t);
50 }50 }
5151
...@@ -77,7 +77,7 @@ fn cosh64(x: f64) f64 {...@@ -77,7 +77,7 @@ fn cosh64(x: f64) f64 {
7777
78 // |x| < log(DBL_MAX)78 // |x| < log(DBL_MAX)
79 if (w < 0x40862E42) {79 if (w < 0x40862E42) {
80 const t = math.exp(ax);80 const t = @exp(ax);
81 // NOTE: If x > log(0x1p26) then 1/t is not required.81 // NOTE: If x > log(0x1p26) then 1/t is not required.
82 return 0.5 * (t + 1 / t);82 return 0.5 * (t + 1 / t);
83 }83 }
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 {...@@ -22,7 +22,7 @@ fn expo2f(x: f32) f32 {
2222
23 const u = (0x7F + k / 2) << 23;23 const u = (0x7F + k / 2) << 23;
24 const scale = @bitCast(f32, u);24 const scale = @bitCast(f32, u);
25 return math.exp(x - kln2) * scale * scale;25 return @exp(x - kln2) * scale * scale;
26}26}
2727
28fn expo2d(x: f64) f64 {28fn expo2d(x: f64) f64 {
...@@ -31,5 +31,5 @@ fn expo2d(x: f64) f64 {...@@ -31,5 +31,5 @@ fn expo2d(x: f64) f64 {
3131
32 const u = (0x3FF + k / 2) << 20;32 const u = (0x3FF + k / 2) << 20;
33 const scale = @bitCast(f64, @as(u64, u) << 32);33 const scale = @bitCast(f64, @as(u64, u) << 32);
34 return math.exp(x - kln2) * scale * scale;34 return @exp(x - kln2) * scale * scale;
35}35}
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 {...@@ -56,7 +56,7 @@ fn hypot32(x: f32, y: f32) f32 {
56 yy *= 0x1.0p-90;56 yy *= 0x1.0p-90;
57 }57 }
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));
60}60}
6161
62fn sq(hi: *f64, lo: *f64, x: f64) void {62fn sq(hi: *f64, lo: *f64, x: f64) void {
...@@ -117,7 +117,7 @@ fn hypot64(x: f64, y: f64) f64 {...@@ -117,7 +117,7 @@ fn hypot64(x: f64, y: f64) f64 {
117 sq(&hx, &lx, x);117 sq(&hx, &lx, x);
118 sq(&hy, &ly, y);118 sq(&hy, &ly, y);
119119
120 return z * math.sqrt(ly + lx + hy + hx);120 return z * @sqrt(ly + lx + hy + hx);
121}121}
122122
123test "math.hypot" {123test "math.hypot" {
lib/std/math/ln.zig+8-163
...@@ -1,12 +1,6 @@...@@ -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
7const std = @import("../std.zig");1const std = @import("../std.zig");
8const math = std.math;2const math = std.math;
9const expect = std.testing.expect;3const testing = std.testing;
104
11/// Returns the natural logarithm of x.5/// Returns the natural logarithm of x.
12///6///
...@@ -15,175 +9,26 @@ const expect = std.testing.expect;...@@ -15,175 +9,26 @@ const expect = std.testing.expect;
15/// - ln(0) = -inf9/// - ln(0) = -inf
16/// - ln(x) = nan if x < 010/// - ln(x) = nan if x < 0
17/// - ln(nan) = nan11/// - ln(nan) = nan
12/// TODO remove this in favor of `@log`.
18pub fn ln(x: anytype) @TypeOf(x) {13pub fn ln(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);14 const T = @TypeOf(x);
20 switch (@typeInfo(T)) {15 switch (@typeInfo(T)) {
21 .ComptimeFloat => {16 .ComptimeFloat => {
22 return @as(comptime_float, ln_64(x));17 return @as(comptime_float, @log(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 };
30 },18 },
19 .Float => return @log(x),
31 .ComptimeInt => {20 .ComptimeInt => {
32 return @as(comptime_int, math.floor(ln_64(@as(f64, x))));21 return @as(comptime_int, @floor(@log(@as(f64, x))));
33 },22 },
34 .Int => |IntType| switch (IntType.signedness) {23 .Int => |IntType| switch (IntType.signedness) {
35 .signed => @compileError("ln not implemented for signed integers"),24 .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)))),
37 },26 },
38 else => @compileError("ln not implemented for " ++ @typeName(T)),27 else => @compileError("ln not implemented for " ++ @typeName(T)),
39 }28 }
40}29}
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
150test "math.ln" {31test "math.ln" {
151 try expect(ln(@as(f32, 0.2)) == ln_32(0.2));32 try testing.expect(ln(@as(f32, 0.2)) == @log(0.2));
152 try expect(ln(@as(f64, 0.2)) == ln_64(0.2));33 try testing.expect(ln(@as(f64, 0.2)) == @log(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))));
189}34}
lib/std/math/log.zig+6-6
...@@ -15,28 +15,28 @@ pub fn log(comptime T: type, base: T, x: T) T {...@@ -15,28 +15,28 @@ pub fn log(comptime T: type, base: T, x: T) T {
15 } else if (base == 10) {15 } else if (base == 10) {
16 return math.log10(x);16 return math.log10(x);
17 } else if ((@typeInfo(T) == .Float or @typeInfo(T) == .ComptimeFloat) and base == math.e) {17 } else if ((@typeInfo(T) == .Float or @typeInfo(T) == .ComptimeFloat) and base == math.e) {
18 return math.ln(x);18 return @log(x);
19 }19 }
2020
21 const float_base = math.lossyCast(f64, base);21 const float_base = math.lossyCast(f64, base);
22 switch (@typeInfo(T)) {22 switch (@typeInfo(T)) {
23 .ComptimeFloat => {23 .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));
25 },25 },
26 .ComptimeInt => {26 .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)));
28 },28 },
2929
30 // TODO implement integer log without using float math30 // TODO implement integer log without using float math
31 .Int => |IntType| switch (IntType.signedness) {31 .Int => |IntType| switch (IntType.signedness) {
32 .signed => @compileError("log not implemented for signed integers"),32 .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))),
34 },34 },
3535
36 .Float => {36 .Float => {
37 switch (T) {37 switch (T) {
38 f32 => return @floatCast(f32, math.ln(@as(f64, x)) / math.ln(float_base)),38 f32 => return @floatCast(f32, @log(@as(f64, x)) / @log(float_base)),
39 f64 => return math.ln(x) / math.ln(float_base),39 f64 => return @log(x) / @log(float_base),
40 else => @compileError("log not implemented for " ++ @typeName(T)),40 else => @compileError("log not implemented for " ++ @typeName(T)),
41 }41 }
42 },42 },
lib/std/math/log10.zig+4-192
...@@ -1,9 +1,3 @@...@@ -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
7const std = @import("../std.zig");1const std = @import("../std.zig");
8const math = std.math;2const math = std.math;
9const testing = std.testing;3const testing = std.testing;
...@@ -20,198 +14,16 @@ pub fn log10(x: anytype) @TypeOf(x) {...@@ -20,198 +14,16 @@ pub fn log10(x: anytype) @TypeOf(x) {
20 const T = @TypeOf(x);14 const T = @TypeOf(x);
21 switch (@typeInfo(T)) {15 switch (@typeInfo(T)) {
22 .ComptimeFloat => {16 .ComptimeFloat => {
23 return @as(comptime_float, log10_64(x));17 return @as(comptime_float, @log10(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 };
31 },18 },
19 .Float => return @log10(x),
32 .ComptimeInt => {20 .ComptimeInt => {
33 return @as(comptime_int, math.floor(log10_64(@as(f64, x))));21 return @as(comptime_int, @floor(@log10(@as(f64, x))));
34 },22 },
35 .Int => |IntType| switch (IntType.signedness) {23 .Int => |IntType| switch (IntType.signedness) {
36 .signed => @compileError("log10 not implemented for signed integers"),24 .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)))),
38 },26 },
39 else => @compileError("log10 not implemented for " ++ @typeName(T)),27 else => @compileError("log10 not implemented for " ++ @typeName(T)),
40 }28 }
41}29}
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,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
7const std = @import("../std.zig");1const std = @import("../std.zig");
8const math = std.math;2const math = std.math;
9const expect = std.testing.expect;3const expect = std.testing.expect;
10const maxInt = std.math.maxInt;
114
12/// Returns the base-2 logarithm of x.5/// Returns the base-2 logarithm of x.
13///6///
...@@ -20,15 +13,9 @@ pub fn log2(x: anytype) @TypeOf(x) {...@@ -20,15 +13,9 @@ pub fn log2(x: anytype) @TypeOf(x) {
20 const T = @TypeOf(x);13 const T = @TypeOf(x);
21 switch (@typeInfo(T)) {14 switch (@typeInfo(T)) {
22 .ComptimeFloat => {15 .ComptimeFloat => {
23 return @as(comptime_float, log2_64(x));16 return @as(comptime_float, @log2(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 };
31 },17 },
18 .Float => return @log2(x),
32 .ComptimeInt => comptime {19 .ComptimeInt => comptime {
33 var result = 0;20 var result = 0;
34 var x_shifted = x;21 var x_shifted = x;
...@@ -46,168 +33,7 @@ pub fn log2(x: anytype) @TypeOf(x) {...@@ -46,168 +33,7 @@ pub fn log2(x: anytype) @TypeOf(x) {
46 }33 }
47}34}
4835
49pub fn log2_32(x_: f32) f32 {36test "log2" {
50 const ivln2hi: f32 = 1.4428710938e+00;37 try expect(log2(@as(f32, 0.2)) == @log2(0.2));
51 const ivln2lo: f32 = -1.7605285393e-04;38 try expect(log2(@as(f64, 0.2)) == @log2(0.2));
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))));
213}39}
lib/std/math/nan.zig+12-19
...@@ -1,27 +1,20 @@...@@ -1,27 +1,20 @@
1const math = @import("../math.zig");1const math = @import("../math.zig");
22
3/// Returns the nan representation for type T.3/// Returns the nan representation for type T.
4pub fn nan(comptime T: type) T {4pub inline fn nan(comptime T: type) T {
5 return switch (T) {5 return switch (@typeInfo(T).Float.bits) {
6 f16 => math.nan_f16,6 16 => math.nan_f16,
7 f32 => math.nan_f32,7 32 => math.nan_f32,
8 f64 => math.nan_f64,8 64 => math.nan_f64,
9 f80 => math.nan_f80,9 80 => math.nan_f80,
10 f128 => math.nan_f128,10 128 => math.nan_f128,
11 else => @compileError("nan not implemented for " ++ @typeName(T)),11 else => @compileError("unreachable"),
12 };12 };
13}13}
1414
15/// Returns the signalling nan representation for type T.15/// Returns the signalling nan representation for type T.
16pub fn snan(comptime T: type) T {16/// Note: A signalling nan is identical to a standard right now by may have a different bit
17 // Note: A signalling nan is identical to a standard right now by may have a different bit17/// representation in the future when required.
18 // representation in the future when required.18pub inline fn snan(comptime T: type) T {
19 return switch (T) {19 return nan(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 };
27}20}
lib/std/math/pow.zig+6-6
...@@ -82,7 +82,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -82,7 +82,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
82 }82 }
83 // pow(x, +inf) = +0 for |x| < 183 // pow(x, +inf) = +0 for |x| < 1
84 // pow(x, -inf) = +0 for |x| > 184 // 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)) {
86 return 0;86 return 0;
87 }87 }
88 // pow(x, -inf) = +inf for |x| < 188 // pow(x, -inf) = +inf for |x| < 1
...@@ -108,14 +108,14 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -108,14 +108,14 @@ pub fn pow(comptime T: type, x: T, y: T) T {
108108
109 // special case sqrt109 // special case sqrt
110 if (y == 0.5) {110 if (y == 0.5) {
111 return math.sqrt(x);111 return @sqrt(x);
112 }112 }
113113
114 if (y == -0.5) {114 if (y == -0.5) {
115 return 1 / math.sqrt(x);115 return 1 / @sqrt(x);
116 }116 }
117117
118 const r1 = math.modf(math.fabs(y));118 const r1 = math.modf(@fabs(y));
119 var yi = r1.ipart;119 var yi = r1.ipart;
120 var yf = r1.fpart;120 var yf = r1.fpart;
121121
...@@ -123,7 +123,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -123,7 +123,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
123 return math.nan(T);123 return math.nan(T);
124 }124 }
125 if (yi >= 1 << (@typeInfo(T).Float.bits - 1)) {125 if (yi >= 1 << (@typeInfo(T).Float.bits - 1)) {
126 return math.exp(y * math.ln(x));126 return @exp(y * @log(x));
127 }127 }
128128
129 // a = a1 * 2^ae129 // a = a1 * 2^ae
...@@ -136,7 +136,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {...@@ -136,7 +136,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
136 yf -= 1;136 yf -= 1;
137 yi += 1;137 yi += 1;
138 }138 }
139 a1 = math.exp(yf * math.ln(x));139 a1 = @exp(yf * @log(x));
140 }140 }
141141
142 // a *= x^yi142 // 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 {...@@ -33,7 +33,7 @@ pub fn next_f64(random: Random, comptime tables: ZigTable) f64 {
33 };33 };
3434
35 const x = u * tables.x[i];35 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
38 // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])38 // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])
39 if (test_x < tables.x[i + 1]) {39 if (test_x < tables.x[i + 1]) {
...@@ -106,18 +106,18 @@ const norm_r = 3.6541528853610088;...@@ -106,18 +106,18 @@ const norm_r = 3.6541528853610088;
106const norm_v = 0.00492867323399;106const norm_v = 0.00492867323399;
107107
108fn norm_f(x: f64) f64 {108fn norm_f(x: f64) f64 {
109 return math.exp(-x * x / 2.0);109 return @exp(-x * x / 2.0);
110}110}
111fn norm_f_inv(y: f64) f64 {111fn norm_f_inv(y: f64) f64 {
112 return math.sqrt(-2.0 * math.ln(y));112 return @sqrt(-2.0 * @log(y));
113}113}
114fn norm_zero_case(random: Random, u: f64) f64 {114fn norm_zero_case(random: Random, u: f64) f64 {
115 var x: f64 = 1;115 var x: f64 = 1;
116 var y: f64 = 0;116 var y: f64 = 0;
117117
118 while (-2.0 * y < x * x) {118 while (-2.0 * y < x * x) {
119 x = math.ln(random.float(f64)) / norm_r;119 x = @log(random.float(f64)) / norm_r;
120 y = math.ln(random.float(f64));120 y = @log(random.float(f64));
121 }121 }
122122
123 if (u < 0) {123 if (u < 0) {
...@@ -151,13 +151,13 @@ const exp_r = 7.69711747013104972;...@@ -151,13 +151,13 @@ const exp_r = 7.69711747013104972;
151const exp_v = 0.0039496598225815571993;151const exp_v = 0.0039496598225815571993;
152152
153fn exp_f(x: f64) f64 {153fn exp_f(x: f64) f64 {
154 return math.exp(-x);154 return @exp(-x);
155}155}
156fn exp_f_inv(y: f64) f64 {156fn exp_f_inv(y: f64) f64 {
157 return -math.ln(y);157 return -@log(y);
158}158}
159fn exp_zero_case(random: Random, _: f64) f64 {159fn exp_zero_case(random: Random, _: f64) f64 {
160 return exp_r - math.ln(random.float(f64));160 return exp_r - @log(random.float(f64));
161}161}
162162
163test "exp dist sanity" {163test "exp dist sanity" {
lib/std/special/c.zig-591
...@@ -12,7 +12,6 @@ const maxInt = std.math.maxInt;...@@ -12,7 +12,6 @@ const maxInt = std.math.maxInt;
12const native_os = builtin.os.tag;12const native_os = builtin.os.tag;
13const native_arch = builtin.cpu.arch;13const native_arch = builtin.cpu.arch;
14const native_abi = builtin.abi;14const native_abi = builtin.abi;
15const long_double_is_f128 = builtin.target.longDoubleIs(f128);
1615
17const is_wasm = switch (native_arch) {16const is_wasm = switch (native_arch) {
18 .wasm32, .wasm64 => true,17 .wasm32, .wasm64 => true,
...@@ -55,53 +54,6 @@ comptime {...@@ -55,53 +54,6 @@ comptime {
55 } else if (is_msvc) {54 } else if (is_msvc) {
56 @export(_fltused, .{ .name = "_fltused", .linkage = .Strong });55 @export(_fltused, .{ .name = "_fltused", .linkage = .Strong });
57 }56 }
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 });
105}57}
10658
107// Avoid dragging in the runtime safety mechanisms into this .o file,59// Avoid dragging in the runtime safety mechanisms into this .o file,
...@@ -352,549 +304,6 @@ test "strncmp" {...@@ -352,549 +304,6 @@ test "strncmp" {
352 try std.testing.expect(strncmp("\xff", "\x02", 1) == 253);304 try std.testing.expect(strncmp("\xff", "\x02", 1) == 253);
353}305}
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
898// TODO we should be able to put this directly in std/linux/x86_64.zig but307// TODO we should be able to put this directly in std/linux/x86_64.zig but
899// it causes a segfault in release mode. this is a workaround of calling it308// it causes a segfault in release mode. this is a workaround of calling it
900// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.309// 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;...@@ -8,6 +8,7 @@ const abi = builtin.abi;
8const is_gnu = abi.isGnu();8const is_gnu = abi.isGnu();
9const is_mingw = os_tag == .windows and is_gnu;9const is_mingw = os_tag == .windows and is_gnu;
10const is_darwin = std.Target.Os.Tag.isDarwin(os_tag);10const is_darwin = std.Target.Os.Tag.isDarwin(os_tag);
11const is_ppc = arch.isPPC() or arch.isPPC64();
1112
12const linkage = if (is_test)13const linkage = if (is_test)
13 std.builtin.GlobalLinkage.Internal14 std.builtin.GlobalLinkage.Internal
...@@ -19,9 +20,6 @@ const strong_linkage = if (is_test)...@@ -19,9 +20,6 @@ const strong_linkage = if (is_test)
19else20else
20 std.builtin.GlobalLinkage.Strong;21 std.builtin.GlobalLinkage.Strong;
2122
22const long_double_is_f80 = builtin.target.longDoubleIs(f80);
23const long_double_is_f128 = builtin.target.longDoubleIs(f128);
24
25comptime {23comptime {
26 // These files do their own comptime exporting logic.24 // These files do their own comptime exporting logic.
27 _ = @import("compiler_rt/atomics.zig");25 _ = @import("compiler_rt/atomics.zig");
...@@ -726,42 +724,25 @@ comptime {...@@ -726,42 +724,25 @@ comptime {
726 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });724 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
727 }725 }
728726
729 if (!is_test) {727 mathExport("ceil", @import("./compiler_rt/ceil.zig"), true);
730 if (long_double_is_f80) {728 mathExport("cos", @import("./compiler_rt/cos.zig"), true);
731 @export(fmodx, .{ .name = "fmodl", .linkage = linkage });729 mathExport("exp", @import("./compiler_rt/exp.zig"), true);
732 } else if (long_double_is_f128) {730 mathExport("exp2", @import("./compiler_rt/exp2.zig"), true);
733 @export(fmodq, .{ .name = "fmodl", .linkage = linkage });731 mathExport("fabs", @import("./compiler_rt/fabs.zig"), true);
734 } else {732 mathExport("floor", @import("./compiler_rt/floor.zig"), true);
735 @export(fmodl, .{ .name = "fmodl", .linkage = linkage });733 mathExport("fma", @import("./compiler_rt/fma.zig"), true);
736 }734 mathExport("fmax", @import("./compiler_rt/fmax.zig"), true);
737 if (long_double_is_f80 or builtin.zig_backend == .stage1) {735 mathExport("fmin", @import("./compiler_rt/fmin.zig"), true);
738 // TODO: https://github.com/ziglang/zig/issues/11161736 mathExport("fmod", @import("./compiler_rt/fmod.zig"), true);
739 @export(fmodx, .{ .name = "fmodx", .linkage = linkage });737 mathExport("log", @import("./compiler_rt/log.zig"), true);
740 }738 mathExport("log10", @import("./compiler_rt/log10.zig"), true);
741 @export(fmodq, .{ .name = "fmodq", .linkage = linkage });739 mathExport("log2", @import("./compiler_rt/log2.zig"), true);
742740 mathExport("round", @import("./compiler_rt/round.zig"), true);
743 @export(floorf, .{ .name = "floorf", .linkage = linkage });741 mathExport("sin", @import("./compiler_rt/sin.zig"), true);
744 @export(floor, .{ .name = "floor", .linkage = linkage });742 mathExport("sincos", @import("./compiler_rt/sincos.zig"), true);
745 @export(floorl, .{ .name = "floorl", .linkage = linkage });743 mathExport("sqrt", @import("./compiler_rt/sqrt.zig"), true);
746744 mathExport("tan", @import("./compiler_rt/tan.zig"), false);
747 @export(ceilf, .{ .name = "ceilf", .linkage = linkage });745 mathExport("trunc", @import("./compiler_rt/trunc.zig"), true);
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 }
765746
766 if (arch.isSPARC()) {747 if (arch.isSPARC()) {
767 // SPARC systems use a different naming scheme748 // SPARC systems use a different naming scheme
...@@ -815,7 +796,7 @@ comptime {...@@ -815,7 +796,7 @@ comptime {
815 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });796 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
816 }797 }
817798
818 if ((arch.isPPC() or arch.isPPC64()) and !is_test) {799 if (is_ppc and !is_test) {
819 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });800 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });
820 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });801 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });
821 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });802 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });
...@@ -840,65 +821,53 @@ comptime {...@@ -840,65 +821,53 @@ comptime {
840 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });821 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });
841 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });822 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });
842 @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage });823 @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");
878 }824 }
879 return math.floor(x);
880}825}
881826
882fn ceilf(x: f32) callconv(.C) f32 {827inline fn mathExport(double_name: []const u8, comptime import: type, is_standard: bool) void {
883 return math.ceil(x);828 const half_name = "__" ++ double_name ++ "h";
884}829 const half_fn = @field(import, half_name);
885fn ceil(x: f64) callconv(.C) f64 {830 const float_name = double_name ++ "f";
886 return math.ceil(x);831 const float_fn = @field(import, float_name);
887}832 const double_fn = @field(import, double_name);
888fn ceill(x: c_longdouble) callconv(.C) c_longdouble {833 const long_double_name = double_name ++ "l";
889 if (!long_double_is_f128) {834 const xf80_name = "__" ++ double_name ++ "x";
890 @panic("TODO implement this");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 }
891 }865 }
892 return math.ceil(x);
893}
894866
895const fmodq = @import("compiler_rt/fmodq.zig").fmodq;867 if (is_ppc and is_standard) {
896const fmodx = @import("compiler_rt/fmodx.zig").fmodx;868 // LLVM PPC backend lowers f128 ops with the suffix `f128` instead of `l`.
897fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble {869 @export(quad_fn, .{ .name = double_name ++ "f128", .linkage = linkage });
898 if (!long_double_is_f128) {
899 @panic("TODO implement this");
900 }870 }
901 return @floatCast(c_longdouble, fmodq(x, y));
902}871}
903872
904// Avoid dragging in the runtime safety mechanisms into this .o file,873// 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 {...@@ -30,9 +30,9 @@ fn test__divxf3(a: f80, b: f80) !void {
30 const x_minus_eps = @bitCast(f80, (@bitCast(u80, x) - 1) | integerBit);30 const x_minus_eps = @bitCast(f80, (@bitCast(u80, x) - 1) | integerBit);
3131
32 // Make sure result is more accurate than the adjacent floats32 // Make sure result is more accurate than the adjacent floats
33 const err_x = std.math.fabs(@mulAdd(f80, x, b, -a));33 const err_x = @fabs(@mulAdd(f80, x, b, -a));
34 const err_x_plus_eps = std.math.fabs(@mulAdd(f80, x_plus_eps, b, -a));34 const err_x_plus_eps = @fabs(@mulAdd(f80, x_plus_eps, b, -a));
35 const err_x_minus_eps = std.math.fabs(@mulAdd(f80, x_minus_eps, b, -a));35 const err_x_minus_eps = @fabs(@mulAdd(f80, x_minus_eps, b, -a));
3636
37 try testing.expect(err_x_minus_eps > err_x);37 try testing.expect(err_x_minus_eps > err_x);
38 try testing.expect(err_x_plus_eps > err_x);38 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 @@...@@ -1,24 +1,24 @@
1const std = @import("std");1const std = @import("std");
2const fmodq = @import("fmodq.zig");2const fmod = @import("fmod.zig");
3const testing = std.testing;3const testing = std.testing;
44
5fn test_fmodq(a: f128, b: f128, exp: f128) !void {5fn test_fmodq(a: f128, b: f128, exp: f128) !void {
6 const res = fmodq.fmodq(a, b);6 const res = fmod.fmodq(a, b);
7 try testing.expect(exp == res);7 try testing.expect(exp == res);
8}8}
99
10fn test_fmodq_nans() !void {10fn test_fmodq_nans() !void {
11 try testing.expect(std.math.isNan(fmodq.fmodq(1.0, std.math.nan(f128))));11 try testing.expect(std.math.isNan(fmod.fmodq(1.0, std.math.nan(f128))));
12 try testing.expect(std.math.isNan(fmodq.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(fmodq.fmodq(std.math.nan(f128), 1.0)));13 try testing.expect(std.math.isNan(fmod.fmodq(std.math.nan(f128), 1.0)));
14 try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.nan(f128), 1.0)));14 try testing.expect(std.math.isNan(fmod.fmodq(-std.math.nan(f128), 1.0)));
15}15}
1616
17fn test_fmodq_infs() !void {17fn test_fmodq_infs() !void {
18 try testing.expect(fmodq.fmodq(1.0, std.math.inf(f128)) == 1.0);18 try testing.expect(fmod.fmodq(1.0, std.math.inf(f128)) == 1.0);
19 try testing.expect(fmodq.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(fmodq.fmodq(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(fmodq.fmodq(-std.math.inf(f128), 1.0)));21 try testing.expect(std.math.isNan(fmod.fmodq(-std.math.inf(f128), 1.0)));
22}22}
2323
24test "fmodq" {24test "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 @@...@@ -1,24 +1,24 @@
1const std = @import("std");1const std = @import("std");
2const fmodx = @import("fmodx.zig");2const fmod = @import("fmod.zig");
3const testing = std.testing;3const testing = std.testing;
44
5fn test_fmodx(a: f80, b: f80, exp: f80) !void {5fn test_fmodx(a: f80, b: f80, exp: f80) !void {
6 const res = fmodx.fmodx(a, b);6 const res = fmod.__fmodx(a, b);
7 try testing.expect(exp == res);7 try testing.expect(exp == res);
8}8}
99
10fn test_fmodx_nans() !void {10fn test_fmodx_nans() !void {
11 try testing.expect(std.math.isNan(fmodx.fmodx(1.0, std.math.nan(f80))));11 try testing.expect(std.math.isNan(fmod.__fmodx(1.0, std.math.nan(f80))));
12 try testing.expect(std.math.isNan(fmodx.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(fmodx.fmodx(std.math.nan(f80), 1.0)));13 try testing.expect(std.math.isNan(fmod.__fmodx(std.math.nan(f80), 1.0)));
14 try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.nan(f80), 1.0)));14 try testing.expect(std.math.isNan(fmod.__fmodx(-std.math.nan(f80), 1.0)));
15}15}
1616
17fn test_fmodx_infs() !void {17fn test_fmodx_infs() !void {
18 try testing.expect(fmodx.fmodx(1.0, std.math.inf(f80)) == 1.0);18 try testing.expect(fmod.__fmodx(1.0, std.math.inf(f80)) == 1.0);
19 try testing.expect(fmodx.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(fmodx.fmodx(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(fmodx.fmodx(-std.math.inf(f80), 1.0)));21 try testing.expect(std.math.isNan(fmod.__fmodx(-std.math.inf(f80), 1.0)));
22}22}
2323
24test "fmodx" {24test "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...@@ -265,7 +265,7 @@ pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance
265test "expectApproxEqRel" {265test "expectApproxEqRel" {
266 inline for ([_]type{ f16, f32, f64, f128 }) |T| {266 inline for ([_]type{ f16, f32, f64, f128 }) |T| {
267 const eps_value = comptime math.epsilon(T);267 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
270 const pos_x: T = 12.0;270 const pos_x: T = 12.0;
271 const pos_y: T = pos_x + 2 * eps_value;271 const pos_y: T = pos_x + 2 * eps_value;
src/Air.zig+6-2
...@@ -249,12 +249,15 @@ pub const Inst = struct {...@@ -249,12 +249,15 @@ pub const Inst = struct {
249 /// Square root of a floating point number.249 /// Square root of a floating point number.
250 /// Uses the `un_op` field.250 /// Uses the `un_op` field.
251 sqrt,251 sqrt,
252 /// Sine a floating point number.252 /// Sine function on a floating point number.
253 /// Uses the `un_op` field.253 /// Uses the `un_op` field.
254 sin,254 sin,
255 /// Cosine a floating point number.255 /// Cosine function on a floating point number.
256 /// Uses the `un_op` field.256 /// Uses the `un_op` field.
257 cos,257 cos,
258 /// Tangent function on a floating point number.
259 /// Uses the `un_op` field.
260 tan,
258 /// Base e exponential of a floating point number.261 /// Base e exponential of a floating point number.
259 /// Uses the `un_op` field.262 /// Uses the `un_op` field.
260 exp,263 exp,
...@@ -921,6 +924,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -921,6 +924,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
921 .sqrt,924 .sqrt,
922 .sin,925 .sin,
923 .cos,926 .cos,
927 .tan,
924 .exp,928 .exp,
925 .exp2,929 .exp2,
926 .log,930 .log,
src/AstGen.zig+4-2
...@@ -2237,7 +2237,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2237,7 +2237,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2237 .field_call_bind,2237 .field_call_bind,
2238 .field_ptr_named,2238 .field_ptr_named,
2239 .field_val_named,2239 .field_val_named,
2240 .field_call_bind_named,
2241 .func,2240 .func,
2242 .func_inferred,2241 .func_inferred,
2243 .int,2242 .int,
...@@ -2329,6 +2328,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2329,6 +2328,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2329 .sqrt,2328 .sqrt,
2330 .sin,2329 .sin,
2331 .cos,2330 .cos,
2331 .tan,
2332 .exp,2332 .exp,
2333 .exp2,2333 .exp2,
2334 .log,2334 .log,
...@@ -7259,6 +7259,7 @@ fn builtinCall(...@@ -7259,6 +7259,7 @@ fn builtinCall(
7259 .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt),7259 .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt),
7260 .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin),7260 .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin),
7261 .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos),7261 .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos),
7262 .tan => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tan),
7262 .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp),7263 .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp),
7263 .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2),7264 .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2),
7264 .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log),7265 .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log),
...@@ -7947,7 +7948,8 @@ fn calleeExpr(...@@ -7947,7 +7948,8 @@ fn calleeExpr(
7947 if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {7948 if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {
7948 const lhs = try expr(gz, scope, .ref, params[0]);7949 const lhs = try expr(gz, scope, .ref, params[0]);
7949 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);7950 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),
7951 .lhs = lhs,7953 .lhs = lhs,
7952 .field_name = field_name,7954 .field_name = field_name,
7953 });7955 });
src/BuiltinFn.zig+8
...@@ -89,6 +89,7 @@ pub const Tag = enum {...@@ -89,6 +89,7 @@ pub const Tag = enum {
89 sqrt,89 sqrt,
90 sin,90 sin,
91 cos,91 cos,
92 tan,
92 exp,93 exp,
93 exp2,94 exp2,
94 log,95 log,
...@@ -771,6 +772,13 @@ pub const list = list: {...@@ -771,6 +772,13 @@ pub const list = list: {
771 .param_count = 1,772 .param_count = 1,
772 },773 },
773 },774 },
775 .{
776 "@tan",
777 .{
778 .tag = .tan,
779 .param_count = 1,
780 },
781 },
774 .{782 .{
775 "@exp",783 "@exp",
776 .{784 .{
src/Liveness.zig+1
...@@ -422,6 +422,7 @@ fn analyzeInst(...@@ -422,6 +422,7 @@ fn analyzeInst(
422 .sqrt,422 .sqrt,
423 .sin,423 .sin,
424 .cos,424 .cos,
425 .tan,
425 .exp,426 .exp,
426 .exp2,427 .exp2,
427 .log,428 .log,
src/Sema.zig+37-37
...@@ -743,7 +743,6 @@ fn analyzeBodyInner(...@@ -743,7 +743,6 @@ fn analyzeBodyInner(
743 .field_val => try sema.zirFieldVal(block, inst),743 .field_val => try sema.zirFieldVal(block, inst),
744 .field_val_named => try sema.zirFieldValNamed(block, inst),744 .field_val_named => try sema.zirFieldValNamed(block, inst),
745 .field_call_bind => try sema.zirFieldCallBind(block, inst),745 .field_call_bind => try sema.zirFieldCallBind(block, inst),
746 .field_call_bind_named => try sema.zirFieldCallBindNamed(block, inst),
747 .func => try sema.zirFunc(block, inst, false),746 .func => try sema.zirFunc(block, inst, false),
748 .func_inferred => try sema.zirFunc(block, inst, true),747 .func_inferred => try sema.zirFunc(block, inst, true),
749 .import => try sema.zirImport(block, inst),748 .import => try sema.zirImport(block, inst),
...@@ -855,6 +854,7 @@ fn analyzeBodyInner(...@@ -855,6 +854,7 @@ fn analyzeBodyInner(
855 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),854 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
856 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin),855 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin),
857 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos),856 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos),
857 .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan),
858 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp),858 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp),
859 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),859 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),
860 .log => try sema.zirUnaryMath(block, inst, .log, Value.log),860 .log => try sema.zirUnaryMath(block, inst, .log, Value.log),
...@@ -910,35 +910,36 @@ fn analyzeBodyInner(...@@ -910,35 +910,36 @@ fn analyzeBodyInner(
910 const extended = datas[inst].extended;910 const extended = datas[inst].extended;
911 break :ext switch (extended.opcode) {911 break :ext switch (extended.opcode) {
912 // zig fmt: off912 // zig fmt: off
913 .func => try sema.zirFuncExtended( block, extended, inst),913 .func => try sema.zirFuncExtended( block, extended, inst),
914 .variable => try sema.zirVarExtended( block, extended),914 .variable => try sema.zirVarExtended( block, extended),
915 .struct_decl => try sema.zirStructDecl( block, extended, inst),915 .struct_decl => try sema.zirStructDecl( block, extended, inst),
916 .enum_decl => try sema.zirEnumDecl( block, extended),916 .enum_decl => try sema.zirEnumDecl( block, extended),
917 .union_decl => try sema.zirUnionDecl( block, extended, inst),917 .union_decl => try sema.zirUnionDecl( block, extended, inst),
918 .opaque_decl => try sema.zirOpaqueDecl( block, extended),918 .opaque_decl => try sema.zirOpaqueDecl( block, extended),
919 .ret_ptr => try sema.zirRetPtr( block, extended),919 .ret_ptr => try sema.zirRetPtr( block, extended),
920 .ret_type => try sema.zirRetType( block, extended),920 .ret_type => try sema.zirRetType( block, extended),
921 .this => try sema.zirThis( block, extended),921 .this => try sema.zirThis( block, extended),
922 .ret_addr => try sema.zirRetAddr( block, extended),922 .ret_addr => try sema.zirRetAddr( block, extended),
923 .builtin_src => try sema.zirBuiltinSrc( block, extended),923 .builtin_src => try sema.zirBuiltinSrc( block, extended),
924 .error_return_trace => try sema.zirErrorReturnTrace( block, extended),924 .error_return_trace => try sema.zirErrorReturnTrace( block, extended),
925 .frame => try sema.zirFrame( block, extended),925 .frame => try sema.zirFrame( block, extended),
926 .frame_address => try sema.zirFrameAddress( block, extended),926 .frame_address => try sema.zirFrameAddress( block, extended),
927 .alloc => try sema.zirAllocExtended( block, extended),927 .alloc => try sema.zirAllocExtended( block, extended),
928 .builtin_extern => try sema.zirBuiltinExtern( block, extended),928 .builtin_extern => try sema.zirBuiltinExtern( block, extended),
929 .@"asm" => try sema.zirAsm( block, extended),929 .@"asm" => try sema.zirAsm( block, extended),
930 .typeof_peer => try sema.zirTypeofPeer( block, extended),930 .typeof_peer => try sema.zirTypeofPeer( block, extended),
931 .compile_log => try sema.zirCompileLog( block, extended),931 .compile_log => try sema.zirCompileLog( block, extended),
932 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),932 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
933 .sub_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),934 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
935 .shl_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),936 .c_undef => try sema.zirCUndef( block, extended),
937 .c_include => try sema.zirCInclude( block, extended),937 .c_include => try sema.zirCInclude( block, extended),
938 .c_define => try sema.zirCDefine( block, extended),938 .c_define => try sema.zirCDefine( block, extended),
939 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),939 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
940 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),940 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
941 .prefetch => try sema.zirPrefetch( block, extended),941 .prefetch => try sema.zirPrefetch( block, extended),
942 .field_call_bind_named => try sema.zirFieldCallBindNamed(block, extended),
942 // zig fmt: on943 // zig fmt: on
943 .dbg_block_begin => {944 .dbg_block_begin => {
944 dbg_block_begins += 1;945 dbg_block_begins += 1;
...@@ -6938,14 +6939,13 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -6938,14 +6939,13 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
6938 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);6939 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
6939}6940}
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 {
6942 const tracy = trace(@src());6943 const tracy = trace(@src());
6943 defer tracy.end();6944 defer tracy.end();
69446945
6945 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6946 const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data;
6946 const src = inst_data.src();6947 const src: LazySrcLoc = .{ .node_offset = extra.node };
6947 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };6948 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
6948 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
6949 const object_ptr = sema.resolveInst(extra.lhs);6949 const object_ptr = sema.resolveInst(extra.lhs);
6950 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);6950 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
6951 return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src);6951 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!...@@ -14051,7 +14051,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
14051 const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) {14051 const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) {
14052 error.FloatCannotFit => {14052 error.FloatCannotFit => {
14053 return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{14053 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)),
14055 dest_ty.fmt(sema.mod),14055 dest_ty.fmt(sema.mod),
14056 });14056 });
14057 },14057 },
...@@ -18371,7 +18371,7 @@ fn coerce(...@@ -18371,7 +18371,7 @@ fn coerce(
18371 }18371 }
18372 const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) {18372 const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) {
18373 error.FloatCannotFit => {18373 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) });
18375 },18375 },
18376 else => |e| return e,18376 else => |e| return e,
18377 };18377 };
src/Zir.zig+21-12
...@@ -407,15 +407,6 @@ pub const Inst = struct {...@@ -407,15 +407,6 @@ pub const Inst = struct {
407 /// The field name is a comptime instruction. Used by @field.407 /// The field name is a comptime instruction. Used by @field.
408 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.408 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
409 field_val_named,409 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,
419 /// Returns a function type, or a function instance, depending on whether410 /// Returns a function type, or a function instance, depending on whether
420 /// the body_len is 0. Calling convention is auto.411 /// the body_len is 0. Calling convention is auto.
421 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.412 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
...@@ -797,6 +788,8 @@ pub const Inst = struct {...@@ -797,6 +788,8 @@ pub const Inst = struct {
797 sin,788 sin,
798 /// Implement builtin `@cos`. Uses `un_node`.789 /// Implement builtin `@cos`. Uses `un_node`.
799 cos,790 cos,
791 /// Implement builtin `@tan`. Uses `un_node`.
792 tan,
800 /// Implement builtin `@exp`. Uses `un_node`.793 /// Implement builtin `@exp`. Uses `un_node`.
801 exp,794 exp,
802 /// Implement builtin `@exp2`. Uses `un_node`.795 /// Implement builtin `@exp2`. Uses `un_node`.
...@@ -1069,7 +1062,6 @@ pub const Inst = struct {...@@ -1069,7 +1062,6 @@ pub const Inst = struct {
1069 .field_call_bind,1062 .field_call_bind,
1070 .field_ptr_named,1063 .field_ptr_named,
1071 .field_val_named,1064 .field_val_named,
1072 .field_call_bind_named,
1073 .func,1065 .func,
1074 .func_inferred,1066 .func_inferred,
1075 .has_decl,1067 .has_decl,
...@@ -1179,6 +1171,7 @@ pub const Inst = struct {...@@ -1179,6 +1171,7 @@ pub const Inst = struct {
1179 .sqrt,1171 .sqrt,
1180 .sin,1172 .sin,
1181 .cos,1173 .cos,
1174 .tan,
1182 .exp,1175 .exp,
1183 .exp2,1176 .exp2,
1184 .log,1177 .log,
...@@ -1358,7 +1351,6 @@ pub const Inst = struct {...@@ -1358,7 +1351,6 @@ pub const Inst = struct {
1358 .field_call_bind,1351 .field_call_bind,
1359 .field_ptr_named,1352 .field_ptr_named,
1360 .field_val_named,1353 .field_val_named,
1361 .field_call_bind_named,
1362 .func,1354 .func,
1363 .func_inferred,1355 .func_inferred,
1364 .has_decl,1356 .has_decl,
...@@ -1451,6 +1443,7 @@ pub const Inst = struct {...@@ -1451,6 +1443,7 @@ pub const Inst = struct {
1451 .sqrt,1443 .sqrt,
1452 .sin,1444 .sin,
1453 .cos,1445 .cos,
1446 .tan,
1454 .exp,1447 .exp,
1455 .exp2,1448 .exp2,
1456 .log,1449 .log,
...@@ -1607,7 +1600,6 @@ pub const Inst = struct {...@@ -1607,7 +1600,6 @@ pub const Inst = struct {
1607 .field_ptr_named = .pl_node,1600 .field_ptr_named = .pl_node,
1608 .field_val_named = .pl_node,1601 .field_val_named = .pl_node,
1609 .field_call_bind = .pl_node,1602 .field_call_bind = .pl_node,
1610 .field_call_bind_named = .pl_node,
1611 .func = .pl_node,1603 .func = .pl_node,
1612 .func_inferred = .pl_node,1604 .func_inferred = .pl_node,
1613 .import = .str_tok,1605 .import = .str_tok,
...@@ -1713,6 +1705,7 @@ pub const Inst = struct {...@@ -1713,6 +1705,7 @@ pub const Inst = struct {
1713 .sqrt = .un_node,1705 .sqrt = .un_node,
1714 .sin = .un_node,1706 .sin = .un_node,
1715 .cos = .un_node,1707 .cos = .un_node,
1708 .tan = .un_node,
1716 .exp = .un_node,1709 .exp = .un_node,
1717 .exp2 = .un_node,1710 .exp2 = .un_node,
1718 .log = .un_node,1711 .log = .un_node,
...@@ -1928,6 +1921,16 @@ pub const Inst = struct {...@@ -1928,6 +1921,16 @@ pub const Inst = struct {
1928 dbg_block_begin,1921 dbg_block_begin,
1929 /// Marks the end of a semantic scope for debug info variables.1922 /// Marks the end of a semantic scope for debug info variables.
1930 dbg_block_end,1923 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
1932 pub const InstData = struct {1935 pub const InstData = struct {
1933 opcode: Extended,1936 opcode: Extended,
...@@ -2963,6 +2966,12 @@ pub const Inst = struct {...@@ -2963,6 +2966,12 @@ pub const Inst = struct {
2963 field_name: Ref,2966 field_name: Ref,
2964 };2967 };
29652968
2969 pub const FieldNamedNode = struct {
2970 node: i32,
2971 lhs: Ref,
2972 field_name: Ref,
2973 };
2974
2966 pub const As = struct {2975 pub const As = struct {
2967 dest_type: Ref,2976 dest_type: Ref,
2968 operand: Ref,2977 operand: Ref,
src/arch/aarch64/CodeGen.zig+1
...@@ -533,6 +533,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -533,6 +533,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
533 .sqrt,533 .sqrt,
534 .sin,534 .sin,
535 .cos,535 .cos,
536 .tan,
536 .exp,537 .exp,
537 .exp2,538 .exp2,
538 .log,539 .log,
src/arch/arm/CodeGen.zig+1
...@@ -571,6 +571,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -571,6 +571,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
571 .sqrt,571 .sqrt,
572 .sin,572 .sin,
573 .cos,573 .cos,
574 .tan,
574 .exp,575 .exp,
575 .exp2,576 .exp2,
576 .log,577 .log,
src/arch/riscv64/CodeGen.zig+1
...@@ -500,6 +500,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -500,6 +500,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
500 .sqrt,500 .sqrt,
501 .sin,501 .sin,
502 .cos,502 .cos,
503 .tan,
503 .exp,504 .exp,
504 .exp2,505 .exp2,
505 .log,506 .log,
src/arch/sparcv9/CodeGen.zig+1
...@@ -451,6 +451,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -451,6 +451,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
451 .sqrt,451 .sqrt,
452 .sin,452 .sin,
453 .cos,453 .cos,
454 .tan,
454 .exp,455 .exp,
455 .exp2,456 .exp2,
456 .log,457 .log,
src/arch/wasm/CodeGen.zig+1
...@@ -1559,6 +1559,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1559,6 +1559,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1559 .sqrt,1559 .sqrt,
1560 .sin,1560 .sin,
1561 .cos,1561 .cos,
1562 .tan,
1562 .exp,1563 .exp,
1563 .exp2,1564 .exp2,
1564 .log,1565 .log,
src/arch/x86_64/CodeGen.zig+1
...@@ -656,6 +656,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -656,6 +656,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
656 .sqrt,656 .sqrt,
657 .sin,657 .sin,
658 .cos,658 .cos,
659 .tan,
659 .exp,660 .exp,
660 .exp2,661 .exp2,
661 .log,662 .log,
src/codegen/c.zig+1
...@@ -1749,6 +1749,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1749,6 +1749,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1749 .sqrt,1749 .sqrt,
1750 .sin,1750 .sin,
1751 .cos,1751 .cos,
1752 .tan,
1752 .exp,1753 .exp,
1753 .exp2,1754 .exp2,
1754 .log,1755 .log,
src/codegen/llvm.zig+298-145
...@@ -3518,19 +3518,20 @@ pub const FuncGen = struct {...@@ -3518,19 +3518,20 @@ pub const FuncGen = struct {
3518 .shr => try self.airShr(inst, false),3518 .shr => try self.airShr(inst, false),
3519 .shr_exact => try self.airShr(inst, true),3519 .shr_exact => try self.airShr(inst, true),
35203520
3521 .sqrt => try self.airUnaryOp(inst, "sqrt"),3521 .sqrt => try self.airUnaryOp(inst, .sqrt),
3522 .sin => try self.airUnaryOp(inst, "sin"),3522 .sin => try self.airUnaryOp(inst, .sin),
3523 .cos => try self.airUnaryOp(inst, "cos"),3523 .cos => try self.airUnaryOp(inst, .cos),
3524 .exp => try self.airUnaryOp(inst, "exp"),3524 .tan => try self.airUnaryOp(inst, .tan),
3525 .exp2 => try self.airUnaryOp(inst, "exp2"),3525 .exp => try self.airUnaryOp(inst, .exp),
3526 .log => try self.airUnaryOp(inst, "log"),3526 .exp2 => try self.airUnaryOp(inst, .exp2),
3527 .log2 => try self.airUnaryOp(inst, "log2"),3527 .log => try self.airUnaryOp(inst, .log),
3528 .log10 => try self.airUnaryOp(inst, "log10"),3528 .log2 => try self.airUnaryOp(inst, .log2),
3529 .fabs => try self.airUnaryOp(inst, "fabs"),3529 .log10 => try self.airUnaryOp(inst, .log10),
3530 .floor => try self.airUnaryOp(inst, "floor"),3530 .fabs => try self.airUnaryOp(inst, .fabs),
3531 .ceil => try self.airUnaryOp(inst, "ceil"),3531 .floor => try self.airUnaryOp(inst, .floor),
3532 .round => try self.airUnaryOp(inst, "round"),3532 .ceil => try self.airUnaryOp(inst, .ceil),
3533 .trunc_float => try self.airUnaryOp(inst, "trunc"),3533 .round => try self.airUnaryOp(inst, .round),
3534 .trunc_float => try self.airUnaryOp(inst, .trunc),
35343535
3535 .cmp_eq => try self.airCmp(inst, .eq),3536 .cmp_eq => try self.airCmp(inst, .eq),
3536 .cmp_gt => try self.airCmp(inst, .gt),3537 .cmp_gt => try self.airCmp(inst, .gt),
...@@ -3905,7 +3906,7 @@ pub const FuncGen = struct {...@@ -3905,7 +3906,7 @@ pub const FuncGen = struct {
3905 rhs: *const llvm.Value,3906 rhs: *const llvm.Value,
3906 operand_ty: Type,3907 operand_ty: Type,
3907 op: math.CompareOperator,3908 op: math.CompareOperator,
3908 ) *const llvm.Value {3909 ) Allocator.Error!*const llvm.Value {
3909 var int_buffer: Type.Payload.Bits = undefined;3910 var int_buffer: Type.Payload.Bits = undefined;
3910 var opt_buffer: Type.Payload.ElemType = undefined;3911 var opt_buffer: Type.Payload.ElemType = undefined;
39113912
...@@ -3947,7 +3948,7 @@ pub const FuncGen = struct {...@@ -3947,7 +3948,7 @@ pub const FuncGen = struct {
3947 self.builder.positionBuilderAtEnd(both_pl_block);3948 self.builder.positionBuilderAtEnd(both_pl_block);
3948 const lhs_payload = self.optPayloadHandle(lhs, is_by_ref);3949 const lhs_payload = self.optPayloadHandle(lhs, is_by_ref);
3949 const rhs_payload = self.optPayloadHandle(rhs, is_by_ref);3950 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);
3951 _ = self.builder.buildBr(end_block);3952 _ = self.builder.buildBr(end_block);
3952 const both_pl_block_end = self.builder.getInsertBlock();3953 const both_pl_block_end = self.builder.getInsertBlock();
39533954
...@@ -3983,17 +3984,7 @@ pub const FuncGen = struct {...@@ -3983,17 +3984,7 @@ pub const FuncGen = struct {
3983 );3984 );
3984 return phi_node;3985 return phi_node;
3985 },3986 },
3986 .Float => {3987 .Float => return self.buildFloatCmp(op, operand_ty, .{ lhs, rhs }),
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 },
3997 else => unreachable,3988 else => unreachable,
3998 };3989 };
3999 const is_signed = int_ty.isSignedInt();3990 const is_signed = int_ty.isSignedInt();
...@@ -5221,7 +5212,7 @@ pub const FuncGen = struct {...@@ -5221,7 +5212,7 @@ pub const FuncGen = struct {
5221 const inst_ty = self.air.typeOfIndex(inst);5212 const inst_ty = self.air.typeOfIndex(inst);
5222 const scalar_ty = inst_ty.scalarType();5213 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 });
5225 if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");5216 if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
5226 return self.builder.buildNUWAdd(lhs, rhs, "");5217 return self.builder.buildNUWAdd(lhs, rhs, "");
5227 }5218 }
...@@ -5260,7 +5251,7 @@ pub const FuncGen = struct {...@@ -5260,7 +5251,7 @@ pub const FuncGen = struct {
5260 const inst_ty = self.air.typeOfIndex(inst);5251 const inst_ty = self.air.typeOfIndex(inst);
5261 const scalar_ty = inst_ty.scalarType();5252 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 });
5264 if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");5255 if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
5265 return self.builder.buildNUWSub(lhs, rhs, "");5256 return self.builder.buildNUWSub(lhs, rhs, "");
5266 }5257 }
...@@ -5298,7 +5289,7 @@ pub const FuncGen = struct {...@@ -5298,7 +5289,7 @@ pub const FuncGen = struct {
5298 const inst_ty = self.air.typeOfIndex(inst);5289 const inst_ty = self.air.typeOfIndex(inst);
5299 const scalar_ty = inst_ty.scalarType();5290 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 });
5302 if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");5293 if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
5303 return self.builder.buildNUWMul(lhs, rhs, "");5294 return self.builder.buildNUWMul(lhs, rhs, "");
5304 }5295 }
...@@ -5333,8 +5324,9 @@ pub const FuncGen = struct {...@@ -5333,8 +5324,9 @@ pub const FuncGen = struct {
5333 const bin_op = self.air.instructions.items(.data)[inst].bin_op;5324 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5334 const lhs = try self.resolveInst(bin_op.lhs);5325 const lhs = try self.resolveInst(bin_op.lhs);
5335 const rhs = try self.resolveInst(bin_op.rhs);5326 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 });
5338 }5330 }
53395331
5340 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5332 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -5347,8 +5339,8 @@ pub const FuncGen = struct {...@@ -5347,8 +5339,8 @@ pub const FuncGen = struct {
5347 const scalar_ty = inst_ty.scalarType();5339 const scalar_ty = inst_ty.scalarType();
53485340
5349 if (scalar_ty.isRuntimeFloat()) {5341 if (scalar_ty.isRuntimeFloat()) {
5350 const result = self.builder.buildFDiv(lhs, rhs, "");5342 const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
5351 return self.callTrunc(result, inst_ty);5343 return self.buildFloatOp(.trunc, inst_ty, 1, .{result});
5352 }5344 }
5353 if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");5345 if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
5354 return self.builder.buildUDiv(lhs, rhs, "");5346 return self.builder.buildUDiv(lhs, rhs, "");
...@@ -5364,8 +5356,8 @@ pub const FuncGen = struct {...@@ -5364,8 +5356,8 @@ pub const FuncGen = struct {
5364 const scalar_ty = inst_ty.scalarType();5356 const scalar_ty = inst_ty.scalarType();
53655357
5366 if (scalar_ty.isRuntimeFloat()) {5358 if (scalar_ty.isRuntimeFloat()) {
5367 const result = self.builder.buildFDiv(lhs, rhs, "");5359 const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
5368 return try self.callFloor(result, inst_ty);5360 return self.buildFloatOp(.floor, inst_ty, 1, .{result});
5369 }5361 }
5370 if (scalar_ty.isSignedInt()) {5362 if (scalar_ty.isSignedInt()) {
5371 // const d = @divTrunc(a, b);5363 // const d = @divTrunc(a, b);
...@@ -5395,7 +5387,7 @@ pub const FuncGen = struct {...@@ -5395,7 +5387,7 @@ pub const FuncGen = struct {
5395 const inst_ty = self.air.typeOfIndex(inst);5387 const inst_ty = self.air.typeOfIndex(inst);
5396 const scalar_ty = inst_ty.scalarType();5388 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 });
5399 if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");5391 if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");
5400 return self.builder.buildExactUDiv(lhs, rhs, "");5392 return self.builder.buildExactUDiv(lhs, rhs, "");
5401 }5393 }
...@@ -5409,7 +5401,7 @@ pub const FuncGen = struct {...@@ -5409,7 +5401,7 @@ pub const FuncGen = struct {
5409 const inst_ty = self.air.typeOfIndex(inst);5401 const inst_ty = self.air.typeOfIndex(inst);
5410 const scalar_ty = inst_ty.scalarType();5402 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 });
5413 if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");5405 if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
5414 return self.builder.buildURem(lhs, rhs, "");5406 return self.builder.buildURem(lhs, rhs, "");
5415 }5407 }
...@@ -5425,11 +5417,11 @@ pub const FuncGen = struct {...@@ -5425,11 +5417,11 @@ pub const FuncGen = struct {
5425 const scalar_ty = inst_ty.scalarType();5417 const scalar_ty = inst_ty.scalarType();
54265418
5427 if (scalar_ty.isRuntimeFloat()) {5419 if (scalar_ty.isRuntimeFloat()) {
5428 const a = self.builder.buildFRem(lhs, rhs, "");5420 const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
5429 const b = self.builder.buildFAdd(a, rhs, "");5421 const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs });
5430 const c = self.builder.buildFRem(b, rhs, "");5422 const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs });
5431 const zero = inst_llvm_ty.constNull();5423 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 });
5433 return self.builder.buildSelect(ltz, c, a, "");5425 return self.builder.buildSelect(ltz, c, a, "");
5434 }5426 }
5435 if (scalar_ty.isSignedInt()) {5427 if (scalar_ty.isSignedInt()) {
...@@ -5508,75 +5500,266 @@ pub const FuncGen = struct {...@@ -5508,75 +5500,266 @@ pub const FuncGen = struct {
5508 return result_struct;5500 return result_struct;
5509 }5501 }
55105502
5511 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5503 fn buildElementwiseCall(
5512 if (self.liveness.isUnused(inst)) return null;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;5514 var i: usize = 0;
5515 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;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);5519 var args: [3]*const llvm.Value = undefined;
5518 const mulend2 = try self.resolveInst(extra.rhs);5520 for (args_vectors) |arg_vector, k| {
5519 const addend = try self.resolveInst(pl_op.operand);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);5529 fn getLibcFunction(
5522 const llvm_ty = try self.dg.llvmType(ty);5530 self: *FuncGen,
5523 const scalar_ty = ty.scalarType();5531 fn_name: [:0]const u8,
5524 const target = self.dg.module.getTarget();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) {5554 fn libcFloatSuffix(float_bits: u16) []const u8 {
5527 intrinsic,5555 return switch (float_bits) {
5528 libc: [*:0]const u8,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,
5529 };5562 };
5563 }
55305564
5531 const strat: Strat = switch (scalar_ty.floatBits(target)) {5565 fn compilerRtFloatAbbrev(float_bits: u16) []const u8 {
5532 16, 32, 64 => Strat.intrinsic,5566 return switch (float_bits) {
5533 80 => if (CType.longdouble.sizeInBits(target) == 80) Strat{ .intrinsic = {} } else Strat{ .libc = "__fmax" },5567 16 => "h",
5534 // LLVM always lowers the fma builtin for f128 to fmal, which is for `long double`.5568 32 => "s",
5535 // On some targets this will be correct; on others it will be incorrect.5569 64 => "d",
5536 128 => if (CType.longdouble.sizeInBits(target) == 128) Strat{ .intrinsic = {} } else Strat{ .libc = "fmaq" },5570 80 => "x",
5571 128 => "t",
5537 else => unreachable,5572 else => unreachable,
5538 };5573 };
5574 }
55395575
5540 switch (strat) {5576 /// Creates a floating point comparison by lowering to the appropriate
5541 .intrinsic => {5577 /// hardware instruction or softfloat routine for the target
5542 const llvm_fn = self.getIntrinsic("llvm.fma", &.{llvm_ty});5578 fn buildFloatCmp(
5543 const params = [_]*const llvm.Value{ mulend1, mulend2, addend };5579 self: *FuncGen,
5544 return self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");5580 pred: math.CompareOperator,
5545 },5581 ty: Type,
5546 .libc => |fn_name| {5582 params: [2]*const llvm.Value,
5547 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);5583 ) !*const llvm.Value {
5548 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(fn_name) orelse b: {5584 const target = self.dg.module.getTarget();
5549 const param_types = [_]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty };5585 const scalar_ty = ty.scalarType();
5550 const fn_type = llvm.functionType(scalar_llvm_ty, &param_types, param_types.len, .False);5586 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
5551 break :b self.dg.object.llvm_module.addFunction(fn_name, fn_type);
5552 };
55535587
5554 if (ty.zigTypeTag() == .Vector) {5588 if (intrinsicsAllowed(scalar_ty, target)) {
5555 const llvm_i32 = self.context.intType(32);5589 const llvm_predicate: llvm.RealPredicate = switch (pred) {
5556 const vector_llvm_ty = try self.dg.llvmType(ty);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;5615 const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty };
5559 var vector = vector_llvm_ty.getUndef();5616 const llvm_i32 = self.context.intType(32);
5560 while (i < ty.vectorLen()) : (i += 1) {5617 const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32);
5561 const index_i32 = llvm_i32.constInt(i, .False);
55625618
5563 const mulend1_elem = self.builder.buildExtractElement(mulend1, index_i32, "");5619 const zero = llvm_i32.constInt(0, .False);
5564 const mulend2_elem = self.builder.buildExtractElement(mulend2, index_i32, "");5620 const int_pred: llvm.IntPredicate = switch (pred) {
5565 const addend_elem = self.builder.buildExtractElement(addend, index_i32, "");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 };5629 if (ty.zigTypeTag() == .Vector) {
5568 const mul_add = self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");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, "");5669 const FloatOpStrat = union(enum) {
5571 }5670 intrinsic: []const u8,
5671 libc: [:0]const u8,
5672 };
55725673
5573 return vector;5674 /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.)
5574 } else {5675 /// by lowering to the appropriate hardware instruction or softfloat
5575 const params = [_]*const llvm.Value{ mulend1, mulend2, addend };5676 /// routine for the target
5576 return self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");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());
5577 }5743 }
5744
5745 break :b libc_fn;
5578 },5746 },
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 });
5580 }5763 }
55815764
5582 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5765 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -6381,14 +6564,14 @@ pub const FuncGen = struct {...@@ -6381,14 +6564,14 @@ pub const FuncGen = struct {
6381 }6564 }
6382 }6565 }
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 {
6385 if (self.liveness.isUnused(inst)) return null;6568 if (self.liveness.isUnused(inst)) return null;
63866569
6387 const un_op = self.air.instructions.items(.data)[inst].un_op;6570 const un_op = self.air.instructions.items(.data)[inst].un_op;
6388 const operand = try self.resolveInst(un_op);6571 const operand = try self.resolveInst(un_op);
6389 const operand_ty = self.air.typeOf(un_op);6572 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});
6392 }6575 }
63936576
6394 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value {6577 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value {
...@@ -6652,17 +6835,9 @@ pub const FuncGen = struct {...@@ -6652,17 +6835,9 @@ pub const FuncGen = struct {
66526835
6653 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6836 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6654 const scalar = try self.resolveInst(ty_op.operand);6837 const scalar = try self.resolveInst(ty_op.operand);
6655 const scalar_ty = self.air.typeOf(ty_op.operand);
6656 const vector_ty = self.air.typeOfIndex(inst);6838 const vector_ty = self.air.typeOfIndex(inst);
6657 const len = vector_ty.vectorLen();6839 const len = vector_ty.vectorLen();
6658 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);6840 return self.builder.buildVectorSplat(len, scalar, "");
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(), "");
6666 }6841 }
66676842
6668 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {6843 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -7191,48 +7366,6 @@ pub const FuncGen = struct {...@@ -7191,48 +7366,6 @@ pub const FuncGen = struct {
7191 return self.builder.buildExtractValue(opt_handle, 0, "");7366 return self.builder.buildExtractValue(opt_handle, 0, "");
7192 }7367 }
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
7236 fn fieldPtr(7369 fn fieldPtr(
7237 self: *FuncGen,7370 self: *FuncGen,
7238 inst: Air.Inst.Index,7371 inst: Air.Inst.Index,
...@@ -8055,6 +8188,26 @@ fn backendSupportsF80(target: std.Target) bool {...@@ -8055,6 +8188,26 @@ fn backendSupportsF80(target: std.Target) bool {
8055 };8188 };
8056}8189}
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
8058/// We need to insert extra padding if LLVM's isn't enough.8211/// We need to insert extra padding if LLVM's isn't enough.
8059/// However we don't want to ever call LLVMABIAlignmentOfType or8212/// However we don't want to ever call LLVMABIAlignmentOfType or
8060/// LLVMABISizeOfType because these functions will trip assertions8213/// LLVMABISizeOfType because these functions will trip assertions
src/codegen/llvm/bindings.zig+8
...@@ -675,6 +675,14 @@ pub const Builder = opaque {...@@ -675,6 +675,14 @@ pub const Builder = opaque {
675 Name: [*:0]const u8,675 Name: [*:0]const u8,
676 ) *const Value;676 ) *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
678 pub const buildPtrToInt = LLVMBuildPtrToInt;686 pub const buildPtrToInt = LLVMBuildPtrToInt;
679 extern fn LLVMBuildPtrToInt(687 extern fn LLVMBuildPtrToInt(
680 *const Builder,688 *const Builder,
src/print_air.zig+1
...@@ -158,6 +158,7 @@ const Writer = struct {...@@ -158,6 +158,7 @@ const Writer = struct {
158 .sqrt,158 .sqrt,
159 .sin,159 .sin,
160 .cos,160 .cos,
161 .tan,
161 .exp,162 .exp,
162 .exp2,163 .exp2,
163 .log,164 .log,
src/print_zir.zig+11-1
...@@ -207,6 +207,7 @@ const Writer = struct {...@@ -207,6 +207,7 @@ const Writer = struct {
207 .sqrt,207 .sqrt,
208 .sin,208 .sin,
209 .cos,209 .cos,
210 .tan,
210 .exp,211 .exp,
211 .exp2,212 .exp2,
212 .log,213 .log,
...@@ -400,7 +401,6 @@ const Writer = struct {...@@ -400,7 +401,6 @@ const Writer = struct {
400401
401 .field_ptr_named,402 .field_ptr_named,
402 .field_val_named,403 .field_val_named,
403 .field_call_bind_named,
404 => try self.writePlNodeFieldNamed(stream, inst),404 => try self.writePlNodeFieldNamed(stream, inst),
405405
406 .as_node => try self.writeAs(stream, inst),406 .as_node => try self.writeAs(stream, inst),
...@@ -509,6 +509,16 @@ const Writer = struct {...@@ -509,6 +509,16 @@ const Writer = struct {
509 try stream.writeAll(")) ");509 try stream.writeAll(")) ");
510 try self.writeSrc(stream, src);510 try self.writeSrc(stream, src);
511 },511 },
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 },
512 }522 }
513 }523 }
514524
src/stage1/all_types.hpp+1
...@@ -1768,6 +1768,7 @@ enum BuiltinFnId {...@@ -1768,6 +1768,7 @@ enum BuiltinFnId {
1768 BuiltinFnIdSqrt,1768 BuiltinFnIdSqrt,
1769 BuiltinFnIdSin,1769 BuiltinFnIdSin,
1770 BuiltinFnIdCos,1770 BuiltinFnIdCos,
1771 BuiltinFnIdTan,
1771 BuiltinFnIdExp,1772 BuiltinFnIdExp,
1772 BuiltinFnIdExp2,1773 BuiltinFnIdExp2,
1773 BuiltinFnIdLog,1774 BuiltinFnIdLog,
src/stage1/analyze.cpp+6-2
...@@ -8928,7 +8928,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS...@@ -8928,7 +8928,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
89288928
8929 assert(next_offset >= llvm_next_offset);8929 assert(next_offset >= llvm_next_offset);
8930 if (next_offset > llvm_next_offset) {8930 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));
8932 if (pad_bytes != 0) {8932 if (pad_bytes != 0) {
8933 LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);8933 LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes);
8934 element_types[gen_field_index] = pad_llvm_type;8934 element_types[gen_field_index] = pad_llvm_type;
...@@ -10375,7 +10375,7 @@ void ZigValue::dump() {...@@ -10375,7 +10375,7 @@ void ZigValue::dump() {
1037510375
10376// float ops that take a single argument10376// float ops that take a single argument
10377//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, lround, llround, lrint, llrint10377//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) {
10379 switch (op) {10379 switch (op) {
10380 case BuiltinFnIdSqrt:10380 case BuiltinFnIdSqrt:
10381 return "sqrt";10381 return "sqrt";
...@@ -10383,6 +10383,8 @@ const char *float_op_to_name(BuiltinFnId op) {...@@ -10383,6 +10383,8 @@ const char *float_op_to_name(BuiltinFnId op) {
10383 return "sin";10383 return "sin";
10384 case BuiltinFnIdCos:10384 case BuiltinFnIdCos:
10385 return "cos";10385 return "cos";
10386 case BuiltinFnIdTan:
10387 return "tan";
10386 case BuiltinFnIdExp:10388 case BuiltinFnIdExp:
10387 return "exp";10389 return "exp";
10388 case BuiltinFnIdExp2:10390 case BuiltinFnIdExp2:
...@@ -10405,6 +10407,8 @@ const char *float_op_to_name(BuiltinFnId op) {...@@ -10405,6 +10407,8 @@ const char *float_op_to_name(BuiltinFnId op) {
10405 return "nearbyint";10407 return "nearbyint";
10406 case BuiltinFnIdRound:10408 case BuiltinFnIdRound:
10407 return "round";10409 return "round";
10410 case BuiltinFnIdMulAdd:
10411 return "fma";
10408 default:10412 default:
10409 zig_unreachable();10413 zig_unreachable();
10410 }10414 }
src/stage1/analyze.hpp+1-1
...@@ -307,7 +307,7 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src);...@@ -307,7 +307,7 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src);
307bool type_has_optional_repr(ZigType *ty);307bool type_has_optional_repr(ZigType *ty);
308bool is_opt_err_set(ZigType *ty);308bool is_opt_err_set(ZigType *ty);
309bool type_is_numeric(ZigType *ty);309bool type_is_numeric(ZigType *ty);
310const char *float_op_to_name(BuiltinFnId op);310const char *float_un_op_to_name(BuiltinFnId op);
311311
312#define src_assert(OK, SOURCE_NODE) src_assert_impl((OK), (SOURCE_NODE), __FILE__, __LINE__)312#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...@@ -4497,6 +4497,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
4497 case BuiltinFnIdSqrt:4497 case BuiltinFnIdSqrt:
4498 case BuiltinFnIdSin:4498 case BuiltinFnIdSin:
4499 case BuiltinFnIdCos:4499 case BuiltinFnIdCos:
4500 case BuiltinFnIdTan:
4500 case BuiltinFnIdExp:4501 case BuiltinFnIdExp:
4501 case BuiltinFnIdExp2:4502 case BuiltinFnIdExp2:
4502 case BuiltinFnIdLog:4503 case BuiltinFnIdLog:
src/stage1/codegen.cpp+401-324
...@@ -869,7 +869,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn...@@ -869,7 +869,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn
869 name = "fma";869 name = "fma";
870 num_args = 3;870 num_args = 3;
871 } else if (fn_id == ZigLLVMFnIdFloatOp) {871 } else if (fn_id == ZigLLVMFnIdFloatOp) {
872 name = float_op_to_name(op);872 name = float_un_op_to_name(op);
873 num_args = 1;873 num_args = 1;
874 } else {874 } else {
875 zig_unreachable();875 zig_unreachable();
...@@ -1604,8 +1604,57 @@ static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *...@@ -1604,8 +1604,57 @@ static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *
1604 return nullptr;1604 return nullptr;
1605}1605}
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,
1609 ZigType *wanted_type, LLVMValueRef expr_val)1658 ZigType *wanted_type, LLVMValueRef expr_val)
1610{1659{
1611 ZigType *scalar_actual_type = (actual_type->id == ZigTypeIdVector) ?1660 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...@@ -1615,87 +1664,47 @@ static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_ty
1615 uint64_t actual_bits = scalar_actual_type->data.floating.bit_count;1664 uint64_t actual_bits = scalar_actual_type->data.floating.bit_count;
1616 uint64_t wanted_bits = scalar_wanted_type->data.floating.bit_count;1665 uint64_t wanted_bits = scalar_wanted_type->data.floating.bit_count;
16171666
16181667 if (actual_bits == wanted_bits)
1619 LLVMTypeRef param_type;1668 return expr_val;
1620 LLVMTypeRef return_type;
1621 const char *func_name;
16221669
1623 LLVMValueRef result;1670 LLVMValueRef result;
1624 bool castTruncatedToF16 = false;1671 bool castTruncatedToF16 = false;
16251672
1626 if (actual_bits == wanted_bits) {1673 char fn_name[64];
1627 return expr_val;1674 if (wanted_bits < actual_bits) {
1628 } else if (actual_bits == 80) {1675 sprintf(fn_name, "__trunc%sf%sf2",
1629 param_type = g->builtin_types.entry_f80->llvm_type;1676 get_compiler_rt_type_abbrev(scalar_actual_type),
1630 switch (wanted_bits) {1677 get_compiler_rt_type_abbrev(scalar_wanted_type));
1631 case 16:1678 } else {
1632 // Only Arm has a native f16 type, other platforms soft-implement it1679 sprintf(fn_name, "__extend%sf%sf2",
1633 // using u16 instead.1680 get_compiler_rt_type_abbrev(scalar_actual_type),
1634 if (target_is_arm(g->zig_target)) {1681 get_compiler_rt_type_abbrev(scalar_wanted_type));
1635 return_type = g->builtin_types.entry_f16->llvm_type;1682 }
1636 } else {1683
1637 return_type = g->builtin_types.entry_u16->llvm_type;1684 LLVMTypeRef return_type = scalar_wanted_type->llvm_type;
1638 castTruncatedToF16 = true;1685 LLVMTypeRef param_type = scalar_actual_type->llvm_type;
1639 }1686
1640 func_name = "__truncxfhf2";1687 if (!target_is_arm(g->zig_target)) {
1641 break;1688 // Only Arm has a native f16 type, other platforms soft-implement it using u16 instead.
1642 case 32:1689 if (scalar_wanted_type == g->builtin_types.entry_f16) {
1643 return_type = g->builtin_types.entry_f32->llvm_type;1690 return_type = g->builtin_types.entry_u16->llvm_type;
1644 func_name = "__truncxfsf2";1691 castTruncatedToF16 = true;
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();
1656 }1692 }
1657 } else if (wanted_bits == 80) {1693 if (scalar_actual_type == g->builtin_types.entry_f16) {
1658 return_type = g->builtin_types.entry_f80->llvm_type;1694 param_type = g->builtin_types.entry_u16->llvm_type;
1659 switch (actual_bits) {1695 expr_val = LLVMBuildBitCast(g->builder, expr_val, param_type, "");
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();
1685 }1696 }
1686 } else {
1687 zig_unreachable();
1688 }1697 }
16891698
1690 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name);1699 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name);
1691 if (func_ref == nullptr) {1700 if (func_ref == nullptr) {
1692 LLVMTypeRef fn_type = LLVMFunctionType(return_type, &param_type, 1, false);1701 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);
1694 }1703 }
16951704
1696 result = LLVMBuildCall(g->builder, func_ref, &expr_val, 1, "");1705 result = LLVMBuildCall(g->builder, func_ref, &expr_val, 1, "");
16971706
1698 // On non-Arm platforms we need to bitcast __truncxfhf2 result back to f161707 // On non-Arm platforms we need to bitcast __trunc<>fhf2 result back to f16
1699 if (castTruncatedToF16) {1708 if (castTruncatedToF16) {
1700 result = LLVMBuildBitCast(g->builder, result, g->builtin_types.entry_f16->llvm_type, "");1709 result = LLVMBuildBitCast(g->builder, result, g->builtin_types.entry_f16->llvm_type, "");
1701 }1710 }
...@@ -1721,7 +1730,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1721,7 +1730,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1721 || scalar_wanted_type == g->builtin_types.entry_f80)1730 || scalar_wanted_type == g->builtin_types.entry_f80)
1722 && !target_has_f80(g->zig_target))1731 && !target_has_f80(g->zig_target))
1723 {1732 {
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);
1725 }1734 }
1726 actual_bits = scalar_actual_type->data.floating.bit_count;1735 actual_bits = scalar_actual_type->data.floating.bit_count;
1727 wanted_bits = scalar_wanted_type->data.floating.bit_count;1736 wanted_bits = scalar_wanted_type->data.floating.bit_count;
...@@ -2978,10 +2987,54 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type,...@@ -2978,10 +2987,54 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type,
2978 return result;2987 return result;
2979}2988}
29802989
2981static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {2990static LLVMValueRef get_soft_float_fn(CodeGen *g, const char *name, int param_count, LLVMTypeRef param_type, LLVMTypeRef return_type) {
2982 assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector);2991 LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name);
2983 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);2992 if (existing_llvm_fn != nullptr) return existing_llvm_fn;
2984 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");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, "");
2985}3038}
29863039
2987enum DivKind {3040enum DivKind {
...@@ -3088,7 +3141,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast...@@ -3088,7 +3141,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
3088 case DivKindExact:3141 case DivKindExact:
3089 if (want_runtime_safety) {3142 if (want_runtime_safety) {
3090 // Safety check: a / b == floor(a / b)3143 // 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
3093 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");3146 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
3094 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");3147 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...@@ -3105,9 +3158,9 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
3105 }3158 }
3106 return result;3159 return result;
3107 case DivKindTrunc:3160 case DivKindTrunc:
3108 return gen_float_op(g, result, operand_type, BuiltinFnIdTrunc);3161 return gen_float_un_op(g, result, operand_type, BuiltinFnIdTrunc);
3109 case DivKindFloor:3162 case DivKindFloor:
3110 return gen_float_op(g, result, operand_type, BuiltinFnIdFloor);3163 return gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor);
3111 }3164 }
3112 zig_unreachable();3165 zig_unreachable();
3113 }3166 }
...@@ -3269,17 +3322,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type...@@ -3269,17 +3322,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type
3269 }3322 }
3270}3323}
32713324
3272static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int param_count, LLVMTypeRef return_type) {3325enum Icmp {
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 {
3283 NONE,3326 NONE,
3284 EQ_ZERO,3327 EQ_ZERO,
3285 NE_ZERO,3328 NE_ZERO,
...@@ -3289,7 +3332,7 @@ enum SoftF80Icmp {...@@ -3289,7 +3332,7 @@ enum SoftF80Icmp {
3289 EQ_ONE,3332 EQ_ONE,
3290};3333};
32913334
3292static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) {3335static LLVMValueRef add_icmp(CodeGen *g, LLVMValueRef val, Icmp kind) {
3293 switch (kind) {3336 switch (kind) {
3294 case NONE:3337 case NONE:
3295 return val;3338 return val;
...@@ -3322,22 +3365,124 @@ static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind)...@@ -3322,22 +3365,124 @@ static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind)
3322 }3365 }
3323}3366}
33243367
3325static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,3368static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) {
3326 Stage1AirInstBinOp *bin_op_instruction)3369 uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0;
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;
33323370
3333 LLVMValueRef op1_value = ir_llvm_value(g, op1);3371 // Handle integers of non-pot bitsize by widening them.
3334 LLVMValueRef op2_value = ir_llvm_value(g, op2);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;3382 const char *int_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type);
3337 LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_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;
3338 int param_count = 2;3478 int param_count = 2;
3339 const char *func_name;3479
3340 SoftF80Icmp res_icmp = NONE;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;
3341 switch (op_id) {3486 switch (op_id) {
3342 case IrBinOpInvalid:3487 case IrBinOpInvalid:
3343 case IrBinOpArrayCat:3488 case IrBinOpArrayCat:
...@@ -3362,152 +3507,129 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3362,152 +3507,129 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
3362 zig_unreachable();3507 zig_unreachable();
3363 case IrBinOpCmpEq:3508 case IrBinOpCmpEq:
3364 return_type = g->builtin_types.entry_i32->llvm_type;3509 return_type = g->builtin_types.entry_i32->llvm_type;
3365 func_name = "__eqxf2";3510 sprintf(fn_name, "__eq%sf2", compiler_rt_type_abbrev);
3366 res_icmp = EQ_ZERO;3511 res_icmp = EQ_ZERO;
3367 break;3512 break;
3368 case IrBinOpCmpNotEq:3513 case IrBinOpCmpNotEq:
3369 return_type = g->builtin_types.entry_i32->llvm_type;3514 return_type = g->builtin_types.entry_i32->llvm_type;
3370 func_name = "__nexf2";3515 sprintf(fn_name, "__ne%sf2", compiler_rt_type_abbrev);
3371 res_icmp = NE_ZERO;3516 res_icmp = NE_ZERO;
3372 break;3517 break;
3373 case IrBinOpCmpLessOrEq:3518 case IrBinOpCmpLessOrEq:
3374 return_type = g->builtin_types.entry_i32->llvm_type;3519 return_type = g->builtin_types.entry_i32->llvm_type;
3375 func_name = "__lexf2";3520 sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev);
3376 res_icmp = LE_ZERO;3521 res_icmp = LE_ZERO;
3377 break;3522 break;
3378 case IrBinOpCmpLessThan:3523 case IrBinOpCmpLessThan:
3379 return_type = g->builtin_types.entry_i32->llvm_type;3524 return_type = g->builtin_types.entry_i32->llvm_type;
3380 func_name = "__lexf2";3525 sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev);
3381 res_icmp = EQ_NEG;3526 res_icmp = EQ_NEG;
3382 break;3527 break;
3383 case IrBinOpCmpGreaterOrEq:3528 case IrBinOpCmpGreaterOrEq:
3384 return_type = g->builtin_types.entry_i32->llvm_type;3529 return_type = g->builtin_types.entry_i32->llvm_type;
3385 func_name = "__gexf2";3530 sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev);
3386 res_icmp = GE_ZERO;3531 res_icmp = GE_ZERO;
3387 break;3532 break;
3388 case IrBinOpCmpGreaterThan:3533 case IrBinOpCmpGreaterThan:
3389 return_type = g->builtin_types.entry_i32->llvm_type;3534 return_type = g->builtin_types.entry_i32->llvm_type;
3390 func_name = "__gexf2";3535 sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev);
3391 res_icmp = EQ_ONE;3536 res_icmp = EQ_ONE;
3392 break;3537 break;
3393 case IrBinOpMaximum:3538 case IrBinOpMaximum:
3394 func_name = "__fmaxx";3539 sprintf(fn_name, "%sfmax%s", math_float_prefix, math_float_suffix);
3395 break;3540 break;
3396 case IrBinOpMinimum:3541 case IrBinOpMinimum:
3397 func_name = "__fminx";3542 sprintf(fn_name, "%sfmin%s", math_float_prefix, math_float_suffix);
3398 break;3543 break;
3399 case IrBinOpMult:3544 case IrBinOpMult:
3400 func_name = "__mulxf3";3545 sprintf(fn_name, "__mul%sf3", compiler_rt_type_abbrev);
3401 break;3546 break;
3402 case IrBinOpAdd:3547 case IrBinOpAdd:
3403 func_name = "__addxf3";3548 sprintf(fn_name, "__add%sf3", compiler_rt_type_abbrev);
3404 break;3549 break;
3405 case IrBinOpSub:3550 case IrBinOpSub:
3406 func_name = "__subxf3";3551 sprintf(fn_name, "__sub%sf3", compiler_rt_type_abbrev);
3407 break;3552 break;
3408 case IrBinOpDivUnspecified:3553 case IrBinOpDivUnspecified:
3409 func_name = "__divxf3";
3410 break;
3411 case IrBinOpDivExact:3554 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;
3416 case IrBinOpDivTrunc:3555 case IrBinOpDivTrunc:
3417 param_count = 1;
3418 func_name = "__truncx";
3419 break;
3420 case IrBinOpDivFloor:3556 case IrBinOpDivFloor:
3421 param_count = 1;3557 sprintf(fn_name, "__div%sf3", compiler_rt_type_abbrev);
3422 func_name = "__floorx";
3423 break;3558 break;
3424 case IrBinOpRemRem:3559 case IrBinOpRemRem:
3425 param_count = 1;
3426 func_name = "__remx";
3427 break;
3428 case IrBinOpRemMod:3560 case IrBinOpRemMod:
3429 param_count = 1;3561 sprintf(fn_name, "%sfmod%s", math_float_prefix, math_float_suffix);
3430 func_name = "__modx";
3431 break;3562 break;
3432 default:3563 default:
3433 zig_unreachable();3564 zig_unreachable();
3434 }3565 }
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
3438 LLVMValueRef result;3569 LLVMValueRef result;
3439 if (vector_len == 0) {3570 if (vector_len == 0) {
3440 LLVMValueRef params[2] = {op1_value, op2_value};3571 LLVMValueRef params[2] = {op1_value, op2_value};
3441 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");3572 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);
3443 } else {3574 } else {
3444 ZigType *alloca_ty = op1->value->type;3575 ZigType *alloca_ty = operand_type;
3445 if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool);3576 if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool);
3446 result = build_alloca(g, alloca_ty, "", 0);3577 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;
3478 for (uint32_t i = 0; i < vector_len; i++) {3580 for (uint32_t i = 0; i < vector_len; i++) {
3479 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);3581 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
3480 LLVMValueRef div_res = LLVMBuildExtractElement(g->builder,
3481 LLVMBuildLoad(g->builder, result, ""), index_value, "");
3482
3483 LLVMValueRef params[2] = {3582 LLVMValueRef params[2] = {
3484 div_res,3583 LLVMBuildExtractElement(g->builder, op1_value, index_value, ""),
3485 LLVMBuildCall(g->builder, floor_func, &div_res, 1, ""),3584 LLVMBuildExtractElement(g->builder, op2_value, index_value, ""),
3486 };3585 };
3487 LLVMValueRef cmp_res = LLVMBuildCall(g->builder, eq_func, params, 2, "");3586 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3488 cmp_res = LLVMBuildTrunc(g->builder, cmp_res, g->builtin_types.entry_bool->llvm_type, "");3587 call_result = add_icmp(g, call_result, res_icmp);
3489 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, ok_bit, ""),3588 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3490 cmp_res, index_value, "");3589 call_result, index_value, "");
3491 }3590 }
34923591
3493 if (vector_len != 0) {3592 result = LLVMBuildLoad(g->builder, result, "");
3494 ok_bit = ZigLLVMBuildAndReduce(g->builder, LLVMBuildLoad(g->builder, ok_bit, ""));3593 }
3495 }
3496 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
3497 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
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);3609 return LLVMBuildSelect(g->builder, ltz, wrapped_result, result, "");
3502 gen_safety_crash(g, PanicMsgIdExactDivisionRemainder);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);3619 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
3505 }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) {3623 LLVMPositionBuilderAtEnd(g->builder, fail_block);
3508 result = LLVMBuildLoad(g->builder, result, "");3624 gen_safety_crash(g, PanicMsgIdExactDivisionRemainder);
3625
3626 LLVMPositionBuilderAtEnd(g->builder, ok_block);
3627 }
3628 return result;
3629 default:
3630 return result;
3509 }3631 }
3510 return result;3632 zig_unreachable();
3511}3633}
35123634
3513static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,3635static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
...@@ -3519,8 +3641,13 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3519,8 +3641,13 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
35193641
3520 ZigType *operand_type = op1->value->type;3642 ZigType *operand_type = op1->value->type;
3521 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;3643 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)) {3644 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);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);
3524 }3651 }
35253652
35263653
...@@ -3828,10 +3955,17 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable,...@@ -3828,10 +3955,17 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable,
3828 }3955 }
3829 case CastOpIntToFloat:3956 case CastOpIntToFloat:
3830 assert(actual_type->id == ZigTypeIdInt);3957 assert(actual_type->id == ZigTypeIdInt);
3831 if (actual_type->data.integral.is_signed) {3958 {
3832 return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), "");3959 if ((wanted_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3833 } else {3960 (wanted_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) {
3834 return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), "");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 }
3835 }3969 }
3836 case CastOpFloatToInt: {3970 case CastOpFloatToInt: {
3837 assert(wanted_type->id == ZigTypeIdInt);3971 assert(wanted_type->id == ZigTypeIdInt);
...@@ -3840,18 +3974,28 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable,...@@ -3840,18 +3974,28 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable,
3840 bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base);3974 bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base);
38413975
3842 LLVMValueRef result;3976 LLVMValueRef result;
3843 if (wanted_type->data.integral.is_signed) {3977 if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3844 result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), "");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);
3845 } else {3980 } 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 }
3847 }3986 }
38483987
3849 if (want_safety) {3988 if (want_safety) {
3850 LLVMValueRef back_to_float;3989 LLVMValueRef back_to_float;
3851 if (wanted_type->data.integral.is_signed) {3990 if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
3852 back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), "");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);
3853 } else {3993 } 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 }
3855 }3999 }
3856 LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, "");4000 LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, "");
3857 LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f);4001 LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f);
...@@ -4151,42 +4295,46 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable,...@@ -4151,42 +4295,46 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable,
4151 return LLVMBuildNot(g->builder, operand, "");4295 return LLVMBuildNot(g->builder, operand, "");
4152}4296}
41534297
4154static LLVMValueRef ir_gen_soft_f80_neg(CodeGen *g, ZigType *op_type, LLVMValueRef operand) {4298static LLVMValueRef gen_soft_float_neg(CodeGen *g, ZigType *operand_type, LLVMValueRef operand) {
4155 uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0;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);4302 ZigType *iX_type = get_int_type(g, true, num_bits);
4158 LLVMValueRef sign_mask = LLVMConstInt(llvm_i80, 1, false);4303 LLVMValueRef sign_mask = LLVMConstInt(iX_type->llvm_type, 1, false);
4159 sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(llvm_i80, 79, false));4304 sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(iX_type->llvm_type, num_bits - 1, false));
41604305
4161 LLVMValueRef result;
4162 if (vector_len == 0) {4306 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, "");
4164 } else {4311 } else {
4165 result = build_alloca(g, op_type, "", 0);4312 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;
4166 }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;4315 LLVMValueRef result = build_alloca(g, iX_vector_type, "", 0);
4169 for (uint32_t i = 0; i < vector_len; i++) {4316 LLVMValueRef bitcasted_operand = LLVMBuildBitCast(g->builder, operand, iX_vector_type->llvm_type, "");
4170 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);4317 for (uint32_t i = 0; i < vector_len; i++) {
4171 LLVMValueRef xor_operand = LLVMBuildExtractElement(g->builder, operand, index_value, "");4318 LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false);
4172 LLVMValueRef xor_result = LLVMBuildXor(g->builder, xor_operand, sign_mask, "");4319 LLVMValueRef elem = LLVMBuildExtractElement(g->builder, bitcasted_operand, index_value, "");
4173 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),4320 LLVMValueRef result_elem = LLVMBuildXor(g->builder, elem, sign_mask, "");
4174 xor_result, index_value, "");4321 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
4175 }4322 result_elem, index_value, "");
4176 if (vector_len != 0) {4323 }
4177 result = LLVMBuildLoad(g->builder, result, "");4324 return LLVMBuildBitCast(g->builder, LLVMBuildLoad(g->builder, result, ""), operand_type->llvm_type, "");
4178 }4325 }
4179 return result;
4180}4326}
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) {
4183 LLVMValueRef llvm_operand = ir_llvm_value(g, operand);4329 LLVMValueRef llvm_operand = ir_llvm_value(g, operand);
4184 ZigType *operand_type = operand->value->type;4330 ZigType *operand_type = operand->value->type;
4185 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?4331 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ?
4186 operand_type->data.vector.elem_type : operand_type;4332 operand_type->data.vector.elem_type : operand_type;
41874333
4188 if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target))4334 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);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
4191 if (scalar_type->id == ZigTypeIdFloat) {4339 if (scalar_type->id == ZigTypeIdFloat) {
4192 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst));4340 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst));
...@@ -4210,7 +4358,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirIn...@@ -4210,7 +4358,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirIn
4210static LLVMValueRef ir_render_negation(CodeGen *g, Stage1Air *executable,4358static LLVMValueRef ir_render_negation(CodeGen *g, Stage1Air *executable,
4211 Stage1AirInstNegation *inst)4359 Stage1AirInstNegation *inst)
4212{4360{
4213 return ir_gen_negation(g, &inst->base, inst->operand, inst->wrapping);4361 return gen_negation(g, &inst->base, inst->operand, inst->wrapping);
4214}4362}
42154363
4216static LLVMValueRef ir_render_bool_not(CodeGen *g, Stage1Air *executable, Stage1AirInstBoolNot *instruction) {4364static 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,...@@ -7024,110 +7172,34 @@ static LLVMValueRef ir_render_atomic_store(CodeGen *g, Stage1Air *executable,
7024 return nullptr;7172 return nullptr;
7025}7173}
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
7109static LLVMValueRef ir_render_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) {7175static 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 }
7115 LLVMValueRef operand = ir_llvm_value(g, instruction->operand);7176 LLVMValueRef operand = ir_llvm_value(g, instruction->operand);
7116 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id);7177 ZigType *operand_type = instruction->operand->value->type;
7117 return LLVMBuildCall(g->builder, fn_val, &operand, 1, "");7178 return gen_float_un_op(g, operand, operand_type, instruction->fn_id);
7118}7179}
71197180
7120static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) {7181static LLVMValueRef ir_render_soft_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction, ZigType *float_type) {
7121 ZigType *op_type = instruction->op1->value->type;7182 ZigType *operand_type = instruction->op1->value->type;
7122 uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0;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";7197 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name);
7125 LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name);
7126 if (func_ref == nullptr) {7198 if (func_ref == nullptr) {
7127 LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type;7199 LLVMTypeRef float_type_ref = float_type->llvm_type;
7128 LLVMTypeRef params[3] = { f80_ref, f80_ref, f80_ref };7200 LLVMTypeRef params[3] = { float_type_ref, float_type_ref, float_type_ref };
7129 LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, params, 3, false);7201 LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, params, 3, false);
7130 func_ref = LLVMAddFunction(g->module, func_name, fn_type);7202 func_ref = LLVMAddFunction(g->module, fn_name, fn_type);
7131 }7203 }
71327204
7133 LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);7205 LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);
...@@ -7161,10 +7233,11 @@ static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable...@@ -7161,10 +7233,11 @@ static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable
7161}7233}
71627234
7163static LLVMValueRef ir_render_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) {7235static LLVMValueRef ir_render_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) {
7164 ZigType *op_type = instruction->op1->value->type;7236 ZigType *operand_type = instruction->op1->value->type;
7165 op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type;7237 operand_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type;
7166 if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) {7238 if ((operand_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) ||
7167 return ir_render_soft_f80_mul_add(g, executable, instruction);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);
7168 }7241 }
7169 LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);7242 LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);
7170 LLVMValueRef op2 = ir_llvm_value(g, instruction->op2);7243 LLVMValueRef op2 = ir_llvm_value(g, instruction->op2);
...@@ -9513,10 +9586,13 @@ static void define_builtin_types(CodeGen *g) {...@@ -9513,10 +9586,13 @@ static void define_builtin_types(CodeGen *g) {
9513 switch (g->zig_target->arch) {9586 switch (g->zig_target->arch) {
9514 case ZigLLVM_x86:9587 case ZigLLVM_x86:
9515 case ZigLLVM_x86_64:9588 case ZigLLVM_x86_64:
9516 if (g->zig_target->abi != ZigLLVM_MSVC)9589 if (g->zig_target->abi != ZigLLVM_MSVC) {
9517 add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble);9590 add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble);
9518 else9591 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 {
9519 add_fp_entry(g, "c_longdouble", 64, LLVMDoubleType(), &g->builtin_types.entry_c_longdouble);9594 add_fp_entry(g, "c_longdouble", 64, LLVMDoubleType(), &g->builtin_types.entry_c_longdouble);
9595 }
9520 break;9596 break;
9521 case ZigLLVM_arm:9597 case ZigLLVM_arm:
9522 case ZigLLVM_armeb:9598 case ZigLLVM_armeb:
...@@ -9750,6 +9826,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -9750,6 +9826,7 @@ static void define_builtin_fns(CodeGen *g) {
9750 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 1);9826 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 1);
9751 create_builtin_fn(g, BuiltinFnIdSin, "sin", 1);9827 create_builtin_fn(g, BuiltinFnIdSin, "sin", 1);
9752 create_builtin_fn(g, BuiltinFnIdCos, "cos", 1);9828 create_builtin_fn(g, BuiltinFnIdCos, "cos", 1);
9829 create_builtin_fn(g, BuiltinFnIdTan, "tan", 1);
9753 create_builtin_fn(g, BuiltinFnIdExp, "exp", 1);9830 create_builtin_fn(g, BuiltinFnIdExp, "exp", 1);
9754 create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 1);9831 create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 1);
9755 create_builtin_fn(g, BuiltinFnIdLog, "log", 1);9832 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_...@@ -24132,6 +24132,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
24132 case BuiltinFnIdCos:24132 case BuiltinFnIdCos:
24133 out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16)));24133 out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16)));
24134 break;24134 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;
24135 case BuiltinFnIdExp:24138 case BuiltinFnIdExp:
24136 out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16)));24139 out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16)));
24137 break;24140 break;
...@@ -24181,6 +24184,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -24181,6 +24184,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
24181 case BuiltinFnIdCos:24184 case BuiltinFnIdCos:
24182 out_val->data.x_f32 = cosf(op->data.x_f32);24185 out_val->data.x_f32 = cosf(op->data.x_f32);
24183 break;24186 break;
24187 case BuiltinFnIdTan:
24188 out_val->data.x_f32 = tanf(op->data.x_f32);
24189 break;
24184 case BuiltinFnIdExp:24190 case BuiltinFnIdExp:
24185 out_val->data.x_f32 = expf(op->data.x_f32);24191 out_val->data.x_f32 = expf(op->data.x_f32);
24186 break;24192 break;
...@@ -24230,6 +24236,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -24230,6 +24236,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
24230 case BuiltinFnIdCos:24236 case BuiltinFnIdCos:
24231 out_val->data.x_f64 = cos(op->data.x_f64);24237 out_val->data.x_f64 = cos(op->data.x_f64);
24232 break;24238 break;
24239 case BuiltinFnIdTan:
24240 out_val->data.x_f64 = tan(op->data.x_f64);
24241 break;
24233 case BuiltinFnIdExp:24242 case BuiltinFnIdExp:
24234 out_val->data.x_f64 = exp(op->data.x_f64);24243 out_val->data.x_f64 = exp(op->data.x_f64);
24235 break;24244 break;
...@@ -24293,6 +24302,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -24293,6 +24302,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
24293 case BuiltinFnIdNearbyInt:24302 case BuiltinFnIdNearbyInt:
24294 case BuiltinFnIdSin:24303 case BuiltinFnIdSin:
24295 case BuiltinFnIdCos:24304 case BuiltinFnIdCos:
24305 case BuiltinFnIdTan:
24296 case BuiltinFnIdExp:24306 case BuiltinFnIdExp:
24297 case BuiltinFnIdExp2:24307 case BuiltinFnIdExp2:
24298 case BuiltinFnIdLog:24308 case BuiltinFnIdLog:
...@@ -24300,7 +24310,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -24300,7 +24310,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
24300 case BuiltinFnIdLog2:24310 case BuiltinFnIdLog2:
24301 return ir_add_error_node(ira, source_node,24311 return ir_add_error_node(ira, source_node,
24302 buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",24312 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)));
24304 default:24314 default:
24305 zig_unreachable();24315 zig_unreachable();
24306 }24316 }
...@@ -24327,24 +24337,94 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -24327,24 +24337,94 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_
24327 break;24337 break;
24328 case BuiltinFnIdCeil:24338 case BuiltinFnIdCeil:
24329 f128M_roundToInt(in, softfloat_round_max, false, out);24339 f128M_roundToInt(in, softfloat_round_max, false, out);
24330 break;24340 break;
24331 case BuiltinFnIdTrunc:24341 case BuiltinFnIdTrunc:
24332 f128M_trunc(in, out);24342 f128M_trunc(in, out);
24333 break;24343 break;
24334 case BuiltinFnIdRound:24344 case BuiltinFnIdRound:
24335 f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);24345 f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);
24336 break;24346 break;
24337 case BuiltinFnIdNearbyInt:24347 case BuiltinFnIdNearbyInt: {
24338 case BuiltinFnIdSin:24348 float64_t f64_value = f128M_to_f64(in);
24339 case BuiltinFnIdCos:24349 double double_value;
24340 case BuiltinFnIdExp:24350 memcpy(&double_value, &f64_value, sizeof(double));
24341 case BuiltinFnIdExp2:24351 double_value = nearbyint(double_value);
24342 case BuiltinFnIdLog:24352 memcpy(&f64_value, &double_value, sizeof(double));
24343 case BuiltinFnIdLog10:24353 f64_to_f128M(f64_value, out);
24344 case BuiltinFnIdLog2:24354 break;
24345 return ir_add_error_node(ira, source_node,24355 }
24346 buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",24356 case BuiltinFnIdSin: {
24347 float_op_to_name(fop), buf_ptr(&float_type->name)));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 }
24348 default:24428 default:
24349 zig_unreachable();24429 zig_unreachable();
24350 }24430 }
src/stage1/ir_print.cpp+2-2
...@@ -2558,13 +2558,13 @@ static void ir_print_add_implicit_return_type(IrPrintSrc *irp, Stage1ZirInstAddI...@@ -2558,13 +2558,13 @@ static void ir_print_add_implicit_return_type(IrPrintSrc *irp, Stage1ZirInstAddI
2558}2558}
25592559
2560static void ir_print_float_op(IrPrintSrc *irp, Stage1ZirInstFloatOp *instruction) {2560static 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));
2562 ir_print_other_inst_src(irp, instruction->operand);2562 ir_print_other_inst_src(irp, instruction->operand);
2563 fprintf(irp->f, ")");2563 fprintf(irp->f, ")");
2564}2564}
25652565
2566static void ir_print_float_op(IrPrintGen *irp, Stage1AirInstFloatOp *instruction) {2566static 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));
2568 ir_print_other_inst_gen(irp, instruction->operand);2568 ir_print_other_inst_gen(irp, instruction->operand);
2569 fprintf(irp->f, ")");2569 fprintf(irp->f, ")");
2570}2570}
src/translate_c.zig+1-1
...@@ -3998,7 +3998,7 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingL...@@ -3998,7 +3998,7 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingL
3998 var dbl = expr.getValueAsApproximateDouble();3998 var dbl = expr.getValueAsApproximateDouble();
3999 const is_negative = dbl < 0;3999 const is_negative = dbl < 0;
4000 if (is_negative) dbl = -dbl;4000 if (is_negative) dbl = -dbl;
4001 const str = if (dbl == std.math.floor(dbl))4001 const str = if (dbl == @floor(dbl))
4002 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})4002 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
4003 else4003 else
4004 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});4004 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
src/value.zig+50-99
...@@ -1155,6 +1155,7 @@ pub const Value = extern union {...@@ -1155,6 +1155,7 @@ pub const Value = extern union {
1155 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),1155 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer),
1156 32 => return floatWriteToMemory(f32, val.toFloat(f32), target, buffer),1156 32 => return floatWriteToMemory(f32, val.toFloat(f32), target, buffer),
1157 64 => return floatWriteToMemory(f64, val.toFloat(f64), target, buffer),1157 64 => return floatWriteToMemory(f64, val.toFloat(f64), target, buffer),
1158 80 => return floatWriteToMemory(f80, val.toFloat(f80), target, buffer),
1158 128 => return floatWriteToMemory(f128, val.toFloat(f128), target, buffer),1159 128 => return floatWriteToMemory(f128, val.toFloat(f128), target, buffer),
1159 else => unreachable,1160 else => unreachable,
1160 },1161 },
...@@ -1379,25 +1380,21 @@ pub const Value = extern union {...@@ -1379,25 +1380,21 @@ pub const Value = extern union {
1379 }1380 }
13801381
1381 fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {1382 fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void {
1383 const endian = target.cpu.arch.endian();
1382 if (F == f80) {1384 if (F == f80) {
1383 switch (target.cpu.arch) {1385 const repr = std.math.break_f80(f);
1384 .i386, .x86_64 => {1386 std.mem.writeInt(u64, buffer[0..8], repr.fraction, endian);
1385 const repr = std.math.break_f80(f);1387 std.mem.writeInt(u16, buffer[8..10], repr.exp, endian);
1386 std.mem.writeIntLittle(u64, buffer[0..8], repr.fraction);1388 // TODO set the rest of the bytes to undefined. should we use 0xaa
1387 std.mem.writeIntLittle(u16, buffer[8..10], repr.exp);1389 // or is there a different way?
1388 // TODO set the rest of the bytes to undefined. should we use 0xaa1390 return;
1389 // or is there a different way?
1390 return;
1391 },
1392 else => {},
1393 }
1394 }1391 }
1395 const Int = @Type(.{ .Int = .{1392 const Int = @Type(.{ .Int = .{
1396 .signedness = .unsigned,1393 .signedness = .unsigned,
1397 .bits = @typeInfo(F).Float.bits,1394 .bits = @typeInfo(F).Float.bits,
1398 } });1395 } });
1399 const int = @bitCast(Int, f);1396 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);
1401 }1398 }
14021399
1403 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {1400 fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F {
...@@ -2869,9 +2866,7 @@ pub const Value = extern union {...@@ -2869,9 +2866,7 @@ pub const Value = extern union {
2869 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)),2866 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)),
2870 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)),2867 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)),
2871 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)),2868 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)),
2872 // We can't lower this properly on non-x86 llvm backends yet2869 80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)),
2873 //80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)),
2874 80 => @panic("TODO f80 intToFloat"),
2875 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)),2870 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)),
2876 else => unreachable,2871 else => unreachable,
2877 }2872 }
...@@ -2908,9 +2903,9 @@ pub const Value = extern union {...@@ -2908,9 +2903,9 @@ pub const Value = extern union {
2908 }2903 }
29092904
2910 const isNegative = std.math.signbit(value);2905 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
2915 var rational = try std.math.big.Rational.init(arena);2910 var rational = try std.math.big.Rational.init(arena);
2916 defer rational.deinit();2911 defer rational.deinit();
...@@ -2941,7 +2936,7 @@ pub const Value = extern union {...@@ -2941,7 +2936,7 @@ pub const Value = extern union {
2941 return 1;2936 return 1;
2942 }2937 }
29432938
2944 const w_value = std.math.fabs(scalar);2939 const w_value = @fabs(scalar);
2945 return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1;2940 return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1;
2946 }2941 }
29472942
...@@ -3737,9 +3732,6 @@ pub const Value = extern union {...@@ -3737,9 +3732,6 @@ pub const Value = extern union {
3737 return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val));3732 return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val));
3738 },3733 },
3739 80 => {3734 80 => {
3740 if (true) {
3741 @panic("TODO implement compiler_rt __remx");
3742 }
3743 const lhs_val = lhs.toFloat(f80);3735 const lhs_val = lhs.toFloat(f80);
3744 const rhs_val = rhs.toFloat(f80);3736 const rhs_val = rhs.toFloat(f80);
3745 return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val));3737 return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val));
...@@ -3782,9 +3774,6 @@ pub const Value = extern union {...@@ -3782,9 +3774,6 @@ pub const Value = extern union {
3782 return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val));3774 return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val));
3783 },3775 },
3784 80 => {3776 80 => {
3785 if (true) {
3786 @panic("TODO implement compiler_rt __modx");
3787 }
3788 const lhs_val = lhs.toFloat(f80);3777 const lhs_val = lhs.toFloat(f80);
3789 const rhs_val = rhs.toFloat(f80);3778 const rhs_val = rhs.toFloat(f80);
3790 return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val));3779 return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val));
...@@ -4198,9 +4187,6 @@ pub const Value = extern union {...@@ -4198,9 +4187,6 @@ pub const Value = extern union {
4198 return Value.Tag.float_64.create(arena, lhs_val / rhs_val);4187 return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
4199 },4188 },
4200 80 => {4189 80 => {
4201 if (true) {
4202 @panic("TODO implement compiler_rt __divxf3");
4203 }
4204 const lhs_val = lhs.toFloat(f80);4190 const lhs_val = lhs.toFloat(f80);
4205 const rhs_val = rhs.toFloat(f80);4191 const rhs_val = rhs.toFloat(f80);
4206 return Value.Tag.float_80.create(arena, lhs_val / rhs_val);4192 return Value.Tag.float_80.create(arena, lhs_val / rhs_val);
...@@ -4255,9 +4241,6 @@ pub const Value = extern union {...@@ -4255,9 +4241,6 @@ pub const Value = extern union {
4255 return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val));4241 return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val));
4256 },4242 },
4257 80 => {4243 80 => {
4258 if (true) {
4259 @panic("TODO implement compiler_rt __floorx");
4260 }
4261 const lhs_val = lhs.toFloat(f80);4244 const lhs_val = lhs.toFloat(f80);
4262 const rhs_val = rhs.toFloat(f80);4245 const rhs_val = rhs.toFloat(f80);
4263 return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val));4246 return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val));
...@@ -4312,9 +4295,6 @@ pub const Value = extern union {...@@ -4312,9 +4295,6 @@ pub const Value = extern union {
4312 return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val));4295 return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val));
4313 },4296 },
4314 80 => {4297 80 => {
4315 if (true) {
4316 @panic("TODO implement compiler_rt __truncx");
4317 }
4318 const lhs_val = lhs.toFloat(f80);4298 const lhs_val = lhs.toFloat(f80);
4319 const rhs_val = rhs.toFloat(f80);4299 const rhs_val = rhs.toFloat(f80);
4320 return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val));4300 return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val));
...@@ -4369,9 +4349,6 @@ pub const Value = extern union {...@@ -4369,9 +4349,6 @@ pub const Value = extern union {
4369 return Value.Tag.float_64.create(arena, lhs_val * rhs_val);4349 return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
4370 },4350 },
4371 80 => {4351 80 => {
4372 if (true) {
4373 @panic("TODO implement compiler_rt __mulxf3");
4374 }
4375 const lhs_val = lhs.toFloat(f80);4352 const lhs_val = lhs.toFloat(f80);
4376 const rhs_val = rhs.toFloat(f80);4353 const rhs_val = rhs.toFloat(f80);
4377 return Value.Tag.float_80.create(arena, lhs_val * rhs_val);4354 return Value.Tag.float_80.create(arena, lhs_val * rhs_val);
...@@ -4411,16 +4388,10 @@ pub const Value = extern union {...@@ -4411,16 +4388,10 @@ pub const Value = extern union {
4411 return Value.Tag.float_64.create(arena, @sqrt(f));4388 return Value.Tag.float_64.create(arena, @sqrt(f));
4412 },4389 },
4413 80 => {4390 80 => {
4414 if (true) {
4415 @panic("TODO implement compiler_rt __sqrtx");
4416 }
4417 const f = val.toFloat(f80);4391 const f = val.toFloat(f80);
4418 return Value.Tag.float_80.create(arena, @sqrt(f));4392 return Value.Tag.float_80.create(arena, @sqrt(f));
4419 },4393 },
4420 128 => {4394 128 => {
4421 if (true) {
4422 @panic("TODO implement compiler_rt sqrtq");
4423 }
4424 const f = val.toFloat(f128);4395 const f = val.toFloat(f128);
4425 return Value.Tag.float_128.create(arena, @sqrt(f));4396 return Value.Tag.float_128.create(arena, @sqrt(f));
4426 },4397 },
...@@ -4454,16 +4425,10 @@ pub const Value = extern union {...@@ -4454,16 +4425,10 @@ pub const Value = extern union {
4454 return Value.Tag.float_64.create(arena, @sin(f));4425 return Value.Tag.float_64.create(arena, @sin(f));
4455 },4426 },
4456 80 => {4427 80 => {
4457 if (true) {
4458 @panic("TODO implement compiler_rt sin for f80");
4459 }
4460 const f = val.toFloat(f80);4428 const f = val.toFloat(f80);
4461 return Value.Tag.float_80.create(arena, @sin(f));4429 return Value.Tag.float_80.create(arena, @sin(f));
4462 },4430 },
4463 128 => {4431 128 => {
4464 if (true) {
4465 @panic("TODO implement compiler_rt sin for f128");
4466 }
4467 const f = val.toFloat(f128);4432 const f = val.toFloat(f128);
4468 return Value.Tag.float_128.create(arena, @sin(f));4433 return Value.Tag.float_128.create(arena, @sin(f));
4469 },4434 },
...@@ -4497,16 +4462,10 @@ pub const Value = extern union {...@@ -4497,16 +4462,10 @@ pub const Value = extern union {
4497 return Value.Tag.float_64.create(arena, @cos(f));4462 return Value.Tag.float_64.create(arena, @cos(f));
4498 },4463 },
4499 80 => {4464 80 => {
4500 if (true) {
4501 @panic("TODO implement compiler_rt cos for f80");
4502 }
4503 const f = val.toFloat(f80);4465 const f = val.toFloat(f80);
4504 return Value.Tag.float_80.create(arena, @cos(f));4466 return Value.Tag.float_80.create(arena, @cos(f));
4505 },4467 },
4506 128 => {4468 128 => {
4507 if (true) {
4508 @panic("TODO implement compiler_rt cos for f128");
4509 }
4510 const f = val.toFloat(f128);4469 const f = val.toFloat(f128);
4511 return Value.Tag.float_128.create(arena, @cos(f));4470 return Value.Tag.float_128.create(arena, @cos(f));
4512 },4471 },
...@@ -4514,6 +4473,43 @@ pub const Value = extern union {...@@ -4514,6 +4473,43 @@ pub const Value = extern union {
4514 }4473 }
4515 }4474 }
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
4517 pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {4513 pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value {
4518 if (float_type.zigTypeTag() == .Vector) {4514 if (float_type.zigTypeTag() == .Vector) {
4519 const result_data = try arena.alloc(Value, float_type.vectorLen());4515 const result_data = try arena.alloc(Value, float_type.vectorLen());
...@@ -4540,16 +4536,10 @@ pub const Value = extern union {...@@ -4540,16 +4536,10 @@ pub const Value = extern union {
4540 return Value.Tag.float_64.create(arena, @exp(f));4536 return Value.Tag.float_64.create(arena, @exp(f));
4541 },4537 },
4542 80 => {4538 80 => {
4543 if (true) {
4544 @panic("TODO implement compiler_rt exp for f80");
4545 }
4546 const f = val.toFloat(f80);4539 const f = val.toFloat(f80);
4547 return Value.Tag.float_80.create(arena, @exp(f));4540 return Value.Tag.float_80.create(arena, @exp(f));
4548 },4541 },
4549 128 => {4542 128 => {
4550 if (true) {
4551 @panic("TODO implement compiler_rt exp for f128");
4552 }
4553 const f = val.toFloat(f128);4543 const f = val.toFloat(f128);
4554 return Value.Tag.float_128.create(arena, @exp(f));4544 return Value.Tag.float_128.create(arena, @exp(f));
4555 },4545 },
...@@ -4583,16 +4573,10 @@ pub const Value = extern union {...@@ -4583,16 +4573,10 @@ pub const Value = extern union {
4583 return Value.Tag.float_64.create(arena, @exp2(f));4573 return Value.Tag.float_64.create(arena, @exp2(f));
4584 },4574 },
4585 80 => {4575 80 => {
4586 if (true) {
4587 @panic("TODO implement compiler_rt exp2 for f80");
4588 }
4589 const f = val.toFloat(f80);4576 const f = val.toFloat(f80);
4590 return Value.Tag.float_80.create(arena, @exp2(f));4577 return Value.Tag.float_80.create(arena, @exp2(f));
4591 },4578 },
4592 128 => {4579 128 => {
4593 if (true) {
4594 @panic("TODO implement compiler_rt exp2 for f128");
4595 }
4596 const f = val.toFloat(f128);4580 const f = val.toFloat(f128);
4597 return Value.Tag.float_128.create(arena, @exp2(f));4581 return Value.Tag.float_128.create(arena, @exp2(f));
4598 },4582 },
...@@ -4626,16 +4610,10 @@ pub const Value = extern union {...@@ -4626,16 +4610,10 @@ pub const Value = extern union {
4626 return Value.Tag.float_64.create(arena, @log(f));4610 return Value.Tag.float_64.create(arena, @log(f));
4627 },4611 },
4628 80 => {4612 80 => {
4629 if (true) {
4630 @panic("TODO implement compiler_rt log for f80");
4631 }
4632 const f = val.toFloat(f80);4613 const f = val.toFloat(f80);
4633 return Value.Tag.float_80.create(arena, @log(f));4614 return Value.Tag.float_80.create(arena, @log(f));
4634 },4615 },
4635 128 => {4616 128 => {
4636 if (true) {
4637 @panic("TODO implement compiler_rt log for f128");
4638 }
4639 const f = val.toFloat(f128);4617 const f = val.toFloat(f128);
4640 return Value.Tag.float_128.create(arena, @log(f));4618 return Value.Tag.float_128.create(arena, @log(f));
4641 },4619 },
...@@ -4669,16 +4647,10 @@ pub const Value = extern union {...@@ -4669,16 +4647,10 @@ pub const Value = extern union {
4669 return Value.Tag.float_64.create(arena, @log2(f));4647 return Value.Tag.float_64.create(arena, @log2(f));
4670 },4648 },
4671 80 => {4649 80 => {
4672 if (true) {
4673 @panic("TODO implement compiler_rt log2 for f80");
4674 }
4675 const f = val.toFloat(f80);4650 const f = val.toFloat(f80);
4676 return Value.Tag.float_80.create(arena, @log2(f));4651 return Value.Tag.float_80.create(arena, @log2(f));
4677 },4652 },
4678 128 => {4653 128 => {
4679 if (true) {
4680 @panic("TODO implement compiler_rt log2 for f128");
4681 }
4682 const f = val.toFloat(f128);4654 const f = val.toFloat(f128);
4683 return Value.Tag.float_128.create(arena, @log2(f));4655 return Value.Tag.float_128.create(arena, @log2(f));
4684 },4656 },
...@@ -4712,16 +4684,10 @@ pub const Value = extern union {...@@ -4712,16 +4684,10 @@ pub const Value = extern union {
4712 return Value.Tag.float_64.create(arena, @log10(f));4684 return Value.Tag.float_64.create(arena, @log10(f));
4713 },4685 },
4714 80 => {4686 80 => {
4715 if (true) {
4716 @panic("TODO implement compiler_rt log10 for f80");
4717 }
4718 const f = val.toFloat(f80);4687 const f = val.toFloat(f80);
4719 return Value.Tag.float_80.create(arena, @log10(f));4688 return Value.Tag.float_80.create(arena, @log10(f));
4720 },4689 },
4721 128 => {4690 128 => {
4722 if (true) {
4723 @panic("TODO implement compiler_rt log10 for f128");
4724 }
4725 const f = val.toFloat(f128);4691 const f = val.toFloat(f128);
4726 return Value.Tag.float_128.create(arena, @log10(f));4692 return Value.Tag.float_128.create(arena, @log10(f));
4727 },4693 },
...@@ -4755,9 +4721,6 @@ pub const Value = extern union {...@@ -4755,9 +4721,6 @@ pub const Value = extern union {
4755 return Value.Tag.float_64.create(arena, @fabs(f));4721 return Value.Tag.float_64.create(arena, @fabs(f));
4756 },4722 },
4757 80 => {4723 80 => {
4758 if (true) {
4759 @panic("TODO implement compiler_rt fabs for f80 (__fabsx)");
4760 }
4761 const f = val.toFloat(f80);4724 const f = val.toFloat(f80);
4762 return Value.Tag.float_80.create(arena, @fabs(f));4725 return Value.Tag.float_80.create(arena, @fabs(f));
4763 },4726 },
...@@ -4795,9 +4758,6 @@ pub const Value = extern union {...@@ -4795,9 +4758,6 @@ pub const Value = extern union {
4795 return Value.Tag.float_64.create(arena, @floor(f));4758 return Value.Tag.float_64.create(arena, @floor(f));
4796 },4759 },
4797 80 => {4760 80 => {
4798 if (true) {
4799 @panic("TODO implement compiler_rt floor for f80 (__floorx)");
4800 }
4801 const f = val.toFloat(f80);4761 const f = val.toFloat(f80);
4802 return Value.Tag.float_80.create(arena, @floor(f));4762 return Value.Tag.float_80.create(arena, @floor(f));
4803 },4763 },
...@@ -4835,9 +4795,6 @@ pub const Value = extern union {...@@ -4835,9 +4795,6 @@ pub const Value = extern union {
4835 return Value.Tag.float_64.create(arena, @ceil(f));4795 return Value.Tag.float_64.create(arena, @ceil(f));
4836 },4796 },
4837 80 => {4797 80 => {
4838 if (true) {
4839 @panic("TODO implement compiler_rt ceil for f80");
4840 }
4841 const f = val.toFloat(f80);4798 const f = val.toFloat(f80);
4842 return Value.Tag.float_80.create(arena, @ceil(f));4799 return Value.Tag.float_80.create(arena, @ceil(f));
4843 },4800 },
...@@ -4875,9 +4832,6 @@ pub const Value = extern union {...@@ -4875,9 +4832,6 @@ pub const Value = extern union {
4875 return Value.Tag.float_64.create(arena, @round(f));4832 return Value.Tag.float_64.create(arena, @round(f));
4876 },4833 },
4877 80 => {4834 80 => {
4878 if (true) {
4879 @panic("TODO implement compiler_rt round for f80");
4880 }
4881 const f = val.toFloat(f80);4835 const f = val.toFloat(f80);
4882 return Value.Tag.float_80.create(arena, @round(f));4836 return Value.Tag.float_80.create(arena, @round(f));
4883 },4837 },
...@@ -4915,9 +4869,6 @@ pub const Value = extern union {...@@ -4915,9 +4869,6 @@ pub const Value = extern union {
4915 return Value.Tag.float_64.create(arena, @trunc(f));4869 return Value.Tag.float_64.create(arena, @trunc(f));
4916 },4870 },
4917 80 => {4871 80 => {
4918 if (true) {
4919 @panic("TODO implement compiler_rt trunc for f80");
4920 }
4921 const f = val.toFloat(f80);4872 const f = val.toFloat(f80);
4922 return Value.Tag.float_80.create(arena, @trunc(f));4873 return Value.Tag.float_80.create(arena, @trunc(f));
4923 },4874 },
src/zig_llvm.cpp+4
...@@ -541,6 +541,10 @@ LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRe...@@ -541,6 +541,10 @@ LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRe
541 return wrap(call_inst);541 return wrap(call_inst);
542}542}
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
544void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {548void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {
545 assert( isa<Function>(unwrap(fn)) );549 assert( isa<Function>(unwrap(fn)) );
546 Function *unwrapped_function = reinterpret_cast<Function*>(unwrap(fn));550 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...@@ -149,6 +149,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMulFixSat(LLVMBuilderRef B, LLVMValueRef
149ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMulFixSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name);149ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMulFixSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name);
150ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);150ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
151ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSShlSat(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);151ZIG_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
154ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,155ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
test/behavior/bugs/920.zig+10-4
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1const std = @import("std");1const std = @import("std");
2const math = std.math;
3const Random = std.rand.Random;2const Random = std.rand.Random;
43
5const ZigTable = struct {4const ZigTable = struct {
...@@ -40,10 +39,10 @@ const norm_r = 3.6541528853610088;...@@ -40,10 +39,10 @@ const norm_r = 3.6541528853610088;
40const norm_v = 0.00492867323399;39const norm_v = 0.00492867323399;
4140
42fn norm_f(x: f64) f64 {41fn norm_f(x: f64) f64 {
43 return math.exp(-x * x / 2.0);42 return @exp(-x * x / 2.0);
44}43}
45fn norm_f_inv(y: f64) f64 {44fn norm_f_inv(y: f64) f64 {
46 return math.sqrt(-2.0 * math.ln(y));45 return @sqrt(-2.0 * @log(y));
47}46}
48fn norm_zero_case(random: *Random, u: f64) f64 {47fn norm_zero_case(random: *Random, u: f64) f64 {
49 _ = random;48 _ = random;
...@@ -64,6 +63,13 @@ test "bug 920 fixed" {...@@ -64,6 +63,13 @@ test "bug 920 fixed" {
64 };63 };
6564
66 for (NormalDist1.f) |_, i| {65 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)));
68 }74 }
69}75}
test/behavior/floatop.zig+5-15
...@@ -609,16 +609,11 @@ test "negation f64" {...@@ -609,16 +609,11 @@ test "negation f64" {
609}609}
610610
611test "negation f80" {611test "negation f80" {
612 if (builtin.zig_backend != .stage1) {612 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
613 // This test case exercises @intToFloat f80 in the compiler implementation.613 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
614 // https://github.com/ziglang/zig/issues/11030614 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
615 return error.SkipZigTest;615 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
616 }616 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
617
618 if (builtin.os.tag == .freebsd) {
619 // TODO file issue to track this failure
620 return error.SkipZigTest;
621 }
622617
623 const S = struct {618 const S = struct {
624 fn doTheTest() !void {619 fn doTheTest() !void {
...@@ -641,11 +636,6 @@ test "negation f128" {...@@ -641,11 +636,6 @@ test "negation f128" {
641 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO636 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
642 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO637 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
649 const S = struct {639 const S = struct {
650 fn doTheTest() !void {640 fn doTheTest() !void {
651 var a: f128 = 1;641 var a: f128 = 1;
test/behavior/math.zig+184-75
...@@ -6,6 +6,7 @@ const expectEqualSlices = std.testing.expectEqualSlices;...@@ -6,6 +6,7 @@ const expectEqualSlices = std.testing.expectEqualSlices;
6const maxInt = std.math.maxInt;6const maxInt = std.math.maxInt;
7const minInt = std.math.minInt;7const minInt = std.math.minInt;
8const mem = std.mem;8const mem = std.mem;
9const math = std.math;
910
10test "assignment operators" {11test "assignment operators" {
11 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO12 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
...@@ -909,32 +910,52 @@ test "comptime float rem int" {...@@ -909,32 +910,52 @@ test "comptime float rem int" {
909}910}
910911
911test "remainder division" {912test "remainder division" {
912 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO913 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
914 comptime try remdiv(f16);919 comptime try remdiv(f16);
915 comptime try remdiv(f32);920 comptime try remdiv(f32);
916 comptime try remdiv(f64);921 comptime try remdiv(f64);
922 comptime try remdiv(f80);
917 comptime try remdiv(f128);923 comptime try remdiv(f128);
918 try remdiv(f16);924 try remdiv(f16);
919 try remdiv(f64);925 try remdiv(f64);
926 try remdiv(f80);
920 try remdiv(f128);927 try remdiv(f128);
921}928}
922929
923fn remdiv(comptime T: type) !void {930fn remdiv(comptime T: type) !void {
924 try expect(@as(T, 1) == @as(T, 1) % @as(T, 2));931 try expect(@as(T, 1) == @as(T, 1) % @as(T, 2));
932 try remdivOne(T, 1, 1, 2);
933
925 try expect(@as(T, 1) == @as(T, 7) % @as(T, 3));934 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));
926}941}
927942
928test "float remainder division using @rem" {943test "float remainder division using @rem" {
929 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO944 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
931 comptime try frem(f16);950 comptime try frem(f16);
932 comptime try frem(f32);951 comptime try frem(f32);
933 comptime try frem(f64);952 comptime try frem(f64);
953 comptime try frem(f80);
934 comptime try frem(f128);954 comptime try frem(f128);
935 try frem(f16);955 try frem(f16);
936 try frem(f32);956 try frem(f32);
937 try frem(f64);957 try frem(f64);
958 try frem(f80);
938 try frem(f128);959 try frem(f128);
939}960}
940961
...@@ -943,29 +964,40 @@ fn frem(comptime T: type) !void {...@@ -943,29 +964,40 @@ fn frem(comptime T: type) !void {
943 f16 => 1.0,964 f16 => 1.0,
944 f32 => 0.001,965 f32 => 0.001,
945 f64 => 0.00001,966 f64 => 0.00001,
967 f80 => 0.000001,
946 f128 => 0.0000001,968 f128 => 0.0000001,
947 else => unreachable,969 else => unreachable,
948 };970 };
949971
950 try expect(std.math.fabs(@rem(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon);972 try fremOne(T, 6.9, 4.0, 2.9, epsilon);
951 try expect(std.math.fabs(@rem(@as(T, -6.9), @as(T, 4.0)) - @as(T, -2.9)) < epsilon);973 try fremOne(T, -6.9, 4.0, -2.9, epsilon);
952 try expect(std.math.fabs(@rem(@as(T, -5.0), @as(T, 3.0)) - @as(T, -2.0)) < epsilon);974 try fremOne(T, -5.0, 3.0, -2.0, epsilon);
953 try expect(std.math.fabs(@rem(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);975 try fremOne(T, 3.0, 2.0, 1.0, epsilon);
954 try expect(std.math.fabs(@rem(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);976 try fremOne(T, 1.0, 2.0, 1.0, epsilon);
955 try expect(std.math.fabs(@rem(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon);977 try fremOne(T, 0.0, 1.0, 0.0, epsilon);
956 try expect(std.math.fabs(@rem(@as(T, -0.0), @as(T, 1.0)) - @as(T, -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);
957}983}
958984
959test "float modulo division using @mod" {985test "float modulo division using @mod" {
960 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO986 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
962 comptime try fmod(f16);992 comptime try fmod(f16);
963 comptime try fmod(f32);993 comptime try fmod(f32);
964 comptime try fmod(f64);994 comptime try fmod(f64);
995 comptime try fmod(f80);
965 comptime try fmod(f128);996 comptime try fmod(f128);
966 try fmod(f16);997 try fmod(f16);
967 try fmod(f32);998 try fmod(f32);
968 try fmod(f64);999 try fmod(f64);
1000 try fmod(f80);
969 try fmod(f128);1001 try fmod(f128);
970}1002}
9711003
...@@ -974,17 +1006,22 @@ fn fmod(comptime T: type) !void {...@@ -974,17 +1006,22 @@ fn fmod(comptime T: type) !void {
974 f16 => 1.0,1006 f16 => 1.0,
975 f32 => 0.001,1007 f32 => 0.001,
976 f64 => 0.00001,1008 f64 => 0.00001,
1009 f80 => 0.000001,
977 f128 => 0.0000001,1010 f128 => 0.0000001,
978 else => unreachable,1011 else => unreachable,
979 };1012 };
9801013
981 try expect(std.math.fabs(@mod(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon);1014 try fmodOne(T, 6.9, 4.0, 2.9, epsilon);
982 try expect(std.math.fabs(@mod(@as(T, -6.9), @as(T, 4.0)) - @as(T, 1.1)) < epsilon);1015 try fmodOne(T, -6.9, 4.0, 1.1, epsilon);
983 try expect(std.math.fabs(@mod(@as(T, -5.0), @as(T, 3.0)) - @as(T, 1.0)) < epsilon);1016 try fmodOne(T, -5.0, 3.0, 1.0, epsilon);
984 try expect(std.math.fabs(@mod(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);1017 try fmodOne(T, 3.0, 2.0, 1.0, epsilon);
985 try expect(std.math.fabs(@mod(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon);1018 try fmodOne(T, 1.0, 2.0, 1.0, epsilon);
986 try expect(std.math.fabs(@mod(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon);1019 try fmodOne(T, 0.0, 1.0, 0.0, epsilon);
987 try expect(std.math.fabs(@mod(@as(T, -0.0), @as(T, 1.0)) - @as(T, -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);
988}1025}
9891026
990test "@sqrt" {1027test "@sqrt" {
...@@ -1036,10 +1073,11 @@ test "@fabs" {...@@ -1036,10 +1073,11 @@ test "@fabs" {
1036}1073}
10371074
1038test "@fabs f80" {1075test "@fabs f80" {
1039 if (true) {1076 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1040 // https://github.com/ziglang/zig/issues/110301077 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1041 return error.SkipZigTest;1078 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1042 }1079 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1080 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10431081
1044 try testFabs(f80, 12.0);1082 try testFabs(f80, 12.0);
1045 comptime try testFabs(f80, 12.0);1083 comptime try testFabs(f80, 12.0);
...@@ -1052,7 +1090,11 @@ fn testFabs(comptime T: type, x: T) !void {...@@ -1052,7 +1090,11 @@ fn testFabs(comptime T: type, x: T) !void {
1052}1090}
10531091
1054test "@floor" {1092test "@floor" {
1055 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO1093 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
1057 try testFloor(f64, 12.0);1099 try testFloor(f64, 12.0);
1058 comptime try testFloor(f64, 12.0);1100 comptime try testFloor(f64, 12.0);
...@@ -1068,23 +1110,24 @@ test "@floor" {...@@ -1068,23 +1110,24 @@ test "@floor" {
1068}1110}
10691111
1070test "@floor f80" {1112test "@floor f80" {
1071 if (true) {1113 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1072 // https://github.com/ziglang/zig/issues/110301114 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1073 return error.SkipZigTest;1115 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1074 }1116 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1117 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1118
1075 try testFloor(f80, 12.0);1119 try testFloor(f80, 12.0);
1076 comptime try testFloor(f80, 12.0);1120 comptime try testFloor(f80, 12.0);
1077}1121}
10781122
1079test "@floor f128" {1123test "@floor f128" {
1080 if (builtin.zig_backend == .stage1) {1124 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1081 // Fails because it incorrectly lowers to a floorl function call.1125 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1082 return error.SkipZigTest;1126 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1083 }1127 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10841128 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1085 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
10861129
1087 testFloor(f128, 12.0);1130 try testFloor(f128, 12.0);
1088 comptime try testFloor(f128, 12.0);1131 comptime try testFloor(f128, 12.0);
1089}1132}
10901133
...@@ -1095,7 +1138,11 @@ fn testFloor(comptime T: type, x: T) !void {...@@ -1095,7 +1138,11 @@ fn testFloor(comptime T: type, x: T) !void {
1095}1138}
10961139
1097test "@ceil" {1140test "@ceil" {
1098 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO1141 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
1100 try testCeil(f64, 12.0);1147 try testCeil(f64, 12.0);
1101 comptime try testCeil(f64, 12.0);1148 comptime try testCeil(f64, 12.0);
...@@ -1111,24 +1158,24 @@ test "@ceil" {...@@ -1111,24 +1158,24 @@ test "@ceil" {
1111}1158}
11121159
1113test "@ceil f80" {1160test "@ceil f80" {
1114 if (true) {1161 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1115 // https://github.com/ziglang/zig/issues/110301162 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1116 return error.SkipZigTest;1163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1117 }1164 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1165 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11181166
1119 try testCeil(f80, 12.0);1167 try testCeil(f80, 12.0);
1120 comptime try testCeil(f80, 12.0);1168 comptime try testCeil(f80, 12.0);
1121}1169}
11221170
1123test "@ceil f128" {1171test "@ceil f128" {
1124 if (builtin.zig_backend == .stage1) {1172 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1125 // Fails because it incorrectly lowers to a ceill function call.1173 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1126 return error.SkipZigTest;1174 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1127 }1175 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11281176 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1129 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
11301177
1131 testCeil(f128, 12.0);1178 try testCeil(f128, 12.0);
1132 comptime try testCeil(f128, 12.0);1179 comptime try testCeil(f128, 12.0);
1133}1180}
11341181
...@@ -1139,7 +1186,11 @@ fn testCeil(comptime T: type, x: T) !void {...@@ -1139,7 +1186,11 @@ fn testCeil(comptime T: type, x: T) !void {
1139}1186}
11401187
1141test "@trunc" {1188test "@trunc" {
1142 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO1189 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
1144 try testTrunc(f64, 12.0);1195 try testTrunc(f64, 12.0);
1145 comptime try testTrunc(f64, 12.0);1196 comptime try testTrunc(f64, 12.0);
...@@ -1155,10 +1206,11 @@ test "@trunc" {...@@ -1155,10 +1206,11 @@ test "@trunc" {
1155}1206}
11561207
1157test "@trunc f80" {1208test "@trunc f80" {
1158 if (true) {1209 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1159 // https://github.com/ziglang/zig/issues/110301210 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1160 return error.SkipZigTest;1211 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1161 }1212 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1213 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11621214
1163 try testTrunc(f80, 12.0);1215 try testTrunc(f80, 12.0);
1164 comptime try testTrunc(f80, 12.0);1216 comptime try testTrunc(f80, 12.0);
...@@ -1171,14 +1223,13 @@ test "@trunc f80" {...@@ -1171,14 +1223,13 @@ test "@trunc f80" {
1171}1223}
11721224
1173test "@trunc f128" {1225test "@trunc f128" {
1174 if (builtin.zig_backend == .stage1) {1226 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1175 // Fails because it incorrectly lowers to a truncl function call.1227 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1176 return error.SkipZigTest;1228 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1177 }1229 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11781230 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1179 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
11801231
1181 testTrunc(f128, 12.0);1232 try testTrunc(f128, 12.0);
1182 comptime try testTrunc(f128, 12.0);1233 comptime try testTrunc(f128, 12.0);
1183}1234}
11841235
...@@ -1197,7 +1248,11 @@ fn testTrunc(comptime T: type, x: T) !void {...@@ -1197,7 +1248,11 @@ fn testTrunc(comptime T: type, x: T) !void {
1197}1248}
11981249
1199test "@round" {1250test "@round" {
1200 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO1251 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
1202 try testRound(f64, 12.0);1257 try testRound(f64, 12.0);
1203 comptime try testRound(f64, 12.0);1258 comptime try testRound(f64, 12.0);
...@@ -1213,24 +1268,24 @@ test "@round" {...@@ -1213,24 +1268,24 @@ test "@round" {
1213}1268}
12141269
1215test "@round f80" {1270test "@round f80" {
1216 if (true) {1271 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1217 // https://github.com/ziglang/zig/issues/110301272 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1218 return error.SkipZigTest;1273 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1219 }1274 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1275 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
12201276
1221 try testRound(f80, 12.0);1277 try testRound(f80, 12.0);
1222 comptime try testRound(f80, 12.0);1278 comptime try testRound(f80, 12.0);
1223}1279}
12241280
1225test "@round f128" {1281test "@round f128" {
1226 if (builtin.zig_backend == .stage1) {1282 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1227 // Fails because it incorrectly lowers to a roundl function call.1283 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1228 return error.SkipZigTest;1284 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1229 }1285 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12301286 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1231 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
12321287
1233 testRound(f128, 12.0);1288 try testRound(f128, 12.0);
1234 comptime try testRound(f128, 12.0);1289 comptime try testRound(f128, 12.0);
1235}1290}
12361291
...@@ -1279,17 +1334,19 @@ test "NaN comparison" {...@@ -1279,17 +1334,19 @@ test "NaN comparison" {
1279}1334}
12801335
1281test "NaN comparison f80" {1336test "NaN comparison f80" {
1282 if (true) {1337 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1283 // https://github.com/ziglang/zig/issues/110301338 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1284 return error.SkipZigTest;1339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1285 }1340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1341 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1342
1286 try testNanEqNan(f80);1343 try testNanEqNan(f80);
1287 comptime try testNanEqNan(f80);1344 comptime try testNanEqNan(f80);
1288}1345}
12891346
1290fn testNanEqNan(comptime F: type) !void {1347fn testNanEqNan(comptime F: type) !void {
1291 var nan1 = std.math.nan(F);1348 var nan1 = math.nan(F);
1292 var nan2 = std.math.nan(F);1349 var nan2 = math.nan(F);
1293 try expect(nan1 != nan2);1350 try expect(nan1 != nan2);
1294 try expect(!(nan1 == nan2));1351 try expect(!(nan1 == nan2));
1295 try expect(!(nan1 > nan2));1352 try expect(!(nan1 > nan2));
...@@ -1346,3 +1403,55 @@ test "signed zeros are represented properly" {...@@ -1346,3 +1403,55 @@ test "signed zeros are represented properly" {
1346 try S.doTheTest();1403 try S.doTheTest();
1347 comptime try S.doTheTest();1404 comptime try S.doTheTest();
1348}1405}
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" {...@@ -32,6 +32,7 @@ test "@mulAdd f16" {
32 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO32 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
33 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO33 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
34 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO34 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
35
35 comptime try testMulAdd16();36 comptime try testMulAdd16();
36 try testMulAdd16();37 try testMulAdd16();
37}38}
...@@ -44,10 +45,12 @@ fn testMulAdd16() !void {...@@ -44,10 +45,12 @@ fn testMulAdd16() !void {
44}45}
4546
46test "@mulAdd f80" {47test "@mulAdd f80" {
47 if (true) {48 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
48 // https://github.com/ziglang/zig/issues/1103049 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
49 return error.SkipZigTest;50 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
50 }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
52 comptime try testMulAdd80();55 comptime try testMulAdd80();
53 try testMulAdd80();56 try testMulAdd80();
...@@ -173,10 +176,12 @@ fn vector80() !void {...@@ -173,10 +176,12 @@ fn vector80() !void {
173}176}
174177
175test "vector f80" {178test "vector f80" {
176 if (true) {179 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
177 // https://github.com/ziglang/zig/issues/11030180 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
178 return error.SkipZigTest;181 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
179 }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
181 comptime try vector80();186 comptime try vector80();
182 try vector80();187 try vector80();