mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
compiler-rt: fix f80 ceil/floor optimization
Our implementation did the classic add-sub rounding trick `(y = x +/- C =+ C - x)` with `C = 1 / eps(T) = 2^(mantissa - 1)`. This approach only works for values whose magnitude is below the rounding capacity of the constant. For a 64-bit mantissa (like f80 has), `C = 2^63` only rounds for `|x| < 2^63`. Before we allowed this to be ran on `e < bias + 64` aka `|x| < 2^64`. And because it isn't large enough, we lose a bit to rounding. For reference, the musl implementation does the same thing, using `mantissa - 1`: https://git.musl-libc.org/cgit/musl/tree/src/math/ceill.c#n18 where `LDBL_MANT_DIG` is 64 for `long double` on x86. This commit also combines the floor and ceil implementations into one generic one, excluding the `f16`/`f32` ones, as those are a bit special, although they could be generalized in a similar fashion.
This commit is contained in:
parent
a38220376e
commit
65f9e6359f
6 changed files with 306 additions and 480 deletions
|
|
@ -221,7 +221,6 @@ set(ZIG_STAGE2_SOURCES
|
|||
lib/compiler_rt/aulldiv.zig
|
||||
lib/compiler_rt/aullrem.zig
|
||||
lib/compiler_rt/bswap.zig
|
||||
lib/compiler_rt/ceil.zig
|
||||
lib/compiler_rt/clear_cache.zig
|
||||
lib/compiler_rt/cmp.zig
|
||||
lib/compiler_rt/cmpdf2.zig
|
||||
|
|
@ -312,7 +311,7 @@ set(ZIG_STAGE2_SOURCES
|
|||
lib/compiler_rt/floatuntisf.zig
|
||||
lib/compiler_rt/floatuntitf.zig
|
||||
lib/compiler_rt/floatuntixf.zig
|
||||
lib/compiler_rt/floor.zig
|
||||
lib/compiler_rt/floor_ceil.zig
|
||||
lib/compiler_rt/fma.zig
|
||||
lib/compiler_rt/fmax.zig
|
||||
lib/compiler_rt/fmin.zig
|
||||
|
|
|
|||
|
|
@ -215,12 +215,11 @@ comptime {
|
|||
_ = @import("compiler_rt/divtc3.zig");
|
||||
|
||||
// Math routines. Alphabetically sorted.
|
||||
_ = @import("compiler_rt/ceil.zig");
|
||||
_ = @import("compiler_rt/cos.zig");
|
||||
_ = @import("compiler_rt/exp.zig");
|
||||
_ = @import("compiler_rt/exp2.zig");
|
||||
_ = @import("compiler_rt/fabs.zig");
|
||||
_ = @import("compiler_rt/floor.zig");
|
||||
_ = @import("compiler_rt/floor_ceil.zig");
|
||||
_ = @import("compiler_rt/fma.zig");
|
||||
_ = @import("compiler_rt/fmax.zig");
|
||||
_ = @import("compiler_rt/fmin.zig");
|
||||
|
|
|
|||
|
|
@ -1,238 +0,0 @@
|
|||
//! Ported from musl, which is MIT licensed.
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
|
||||
//!
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceill.c
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const arch = builtin.cpu.arch;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const expect = std.testing.expect;
|
||||
const common = @import("common.zig");
|
||||
|
||||
pub const panic = common.panic;
|
||||
|
||||
comptime {
|
||||
@export(&__ceilh, .{ .name = "__ceilh", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&ceilf, .{ .name = "ceilf", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&ceil, .{ .name = "ceil", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&__ceilx, .{ .name = "__ceilx", .linkage = common.linkage, .visibility = common.visibility });
|
||||
if (common.want_ppc_abi) {
|
||||
@export(&ceilq, .{ .name = "ceilf128", .linkage = common.linkage, .visibility = common.visibility });
|
||||
}
|
||||
@export(&ceilq, .{ .name = "ceilq", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&ceill, .{ .name = "ceill", .linkage = common.linkage, .visibility = common.visibility });
|
||||
}
|
||||
|
||||
pub fn __ceilh(x: f16) callconv(.c) f16 {
|
||||
var u: u16 = @bitCast(x);
|
||||
const e = @as(i16, @intCast((u >> 10) & 31)) - 15;
|
||||
var m: u16 = undefined;
|
||||
|
||||
if (e >= 10) return x;
|
||||
|
||||
if (e >= 0) {
|
||||
m = @as(u16, 0x03FF) >> @intCast(e);
|
||||
if (u & m == 0) return x;
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
if (u >> 15 == 0) u += m;
|
||||
u &= ~m;
|
||||
return @bitCast(u);
|
||||
} else {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
return if (u >> 15 != 0) -0.0 else if (u << 1 != 0) 1.0 else x;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceilf(x: f32) callconv(.c) f32 {
|
||||
var u: u32 = @bitCast(x);
|
||||
const e = @as(i32, @intCast((u >> 23) & 0xFF)) - 0x7F;
|
||||
var m: u32 = undefined;
|
||||
|
||||
if (e >= 23) return x;
|
||||
|
||||
if (e >= 0) {
|
||||
m = @as(u32, 0x007FFFFF) >> @intCast(e);
|
||||
if (u & m == 0) return x;
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
if (u >> 31 == 0) u += m;
|
||||
u &= ~m;
|
||||
return @bitCast(u);
|
||||
} else {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
return if (u >> 31 != 0) -0.0 else if (u << 1 != 0) 1.0 else x;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceil(x: f64) callconv(.c) f64 {
|
||||
const f64_toint = 1.0 / math.floatEps(f64);
|
||||
|
||||
const u: u64 = @bitCast(x);
|
||||
const e = (u >> 52) & 0x7FF;
|
||||
var y: f64 = undefined;
|
||||
|
||||
if (e >= 0x3FF + 52 or x == 0) {
|
||||
return x;
|
||||
}
|
||||
|
||||
if (u >> 63 != 0) {
|
||||
y = x - f64_toint + f64_toint - x;
|
||||
} else {
|
||||
y = x + f64_toint - f64_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FF - 1) {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(y);
|
||||
if (u >> 63 != 0) {
|
||||
return -0.0;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
} else if (y < 0) {
|
||||
return x + y + 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn __ceilx(x: f80) callconv(.c) f80 {
|
||||
const f80_toint = 1.0 / math.floatEps(f80);
|
||||
|
||||
const u: u80 = @bitCast(x);
|
||||
const e = (u >> 64) & 0x7FFF;
|
||||
var y: f80 = undefined;
|
||||
|
||||
if (e >= 0x3FFF + 64 or x == 0) return x;
|
||||
|
||||
if (u >> 79 != 0) {
|
||||
y = x - f80_toint + f80_toint - x;
|
||||
} else {
|
||||
y = x + f80_toint - f80_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FFF - 1) {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(y);
|
||||
if (u >> 79 != 0) {
|
||||
return -0.0;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
} else if (y < 0) {
|
||||
return x + y + 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceilq(x: f128) callconv(.c) f128 {
|
||||
const f128_toint = 1.0 / math.floatEps(f128);
|
||||
|
||||
const u: u128 = @bitCast(x);
|
||||
const e = (u >> 112) & 0x7FFF;
|
||||
var y: f128 = undefined;
|
||||
|
||||
if (e >= 0x3FFF + 112 or x == 0) return x;
|
||||
|
||||
if (u >> 127 != 0) {
|
||||
y = x - f128_toint + f128_toint - x;
|
||||
} else {
|
||||
y = x + f128_toint - f128_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FFF - 1) {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(y);
|
||||
if (u >> 127 != 0) {
|
||||
return -0.0;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
} else if (y < 0) {
|
||||
return x + y + 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ceill(x: c_longdouble) callconv(.c) c_longdouble {
|
||||
switch (@typeInfo(c_longdouble).float.bits) {
|
||||
16 => return __ceilh(x),
|
||||
32 => return ceilf(x),
|
||||
64 => return ceil(x),
|
||||
80 => return __ceilx(x),
|
||||
128 => return ceilq(x),
|
||||
else => @compileError("unreachable"),
|
||||
}
|
||||
}
|
||||
|
||||
test "ceil16" {
|
||||
try expect(__ceilh(1.3) == 2.0);
|
||||
try expect(__ceilh(-1.3) == -1.0);
|
||||
try expect(__ceilh(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil32" {
|
||||
try expect(ceilf(1.3) == 2.0);
|
||||
try expect(ceilf(-1.3) == -1.0);
|
||||
try expect(ceilf(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil64" {
|
||||
try expect(ceil(1.3) == 2.0);
|
||||
try expect(ceil(-1.3) == -1.0);
|
||||
try expect(ceil(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil80" {
|
||||
try expect(__ceilx(1.3) == 2.0);
|
||||
try expect(__ceilx(-1.3) == -1.0);
|
||||
try expect(__ceilx(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil128" {
|
||||
try expect(ceilq(1.3) == 2.0);
|
||||
try expect(ceilq(-1.3) == -1.0);
|
||||
try expect(ceilq(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil16.special" {
|
||||
try expect(__ceilh(0.0) == 0.0);
|
||||
try expect(__ceilh(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__ceilh(math.inf(f16))));
|
||||
try expect(math.isNegativeInf(__ceilh(-math.inf(f16))));
|
||||
try expect(math.isNan(__ceilh(math.nan(f16))));
|
||||
}
|
||||
|
||||
test "ceil32.special" {
|
||||
try expect(ceilf(0.0) == 0.0);
|
||||
try expect(ceilf(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(ceilf(math.inf(f32))));
|
||||
try expect(math.isNegativeInf(ceilf(-math.inf(f32))));
|
||||
try expect(math.isNan(ceilf(math.nan(f32))));
|
||||
}
|
||||
|
||||
test "ceil64.special" {
|
||||
try expect(ceil(0.0) == 0.0);
|
||||
try expect(ceil(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(ceil(math.inf(f64))));
|
||||
try expect(math.isNegativeInf(ceil(-math.inf(f64))));
|
||||
try expect(math.isNan(ceil(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "ceil80.special" {
|
||||
try expect(__ceilx(0.0) == 0.0);
|
||||
try expect(__ceilx(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__ceilx(math.inf(f80))));
|
||||
try expect(math.isNegativeInf(__ceilx(-math.inf(f80))));
|
||||
try expect(math.isNan(__ceilx(math.nan(f80))));
|
||||
}
|
||||
|
||||
test "ceil128.special" {
|
||||
try expect(ceilq(0.0) == 0.0);
|
||||
try expect(ceilq(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(ceilq(math.inf(f128))));
|
||||
try expect(math.isNegativeInf(ceilq(-math.inf(f128))));
|
||||
try expect(math.isNan(ceilq(math.nan(f128))));
|
||||
}
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
//! 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/floorf.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/floorl.c
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const expect = std.testing.expect;
|
||||
const arch = builtin.cpu.arch;
|
||||
const common = @import("common.zig");
|
||||
|
||||
pub const panic = common.panic;
|
||||
|
||||
comptime {
|
||||
@export(&__floorh, .{ .name = "__floorh", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&floorf, .{ .name = "floorf", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&floor, .{ .name = "floor", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&__floorx, .{ .name = "__floorx", .linkage = common.linkage, .visibility = common.visibility });
|
||||
if (common.want_ppc_abi) {
|
||||
@export(&floorq, .{ .name = "floorf128", .linkage = common.linkage, .visibility = common.visibility });
|
||||
}
|
||||
@export(&floorq, .{ .name = "floorq", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&floorl, .{ .name = "floorl", .linkage = common.linkage, .visibility = common.visibility });
|
||||
}
|
||||
|
||||
pub fn __floorh(x: f16) callconv(.c) f16 {
|
||||
var u: u16 = @bitCast(x);
|
||||
const e = @as(i16, @intCast((u >> 10) & 31)) - 15;
|
||||
var m: u16 = undefined;
|
||||
|
||||
if (e >= 10) return x;
|
||||
|
||||
if (e >= 0) {
|
||||
m = @as(u16, 0x03FF) >> @intCast(e);
|
||||
if (u & m == 0) return x;
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
if (u >> 15 != 0) u += m;
|
||||
return @bitCast(u & ~m);
|
||||
} else {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
return if (u >> 15 == 0) 0.0 else if (u << 1 != 0) -1.0 else x;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floorf(x: f32) callconv(.c) f32 {
|
||||
var u: u32 = @bitCast(x);
|
||||
const e = @as(i32, @intCast((u >> 23) & 0xFF)) - 0x7F;
|
||||
var m: u32 = undefined;
|
||||
|
||||
if (e >= 23) return x;
|
||||
|
||||
if (e >= 0) {
|
||||
m = @as(u32, 0x007FFFFF) >> @intCast(e);
|
||||
if (u & m == 0) return x;
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
if (u >> 31 != 0) u += m;
|
||||
return @bitCast(u & ~m);
|
||||
} else {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
return if (u >> 31 == 0) 0.0 else if (u << 1 != 0) -1.0 else x;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floor(x: f64) callconv(.c) f64 {
|
||||
const f64_toint = 1.0 / math.floatEps(f64);
|
||||
|
||||
const u: u64 = @bitCast(x);
|
||||
const e = (u >> 52) & 0x7FF;
|
||||
var y: f64 = undefined;
|
||||
|
||||
if (e >= 0x3FF + 52 or x == 0) {
|
||||
return x;
|
||||
}
|
||||
|
||||
if (u >> 63 != 0) {
|
||||
y = x - f64_toint + f64_toint - x;
|
||||
} else {
|
||||
y = x + f64_toint - f64_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FF - 1) {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(y);
|
||||
if (u >> 63 != 0) {
|
||||
return -1.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
} else if (y > 0) {
|
||||
return x + y - 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn __floorx(x: f80) callconv(.c) f80 {
|
||||
const f80_toint = 1.0 / math.floatEps(f80);
|
||||
|
||||
const u: u80 = @bitCast(x);
|
||||
const e = (u >> 64) & 0x7FFF;
|
||||
var y: f80 = undefined;
|
||||
|
||||
if (e >= 0x3FFF + 64 or x == 0) {
|
||||
return x;
|
||||
}
|
||||
|
||||
if (u >> 79 != 0) {
|
||||
y = x - f80_toint + f80_toint - x;
|
||||
} else {
|
||||
y = x + f80_toint - f80_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FFF - 1) {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(y);
|
||||
if (u >> 79 != 0) {
|
||||
return -1.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
} else if (y > 0) {
|
||||
return x + y - 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floorq(x: f128) callconv(.c) f128 {
|
||||
const f128_toint = 1.0 / math.floatEps(f128);
|
||||
|
||||
const u: u128 = @bitCast(x);
|
||||
const e = (u >> 112) & 0x7FFF;
|
||||
var y: f128 = undefined;
|
||||
|
||||
if (e >= 0x3FFF + 112 or x == 0) return x;
|
||||
|
||||
if (u >> 127 != 0) {
|
||||
y = x - f128_toint + f128_toint - x;
|
||||
} else {
|
||||
y = x + f128_toint - f128_toint - x;
|
||||
}
|
||||
|
||||
if (e <= 0x3FFF - 1) {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(y);
|
||||
if (u >> 127 != 0) {
|
||||
return -1.0;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
} else if (y > 0) {
|
||||
return x + y - 1;
|
||||
} else {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floorl(x: c_longdouble) callconv(.c) c_longdouble {
|
||||
switch (@typeInfo(c_longdouble).float.bits) {
|
||||
16 => return __floorh(x),
|
||||
32 => return floorf(x),
|
||||
64 => return floor(x),
|
||||
80 => return __floorx(x),
|
||||
128 => return floorq(x),
|
||||
else => @compileError("unreachable"),
|
||||
}
|
||||
}
|
||||
|
||||
test "floor16" {
|
||||
try expect(__floorh(1.3) == 1.0);
|
||||
try expect(__floorh(-1.3) == -2.0);
|
||||
try expect(__floorh(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor32" {
|
||||
try expect(floorf(1.3) == 1.0);
|
||||
try expect(floorf(-1.3) == -2.0);
|
||||
try expect(floorf(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor64" {
|
||||
try expect(floor(1.3) == 1.0);
|
||||
try expect(floor(-1.3) == -2.0);
|
||||
try expect(floor(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor80" {
|
||||
try expect(__floorx(1.3) == 1.0);
|
||||
try expect(__floorx(-1.3) == -2.0);
|
||||
try expect(__floorx(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor128" {
|
||||
try expect(floorq(1.3) == 1.0);
|
||||
try expect(floorq(-1.3) == -2.0);
|
||||
try expect(floorq(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor16.special" {
|
||||
try expect(__floorh(0.0) == 0.0);
|
||||
try expect(__floorh(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__floorh(math.inf(f16))));
|
||||
try expect(math.isNegativeInf(__floorh(-math.inf(f16))));
|
||||
try expect(math.isNan(__floorh(math.nan(f16))));
|
||||
}
|
||||
|
||||
test "floor32.special" {
|
||||
try expect(floorf(0.0) == 0.0);
|
||||
try expect(floorf(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(floorf(math.inf(f32))));
|
||||
try expect(math.isNegativeInf(floorf(-math.inf(f32))));
|
||||
try expect(math.isNan(floorf(math.nan(f32))));
|
||||
}
|
||||
|
||||
test "floor64.special" {
|
||||
try expect(floor(0.0) == 0.0);
|
||||
try expect(floor(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(floor(math.inf(f64))));
|
||||
try expect(math.isNegativeInf(floor(-math.inf(f64))));
|
||||
try expect(math.isNan(floor(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "floor80.special" {
|
||||
try expect(__floorx(0.0) == 0.0);
|
||||
try expect(__floorx(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__floorx(math.inf(f80))));
|
||||
try expect(math.isNegativeInf(__floorx(-math.inf(f80))));
|
||||
try expect(math.isNan(__floorx(math.nan(f80))));
|
||||
}
|
||||
|
||||
test "floor128.special" {
|
||||
try expect(floorq(0.0) == 0.0);
|
||||
try expect(floorq(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(floorq(math.inf(f128))));
|
||||
try expect(math.isNegativeInf(floorq(-math.inf(f128))));
|
||||
try expect(math.isNan(floorq(math.nan(f128))));
|
||||
}
|
||||
286
lib/compiler_rt/floor_ceil.zig
Normal file
286
lib/compiler_rt/floor_ceil.zig
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
//! Ported from musl, which is MIT licensed.
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
|
||||
//!
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceill.c
|
||||
//!
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c
|
||||
//! https://git.musl-libc.org/cgit/musl/tree/src/math/floorl.c
|
||||
|
||||
const std = @import("std");
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const common = @import("common.zig");
|
||||
|
||||
pub const panic = common.panic;
|
||||
|
||||
comptime {
|
||||
// floor
|
||||
@export(&__floorh, .{ .name = "__floorh", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&floorf, .{ .name = "floorf", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&floor, .{ .name = "floor", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&__floorx, .{ .name = "__floorx", .linkage = common.linkage, .visibility = common.visibility });
|
||||
if (common.want_ppc_abi) {
|
||||
@export(&floorq, .{ .name = "floorf128", .linkage = common.linkage, .visibility = common.visibility });
|
||||
}
|
||||
@export(&floorq, .{ .name = "floorq", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&floorl, .{ .name = "floorl", .linkage = common.linkage, .visibility = common.visibility });
|
||||
|
||||
// ceil
|
||||
@export(&__ceilh, .{ .name = "__ceilh", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&ceilf, .{ .name = "ceilf", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&ceil, .{ .name = "ceil", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&__ceilx, .{ .name = "__ceilx", .linkage = common.linkage, .visibility = common.visibility });
|
||||
if (common.want_ppc_abi) {
|
||||
@export(&ceilq, .{ .name = "ceilf128", .linkage = common.linkage, .visibility = common.visibility });
|
||||
}
|
||||
@export(&ceilq, .{ .name = "ceilq", .linkage = common.linkage, .visibility = common.visibility });
|
||||
@export(&ceill, .{ .name = "ceill", .linkage = common.linkage, .visibility = common.visibility });
|
||||
}
|
||||
|
||||
pub fn __floorh(x: f16) callconv(.c) f16 {
|
||||
return impl(f16, .floor, x);
|
||||
}
|
||||
|
||||
pub fn floorf(x: f32) callconv(.c) f32 {
|
||||
return impl(f32, .floor, x);
|
||||
}
|
||||
|
||||
pub fn floor(x: f64) callconv(.c) f64 {
|
||||
return impl(f64, .floor, x);
|
||||
}
|
||||
|
||||
pub fn __floorx(x: f80) callconv(.c) f80 {
|
||||
return impl(f80, .floor, x);
|
||||
}
|
||||
|
||||
pub fn floorq(x: f128) callconv(.c) f128 {
|
||||
return impl(f128, .floor, x);
|
||||
}
|
||||
|
||||
pub fn floorl(x: c_longdouble) callconv(.c) c_longdouble {
|
||||
return impl(std.meta.Float(@bitSizeOf(c_longdouble)), .floor, x);
|
||||
}
|
||||
|
||||
pub fn __ceilh(x: f16) callconv(.c) f16 {
|
||||
return impl(f16, .ceil, x);
|
||||
}
|
||||
|
||||
pub fn ceilf(x: f32) callconv(.c) f32 {
|
||||
return impl(f32, .ceil, x);
|
||||
}
|
||||
|
||||
pub fn ceil(x: f64) callconv(.c) f64 {
|
||||
return impl(f64, .ceil, x);
|
||||
}
|
||||
|
||||
pub fn __ceilx(x: f80) callconv(.c) f80 {
|
||||
return impl(f80, .ceil, x);
|
||||
}
|
||||
|
||||
pub fn ceilq(x: f128) callconv(.c) f128 {
|
||||
return impl(f128, .ceil, x);
|
||||
}
|
||||
|
||||
pub fn ceill(x: c_longdouble) callconv(.c) c_longdouble {
|
||||
return impl(std.meta.Float(@bitSizeOf(c_longdouble)), .ceil, x);
|
||||
}
|
||||
|
||||
inline fn impl(comptime T: type, comptime op: enum { floor, ceil }, x: T) T {
|
||||
const C = 1.0 / math.floatEps(T);
|
||||
const mantissa = math.floatMantissaBits(T);
|
||||
const mask = (1 << math.floatExponentBits(T)) - 1;
|
||||
const bias = (1 << (math.floatExponentBits(T) - 1)) - 1;
|
||||
|
||||
const bits = @bitSizeOf(T);
|
||||
const U = @Int(.unsigned, bits);
|
||||
var u: U = @bitCast(x);
|
||||
switch (T) {
|
||||
f16, f32 => {
|
||||
const e = @as(@Int(.signed, bits), @intCast((u >> mantissa) & mask)) - bias;
|
||||
if (e >= mantissa) return x;
|
||||
|
||||
if (e >= 0) {
|
||||
const m = (@as(U, 1) << @intCast(mantissa - e)) - 1;
|
||||
if (u & m == 0) return x;
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
if (u >> bits - 1 == @intFromBool(op == .floor)) u += m;
|
||||
return @bitCast(u & ~m);
|
||||
} else {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
|
||||
return switch (op) {
|
||||
.floor => if (u >> bits - 1 == 0) 0.0 else if (u << 1 != 0) -1.0 else x,
|
||||
.ceil => if (u >> bits - 1 != 0) -0.0 else if (u << 1 != 0) 1.0 else x,
|
||||
};
|
||||
}
|
||||
},
|
||||
f64, f80, f128 => {
|
||||
const e = (u >> mantissa) & mask;
|
||||
if (e >= bias + math.floatFractionalBits(T) or x == 0) return x;
|
||||
|
||||
const positive = u >> @bitSizeOf(T) - 1 == 0;
|
||||
const y: T = if (positive)
|
||||
x + C - C - x
|
||||
else
|
||||
x - C + C - x;
|
||||
|
||||
if (e <= bias - 1) {
|
||||
if (common.want_float_exceptions) mem.doNotOptimizeAway(y);
|
||||
return switch (op) {
|
||||
.floor => if (positive) 0.0 else -1.0,
|
||||
.ceil => if (positive) 1.0 else -0.0,
|
||||
};
|
||||
}
|
||||
switch (op) {
|
||||
.floor => if (y > 0) return x + y - 1,
|
||||
.ceil => if (y < 0) return x + y + 1,
|
||||
}
|
||||
return x + y;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
test "floor16" {
|
||||
try expect(__floorh(1.3) == 1.0);
|
||||
try expect(__floorh(-1.3) == -2.0);
|
||||
try expect(__floorh(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor32" {
|
||||
try expect(floorf(1.3) == 1.0);
|
||||
try expect(floorf(-1.3) == -2.0);
|
||||
try expect(floorf(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor64" {
|
||||
try expect(floor(1.3) == 1.0);
|
||||
try expect(floor(-1.3) == -2.0);
|
||||
try expect(floor(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor80" {
|
||||
try expect(__floorx(1.3) == 1.0);
|
||||
try expect(__floorx(-1.3) == -2.0);
|
||||
try expect(__floorx(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor128" {
|
||||
try expect(floorq(1.3) == 1.0);
|
||||
try expect(floorq(-1.3) == -2.0);
|
||||
try expect(floorq(0.2) == 0.0);
|
||||
}
|
||||
|
||||
test "floor16.special" {
|
||||
try expect(__floorh(0.0) == 0.0);
|
||||
try expect(__floorh(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__floorh(math.inf(f16))));
|
||||
try expect(math.isNegativeInf(__floorh(-math.inf(f16))));
|
||||
try expect(math.isNan(__floorh(math.nan(f16))));
|
||||
}
|
||||
|
||||
test "floor32.special" {
|
||||
try expect(floorf(0.0) == 0.0);
|
||||
try expect(floorf(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(floorf(math.inf(f32))));
|
||||
try expect(math.isNegativeInf(floorf(-math.inf(f32))));
|
||||
try expect(math.isNan(floorf(math.nan(f32))));
|
||||
}
|
||||
|
||||
test "floor64.special" {
|
||||
try expect(floor(0.0) == 0.0);
|
||||
try expect(floor(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(floor(math.inf(f64))));
|
||||
try expect(math.isNegativeInf(floor(-math.inf(f64))));
|
||||
try expect(math.isNan(floor(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "floor80.special" {
|
||||
try expect(__floorx(0.0) == 0.0);
|
||||
try expect(__floorx(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__floorx(math.inf(f80))));
|
||||
try expect(math.isNegativeInf(__floorx(-math.inf(f80))));
|
||||
try expect(math.isNan(__floorx(math.nan(f80))));
|
||||
}
|
||||
|
||||
test "floor128.special" {
|
||||
try expect(floorq(0.0) == 0.0);
|
||||
try expect(floorq(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(floorq(math.inf(f128))));
|
||||
try expect(math.isNegativeInf(floorq(-math.inf(f128))));
|
||||
try expect(math.isNan(floorq(math.nan(f128))));
|
||||
}
|
||||
|
||||
test "ceil16" {
|
||||
try expect(__ceilh(1.3) == 2.0);
|
||||
try expect(__ceilh(-1.3) == -1.0);
|
||||
try expect(__ceilh(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil32" {
|
||||
try expect(ceilf(1.3) == 2.0);
|
||||
try expect(ceilf(-1.3) == -1.0);
|
||||
try expect(ceilf(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil64" {
|
||||
try expect(ceil(1.3) == 2.0);
|
||||
try expect(ceil(-1.3) == -1.0);
|
||||
try expect(ceil(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil80" {
|
||||
try expect(__ceilx(1.3) == 2.0);
|
||||
try expect(__ceilx(-1.3) == -1.0);
|
||||
try expect(__ceilx(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil128" {
|
||||
try expect(ceilq(1.3) == 2.0);
|
||||
try expect(ceilq(-1.3) == -1.0);
|
||||
try expect(ceilq(0.2) == 1.0);
|
||||
}
|
||||
|
||||
test "ceil16.special" {
|
||||
try expect(__ceilh(0.0) == 0.0);
|
||||
try expect(__ceilh(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__ceilh(math.inf(f16))));
|
||||
try expect(math.isNegativeInf(__ceilh(-math.inf(f16))));
|
||||
try expect(math.isNan(__ceilh(math.nan(f16))));
|
||||
}
|
||||
|
||||
test "ceil32.special" {
|
||||
try expect(ceilf(0.0) == 0.0);
|
||||
try expect(ceilf(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(ceilf(math.inf(f32))));
|
||||
try expect(math.isNegativeInf(ceilf(-math.inf(f32))));
|
||||
try expect(math.isNan(ceilf(math.nan(f32))));
|
||||
}
|
||||
|
||||
test "ceil64.special" {
|
||||
try expect(ceil(0.0) == 0.0);
|
||||
try expect(ceil(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(ceil(math.inf(f64))));
|
||||
try expect(math.isNegativeInf(ceil(-math.inf(f64))));
|
||||
try expect(math.isNan(ceil(math.nan(f64))));
|
||||
}
|
||||
|
||||
test "ceil80.special" {
|
||||
try expect(__ceilx(0.0) == 0.0);
|
||||
try expect(__ceilx(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(__ceilx(math.inf(f80))));
|
||||
try expect(math.isNegativeInf(__ceilx(-math.inf(f80))));
|
||||
try expect(math.isNan(__ceilx(math.nan(f80))));
|
||||
}
|
||||
|
||||
test "ceil128.special" {
|
||||
try expect(ceilq(0.0) == 0.0);
|
||||
try expect(ceilq(-0.0) == -0.0);
|
||||
try expect(math.isPositiveInf(ceilq(math.inf(f128))));
|
||||
try expect(math.isNegativeInf(ceilq(-math.inf(f128))));
|
||||
try expect(math.isNan(ceilq(math.nan(f128))));
|
||||
}
|
||||
|
|
@ -1298,6 +1298,24 @@ test "@ceil f80/f128/c_longdouble" {
|
|||
try comptime testCeil(c_longdouble);
|
||||
}
|
||||
|
||||
test "@ceil f80 maxInt(u64)" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
|
||||
// https://github.com/ziglang/zig/issues/12602
|
||||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
var x: u64 = std.math.maxInt(u64);
|
||||
x = x;
|
||||
const float: f80 = @floatFromInt(x);
|
||||
try std.testing.expect(float == @ceil(float));
|
||||
}
|
||||
|
||||
fn testCeil(comptime T: type) !void {
|
||||
var two_point_one: T = 2.1;
|
||||
try expect(@ceil(two_point_one) == 3.0);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue