zig/lib/compiler_rt/round.zig
Andrew Kelley cd019ee502 compiler_rt: avoid weak aliases on Windows
When exporting math functions for Windows, we provide weak exports of
'l' variants rather than weak aliases. We still use aliases on other
operating systems so that the 'l' variants have one less jump
instruction in this case.
2022-05-08 13:06:21 -07:00

181 lines
4.1 KiB
Zig

// Ported from musl, which is licensed under the MIT license:
// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
// https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c
// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c
const std = @import("std");
const math = std.math;
const expect = std.testing.expect;
pub fn __roundh(x: f16) callconv(.C) f16 {
// TODO: more efficient implementation
return @floatCast(f16, roundf(x));
}
pub fn roundf(x_: f32) callconv(.C) f32 {
const f32_toint = 1.0 / math.floatEps(f32);
var x = x_;
const u = @bitCast(u32, x);
const e = (u >> 23) & 0xFF;
var y: f32 = undefined;
if (e >= 0x7F + 23) {
return x;
}
if (u >> 31 != 0) {
x = -x;
}
if (e < 0x7F - 1) {
math.doNotOptimizeAway(x + f32_toint);
return 0 * @bitCast(f32, u);
}
y = x + f32_toint - f32_toint - x;
if (y > 0.5) {
y = y + x - 1;
} else if (y <= -0.5) {
y = y + x + 1;
} else {
y = y + x;
}
if (u >> 31 != 0) {
return -y;
} else {
return y;
}
}
pub fn round(x_: f64) callconv(.C) f64 {
const f64_toint = 1.0 / math.floatEps(f64);
var x = x_;
const u = @bitCast(u64, x);
const e = (u >> 52) & 0x7FF;
var y: f64 = undefined;
if (e >= 0x3FF + 52) {
return x;
}
if (u >> 63 != 0) {
x = -x;
}
if (e < 0x3ff - 1) {
math.doNotOptimizeAway(x + f64_toint);
return 0 * @bitCast(f64, u);
}
y = x + f64_toint - f64_toint - x;
if (y > 0.5) {
y = y + x - 1;
} else if (y <= -0.5) {
y = y + x + 1;
} else {
y = y + x;
}
if (u >> 63 != 0) {
return -y;
} else {
return y;
}
}
pub fn __roundx(x: f80) callconv(.C) f80 {
// TODO: more efficient implementation
return @floatCast(f80, roundq(x));
}
pub fn roundq(x_: f128) callconv(.C) f128 {
const f128_toint = 1.0 / math.floatEps(f128);
var x = x_;
const u = @bitCast(u128, x);
const e = (u >> 112) & 0x7FFF;
var y: f128 = undefined;
if (e >= 0x3FFF + 112) {
return x;
}
if (u >> 127 != 0) {
x = -x;
}
if (e < 0x3FFF - 1) {
math.doNotOptimizeAway(x + f128_toint);
return 0 * @bitCast(f128, u);
}
y = x + f128_toint - f128_toint - x;
if (y > 0.5) {
y = y + x - 1;
} else if (y <= -0.5) {
y = y + x + 1;
} else {
y = y + x;
}
if (u >> 127 != 0) {
return -y;
} else {
return y;
}
}
pub fn roundl(x: c_longdouble) callconv(.C) c_longdouble {
switch (@typeInfo(c_longdouble).Float.bits) {
16 => return __roundh(x),
32 => return roundf(x),
64 => return round(x),
80 => return __roundx(x),
128 => return roundq(x),
else => @compileError("unreachable"),
}
}
test "round32" {
try expect(roundf(1.3) == 1.0);
try expect(roundf(-1.3) == -1.0);
try expect(roundf(0.2) == 0.0);
try expect(roundf(1.8) == 2.0);
}
test "round64" {
try expect(round(1.3) == 1.0);
try expect(round(-1.3) == -1.0);
try expect(round(0.2) == 0.0);
try expect(round(1.8) == 2.0);
}
test "round128" {
try expect(roundq(1.3) == 1.0);
try expect(roundq(-1.3) == -1.0);
try expect(roundq(0.2) == 0.0);
try expect(roundq(1.8) == 2.0);
}
test "round32.special" {
try expect(roundf(0.0) == 0.0);
try expect(roundf(-0.0) == -0.0);
try expect(math.isPositiveInf(roundf(math.inf(f32))));
try expect(math.isNegativeInf(roundf(-math.inf(f32))));
try expect(math.isNan(roundf(math.nan(f32))));
}
test "round64.special" {
try expect(round(0.0) == 0.0);
try expect(round(-0.0) == -0.0);
try expect(math.isPositiveInf(round(math.inf(f64))));
try expect(math.isNegativeInf(round(-math.inf(f64))));
try expect(math.isNan(round(math.nan(f64))));
}
test "round128.special" {
try expect(roundq(0.0) == 0.0);
try expect(roundq(-0.0) == -0.0);
try expect(math.isPositiveInf(roundq(math.inf(f128))));
try expect(math.isNegativeInf(roundq(-math.inf(f128))));
try expect(math.isNan(roundq(math.nan(f128))));
}