| author | |
| committer | |
| log | 938efe4aab09b5806cd3e9903619b95a5f122f36 |
| tree | cbd969a877bc2590ca212d4b5ba17eb3c711e636 |
| parent | 52e0f7870695f11eea7700df9a5c07025dc7d5a2 |
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.6 files changed, 304 insertions(+), 476 deletions(-)
CMakeLists.txt+1-2| ... | ... | @@ -221,7 +221,6 @@ set(ZIG_STAGE2_SOURCES |
| 221 | 221 | lib/compiler_rt/aulldiv.zig |
| 222 | 222 | lib/compiler_rt/aullrem.zig |
| 223 | 223 | lib/compiler_rt/bswap.zig |
| 224 | lib/compiler_rt/ceil.zig | |
| 225 | 224 | lib/compiler_rt/clear_cache.zig |
| 226 | 225 | lib/compiler_rt/cmp.zig |
| 227 | 226 | lib/compiler_rt/cmpdf2.zig |
| ... | ... | @@ -312,7 +311,7 @@ set(ZIG_STAGE2_SOURCES |
| 312 | 311 | lib/compiler_rt/floatuntisf.zig |
| 313 | 312 | lib/compiler_rt/floatuntitf.zig |
| 314 | 313 | lib/compiler_rt/floatuntixf.zig |
| 315 | lib/compiler_rt/floor.zig | |
| 314 | lib/compiler_rt/floor_ceil.zig | |
| 316 | 315 | lib/compiler_rt/fma.zig |
| 317 | 316 | lib/compiler_rt/fmax.zig |
| 318 | 317 | lib/compiler_rt/fmin.zig |
lib/compiler_rt.zig+1-2| ... | ... | @@ -231,12 +231,11 @@ comptime { |
| 231 | 231 | _ = @import("compiler_rt/divtc3.zig"); |
| 232 | 232 | |
| 233 | 233 | // Math routines. Alphabetically sorted. |
| 234 | _ = @import("compiler_rt/ceil.zig"); | |
| 235 | 234 | _ = @import("compiler_rt/cos.zig"); |
| 236 | 235 | _ = @import("compiler_rt/exp.zig"); |
| 237 | 236 | _ = @import("compiler_rt/exp2.zig"); |
| 238 | 237 | _ = @import("compiler_rt/fabs.zig"); |
| 239 | _ = @import("compiler_rt/floor.zig"); | |
| 238 | _ = @import("compiler_rt/floor_ceil.zig"); | |
| 240 | 239 | _ = @import("compiler_rt/fma.zig"); |
| 241 | 240 | _ = @import("compiler_rt/fmax.zig"); |
| 242 | 241 | _ = @import("compiler_rt/fmin.zig"); |
lib/compiler_rt/ceil.zig deleted-236| ... | ... | @@ -1,236 +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/ceilf.c | |
| 5 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c | |
| 6 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/ceill.c | |
| 7 | ||
| 8 | const std = @import("std"); | |
| 9 | const builtin = @import("builtin"); | |
| 10 | const arch = builtin.cpu.arch; | |
| 11 | const math = std.math; | |
| 12 | const mem = std.mem; | |
| 13 | const expect = std.testing.expect; | |
| 14 | const common = @import("common.zig"); | |
| 15 | ||
| 16 | comptime { | |
| 17 | @export(&__ceilh, .{ .name = "__ceilh", .linkage = common.linkage, .visibility = common.visibility }); | |
| 18 | @export(&ceilf, .{ .name = "ceilf", .linkage = common.linkage, .visibility = common.visibility }); | |
| 19 | @export(&ceil, .{ .name = "ceil", .linkage = common.linkage, .visibility = common.visibility }); | |
| 20 | @export(&__ceilx, .{ .name = "__ceilx", .linkage = common.linkage, .visibility = common.visibility }); | |
| 21 | if (common.want_ppc_abi) { | |
| 22 | @export(&ceilq, .{ .name = "ceilf128", .linkage = common.linkage, .visibility = common.visibility }); | |
| 23 | } | |
| 24 | @export(&ceilq, .{ .name = "ceilq", .linkage = common.linkage, .visibility = common.visibility }); | |
| 25 | @export(&ceill, .{ .name = "ceill", .linkage = common.linkage, .visibility = common.visibility }); | |
| 26 | } | |
| 27 | ||
| 28 | pub fn __ceilh(x: f16) callconv(.c) f16 { | |
| 29 | var u: u16 = @bitCast(x); | |
| 30 | const e = @as(i16, @intCast((u >> 10) & 31)) - 15; | |
| 31 | var m: u16 = undefined; | |
| 32 | ||
| 33 | if (e >= 10) return x; | |
| 34 | ||
| 35 | if (e >= 0) { | |
| 36 | m = @as(u16, 0x03FF) >> @intCast(e); | |
| 37 | if (u & m == 0) return x; | |
| 38 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 39 | if (u >> 15 == 0) u += m; | |
| 40 | u &= ~m; | |
| 41 | return @bitCast(u); | |
| 42 | } else { | |
| 43 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 44 | return if (u >> 15 != 0) -0.0 else if (u << 1 != 0) 1.0 else x; | |
| 45 | } | |
| 46 | } | |
| 47 | ||
| 48 | pub fn ceilf(x: f32) callconv(.c) f32 { | |
| 49 | var u: u32 = @bitCast(x); | |
| 50 | const e = @as(i32, @intCast((u >> 23) & 0xFF)) - 0x7F; | |
| 51 | var m: u32 = undefined; | |
| 52 | ||
| 53 | if (e >= 23) return x; | |
| 54 | ||
| 55 | if (e >= 0) { | |
| 56 | m = @as(u32, 0x007FFFFF) >> @intCast(e); | |
| 57 | if (u & m == 0) return x; | |
| 58 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 59 | if (u >> 31 == 0) u += m; | |
| 60 | u &= ~m; | |
| 61 | return @bitCast(u); | |
| 62 | } else { | |
| 63 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 64 | return if (u >> 31 != 0) -0.0 else if (u << 1 != 0) 1.0 else x; | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | pub fn ceil(x: f64) callconv(.c) f64 { | |
| 69 | const f64_toint = 1.0 / math.floatEps(f64); | |
| 70 | ||
| 71 | const u: u64 = @bitCast(x); | |
| 72 | const e = (u >> 52) & 0x7FF; | |
| 73 | var y: f64 = undefined; | |
| 74 | ||
| 75 | if (e >= 0x3FF + 52 or x == 0) { | |
| 76 | return x; | |
| 77 | } | |
| 78 | ||
| 79 | if (u >> 63 != 0) { | |
| 80 | y = x - f64_toint + f64_toint - x; | |
| 81 | } else { | |
| 82 | y = x + f64_toint - f64_toint - x; | |
| 83 | } | |
| 84 | ||
| 85 | if (e <= 0x3FF - 1) { | |
| 86 | if (common.want_float_exceptions) mem.doNotOptimizeAway(y); | |
| 87 | if (u >> 63 != 0) { | |
| 88 | return -0.0; | |
| 89 | } else { | |
| 90 | return 1.0; | |
| 91 | } | |
| 92 | } else if (y < 0) { | |
| 93 | return x + y + 1; | |
| 94 | } else { | |
| 95 | return x + y; | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | pub fn __ceilx(x: f80) callconv(.c) f80 { | |
| 100 | const f80_toint = 1.0 / math.floatEps(f80); | |
| 101 | ||
| 102 | const u: u80 = @bitCast(x); | |
| 103 | const e = (u >> 64) & 0x7FFF; | |
| 104 | var y: f80 = undefined; | |
| 105 | ||
| 106 | if (e >= 0x3FFF + 64 or x == 0) return x; | |
| 107 | ||
| 108 | if (u >> 79 != 0) { | |
| 109 | y = x - f80_toint + f80_toint - x; | |
| 110 | } else { | |
| 111 | y = x + f80_toint - f80_toint - x; | |
| 112 | } | |
| 113 | ||
| 114 | if (e <= 0x3FFF - 1) { | |
| 115 | if (common.want_float_exceptions) mem.doNotOptimizeAway(y); | |
| 116 | if (u >> 79 != 0) { | |
| 117 | return -0.0; | |
| 118 | } else { | |
| 119 | return 1.0; | |
| 120 | } | |
| 121 | } else if (y < 0) { | |
| 122 | return x + y + 1; | |
| 123 | } else { | |
| 124 | return x + y; | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | pub fn ceilq(x: f128) callconv(.c) f128 { | |
| 129 | const f128_toint = 1.0 / math.floatEps(f128); | |
| 130 | ||
| 131 | const u: u128 = @bitCast(x); | |
| 132 | const e = (u >> 112) & 0x7FFF; | |
| 133 | var y: f128 = undefined; | |
| 134 | ||
| 135 | if (e >= 0x3FFF + 112 or x == 0) return x; | |
| 136 | ||
| 137 | if (u >> 127 != 0) { | |
| 138 | y = x - f128_toint + f128_toint - x; | |
| 139 | } else { | |
| 140 | y = x + f128_toint - f128_toint - x; | |
| 141 | } | |
| 142 | ||
| 143 | if (e <= 0x3FFF - 1) { | |
| 144 | if (common.want_float_exceptions) mem.doNotOptimizeAway(y); | |
| 145 | if (u >> 127 != 0) { | |
| 146 | return -0.0; | |
| 147 | } else { | |
| 148 | return 1.0; | |
| 149 | } | |
| 150 | } else if (y < 0) { | |
| 151 | return x + y + 1; | |
| 152 | } else { | |
| 153 | return x + y; | |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 157 | pub fn ceill(x: c_longdouble) callconv(.c) c_longdouble { | |
| 158 | switch (@typeInfo(c_longdouble).float.bits) { | |
| 159 | 16 => return __ceilh(x), | |
| 160 | 32 => return ceilf(x), | |
| 161 | 64 => return ceil(x), | |
| 162 | 80 => return __ceilx(x), | |
| 163 | 128 => return ceilq(x), | |
| 164 | else => @compileError("unreachable"), | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | test "ceil16" { | |
| 169 | try expect(__ceilh(1.3) == 2.0); | |
| 170 | try expect(__ceilh(-1.3) == -1.0); | |
| 171 | try expect(__ceilh(0.2) == 1.0); | |
| 172 | } | |
| 173 | ||
| 174 | test "ceil32" { | |
| 175 | try expect(ceilf(1.3) == 2.0); | |
| 176 | try expect(ceilf(-1.3) == -1.0); | |
| 177 | try expect(ceilf(0.2) == 1.0); | |
| 178 | } | |
| 179 | ||
| 180 | test "ceil64" { | |
| 181 | try expect(ceil(1.3) == 2.0); | |
| 182 | try expect(ceil(-1.3) == -1.0); | |
| 183 | try expect(ceil(0.2) == 1.0); | |
| 184 | } | |
| 185 | ||
| 186 | test "ceil80" { | |
| 187 | try expect(__ceilx(1.3) == 2.0); | |
| 188 | try expect(__ceilx(-1.3) == -1.0); | |
| 189 | try expect(__ceilx(0.2) == 1.0); | |
| 190 | } | |
| 191 | ||
| 192 | test "ceil128" { | |
| 193 | try expect(ceilq(1.3) == 2.0); | |
| 194 | try expect(ceilq(-1.3) == -1.0); | |
| 195 | try expect(ceilq(0.2) == 1.0); | |
| 196 | } | |
| 197 | ||
| 198 | test "ceil16.special" { | |
| 199 | try expect(__ceilh(0.0) == 0.0); | |
| 200 | try expect(__ceilh(-0.0) == -0.0); | |
| 201 | try expect(math.isPositiveInf(__ceilh(math.inf(f16)))); | |
| 202 | try expect(math.isNegativeInf(__ceilh(-math.inf(f16)))); | |
| 203 | try expect(math.isNan(__ceilh(math.nan(f16)))); | |
| 204 | } | |
| 205 | ||
| 206 | test "ceil32.special" { | |
| 207 | try expect(ceilf(0.0) == 0.0); | |
| 208 | try expect(ceilf(-0.0) == -0.0); | |
| 209 | try expect(math.isPositiveInf(ceilf(math.inf(f32)))); | |
| 210 | try expect(math.isNegativeInf(ceilf(-math.inf(f32)))); | |
| 211 | try expect(math.isNan(ceilf(math.nan(f32)))); | |
| 212 | } | |
| 213 | ||
| 214 | test "ceil64.special" { | |
| 215 | try expect(ceil(0.0) == 0.0); | |
| 216 | try expect(ceil(-0.0) == -0.0); | |
| 217 | try expect(math.isPositiveInf(ceil(math.inf(f64)))); | |
| 218 | try expect(math.isNegativeInf(ceil(-math.inf(f64)))); | |
| 219 | try expect(math.isNan(ceil(math.nan(f64)))); | |
| 220 | } | |
| 221 | ||
| 222 | test "ceil80.special" { | |
| 223 | try expect(__ceilx(0.0) == 0.0); | |
| 224 | try expect(__ceilx(-0.0) == -0.0); | |
| 225 | try expect(math.isPositiveInf(__ceilx(math.inf(f80)))); | |
| 226 | try expect(math.isNegativeInf(__ceilx(-math.inf(f80)))); | |
| 227 | try expect(math.isNan(__ceilx(math.nan(f80)))); | |
| 228 | } | |
| 229 | ||
| 230 | test "ceil128.special" { | |
| 231 | try expect(ceilq(0.0) == 0.0); | |
| 232 | try expect(ceilq(-0.0) == -0.0); | |
| 233 | try expect(math.isPositiveInf(ceilq(math.inf(f128)))); | |
| 234 | try expect(math.isNegativeInf(ceilq(-math.inf(f128)))); | |
| 235 | try expect(math.isNan(ceilq(math.nan(f128)))); | |
| 236 | } |
lib/compiler_rt/floor.zig deleted-236| ... | ... | @@ -1,236 +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 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/floorl.c | |
| 7 | ||
| 8 | const std = @import("std"); | |
| 9 | const builtin = @import("builtin"); | |
| 10 | const math = std.math; | |
| 11 | const mem = std.mem; | |
| 12 | const expect = std.testing.expect; | |
| 13 | const arch = builtin.cpu.arch; | |
| 14 | const common = @import("common.zig"); | |
| 15 | ||
| 16 | comptime { | |
| 17 | @export(&__floorh, .{ .name = "__floorh", .linkage = common.linkage, .visibility = common.visibility }); | |
| 18 | @export(&floorf, .{ .name = "floorf", .linkage = common.linkage, .visibility = common.visibility }); | |
| 19 | @export(&floor, .{ .name = "floor", .linkage = common.linkage, .visibility = common.visibility }); | |
| 20 | @export(&__floorx, .{ .name = "__floorx", .linkage = common.linkage, .visibility = common.visibility }); | |
| 21 | if (common.want_ppc_abi) { | |
| 22 | @export(&floorq, .{ .name = "floorf128", .linkage = common.linkage, .visibility = common.visibility }); | |
| 23 | } | |
| 24 | @export(&floorq, .{ .name = "floorq", .linkage = common.linkage, .visibility = common.visibility }); | |
| 25 | @export(&floorl, .{ .name = "floorl", .linkage = common.linkage, .visibility = common.visibility }); | |
| 26 | } | |
| 27 | ||
| 28 | pub fn __floorh(x: f16) callconv(.c) f16 { | |
| 29 | var u: u16 = @bitCast(x); | |
| 30 | const e = @as(i16, @intCast((u >> 10) & 31)) - 15; | |
| 31 | var m: u16 = undefined; | |
| 32 | ||
| 33 | if (e >= 10) return x; | |
| 34 | ||
| 35 | if (e >= 0) { | |
| 36 | m = @as(u16, 0x03FF) >> @intCast(e); | |
| 37 | if (u & m == 0) return x; | |
| 38 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 39 | if (u >> 15 != 0) u += m; | |
| 40 | return @bitCast(u & ~m); | |
| 41 | } else { | |
| 42 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 43 | return if (u >> 15 == 0) 0.0 else if (u << 1 != 0) -1.0 else x; | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | pub fn floorf(x: f32) callconv(.c) f32 { | |
| 48 | var u: u32 = @bitCast(x); | |
| 49 | const e = @as(i32, @intCast((u >> 23) & 0xFF)) - 0x7F; | |
| 50 | var m: u32 = undefined; | |
| 51 | ||
| 52 | if (e >= 23) return x; | |
| 53 | ||
| 54 | if (e >= 0) { | |
| 55 | m = @as(u32, 0x007FFFFF) >> @intCast(e); | |
| 56 | if (u & m == 0) return x; | |
| 57 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 58 | if (u >> 31 != 0) u += m; | |
| 59 | return @bitCast(u & ~m); | |
| 60 | } else { | |
| 61 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 62 | return if (u >> 31 == 0) 0.0 else if (u << 1 != 0) -1.0 else x; | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 66 | pub fn floor(x: f64) callconv(.c) f64 { | |
| 67 | const f64_toint = 1.0 / math.floatEps(f64); | |
| 68 | ||
| 69 | const u: u64 = @bitCast(x); | |
| 70 | const e = (u >> 52) & 0x7FF; | |
| 71 | var y: f64 = undefined; | |
| 72 | ||
| 73 | if (e >= 0x3FF + 52 or x == 0) { | |
| 74 | return x; | |
| 75 | } | |
| 76 | ||
| 77 | if (u >> 63 != 0) { | |
| 78 | y = x - f64_toint + f64_toint - x; | |
| 79 | } else { | |
| 80 | y = x + f64_toint - f64_toint - x; | |
| 81 | } | |
| 82 | ||
| 83 | if (e <= 0x3FF - 1) { | |
| 84 | if (common.want_float_exceptions) mem.doNotOptimizeAway(y); | |
| 85 | if (u >> 63 != 0) { | |
| 86 | return -1.0; | |
| 87 | } else { | |
| 88 | return 0.0; | |
| 89 | } | |
| 90 | } else if (y > 0) { | |
| 91 | return x + y - 1; | |
| 92 | } else { | |
| 93 | return x + y; | |
| 94 | } | |
| 95 | } | |
| 96 | ||
| 97 | pub fn __floorx(x: f80) callconv(.c) f80 { | |
| 98 | const f80_toint = 1.0 / math.floatEps(f80); | |
| 99 | ||
| 100 | const u: u80 = @bitCast(x); | |
| 101 | const e = (u >> 64) & 0x7FFF; | |
| 102 | var y: f80 = undefined; | |
| 103 | ||
| 104 | if (e >= 0x3FFF + 64 or x == 0) { | |
| 105 | return x; | |
| 106 | } | |
| 107 | ||
| 108 | if (u >> 79 != 0) { | |
| 109 | y = x - f80_toint + f80_toint - x; | |
| 110 | } else { | |
| 111 | y = x + f80_toint - f80_toint - x; | |
| 112 | } | |
| 113 | ||
| 114 | if (e <= 0x3FFF - 1) { | |
| 115 | if (common.want_float_exceptions) mem.doNotOptimizeAway(y); | |
| 116 | if (u >> 79 != 0) { | |
| 117 | return -1.0; | |
| 118 | } else { | |
| 119 | return 0.0; | |
| 120 | } | |
| 121 | } else if (y > 0) { | |
| 122 | return x + y - 1; | |
| 123 | } else { | |
| 124 | return x + y; | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | pub fn floorq(x: f128) callconv(.c) f128 { | |
| 129 | const f128_toint = 1.0 / math.floatEps(f128); | |
| 130 | ||
| 131 | const u: u128 = @bitCast(x); | |
| 132 | const e = (u >> 112) & 0x7FFF; | |
| 133 | var y: f128 = undefined; | |
| 134 | ||
| 135 | if (e >= 0x3FFF + 112 or x == 0) return x; | |
| 136 | ||
| 137 | if (u >> 127 != 0) { | |
| 138 | y = x - f128_toint + f128_toint - x; | |
| 139 | } else { | |
| 140 | y = x + f128_toint - f128_toint - x; | |
| 141 | } | |
| 142 | ||
| 143 | if (e <= 0x3FFF - 1) { | |
| 144 | if (common.want_float_exceptions) mem.doNotOptimizeAway(y); | |
| 145 | if (u >> 127 != 0) { | |
| 146 | return -1.0; | |
| 147 | } else { | |
| 148 | return 0.0; | |
| 149 | } | |
| 150 | } else if (y > 0) { | |
| 151 | return x + y - 1; | |
| 152 | } else { | |
| 153 | return x + y; | |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 157 | pub fn floorl(x: c_longdouble) callconv(.c) c_longdouble { | |
| 158 | switch (@typeInfo(c_longdouble).float.bits) { | |
| 159 | 16 => return __floorh(x), | |
| 160 | 32 => return floorf(x), | |
| 161 | 64 => return floor(x), | |
| 162 | 80 => return __floorx(x), | |
| 163 | 128 => return floorq(x), | |
| 164 | else => @compileError("unreachable"), | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | test "floor16" { | |
| 169 | try expect(__floorh(1.3) == 1.0); | |
| 170 | try expect(__floorh(-1.3) == -2.0); | |
| 171 | try expect(__floorh(0.2) == 0.0); | |
| 172 | } | |
| 173 | ||
| 174 | test "floor32" { | |
| 175 | try expect(floorf(1.3) == 1.0); | |
| 176 | try expect(floorf(-1.3) == -2.0); | |
| 177 | try expect(floorf(0.2) == 0.0); | |
| 178 | } | |
| 179 | ||
| 180 | test "floor64" { | |
| 181 | try expect(floor(1.3) == 1.0); | |
| 182 | try expect(floor(-1.3) == -2.0); | |
| 183 | try expect(floor(0.2) == 0.0); | |
| 184 | } | |
| 185 | ||
| 186 | test "floor80" { | |
| 187 | try expect(__floorx(1.3) == 1.0); | |
| 188 | try expect(__floorx(-1.3) == -2.0); | |
| 189 | try expect(__floorx(0.2) == 0.0); | |
| 190 | } | |
| 191 | ||
| 192 | test "floor128" { | |
| 193 | try expect(floorq(1.3) == 1.0); | |
| 194 | try expect(floorq(-1.3) == -2.0); | |
| 195 | try expect(floorq(0.2) == 0.0); | |
| 196 | } | |
| 197 | ||
| 198 | test "floor16.special" { | |
| 199 | try expect(__floorh(0.0) == 0.0); | |
| 200 | try expect(__floorh(-0.0) == -0.0); | |
| 201 | try expect(math.isPositiveInf(__floorh(math.inf(f16)))); | |
| 202 | try expect(math.isNegativeInf(__floorh(-math.inf(f16)))); | |
| 203 | try expect(math.isNan(__floorh(math.nan(f16)))); | |
| 204 | } | |
| 205 | ||
| 206 | test "floor32.special" { | |
| 207 | try expect(floorf(0.0) == 0.0); | |
| 208 | try expect(floorf(-0.0) == -0.0); | |
| 209 | try expect(math.isPositiveInf(floorf(math.inf(f32)))); | |
| 210 | try expect(math.isNegativeInf(floorf(-math.inf(f32)))); | |
| 211 | try expect(math.isNan(floorf(math.nan(f32)))); | |
| 212 | } | |
| 213 | ||
| 214 | test "floor64.special" { | |
| 215 | try expect(floor(0.0) == 0.0); | |
| 216 | try expect(floor(-0.0) == -0.0); | |
| 217 | try expect(math.isPositiveInf(floor(math.inf(f64)))); | |
| 218 | try expect(math.isNegativeInf(floor(-math.inf(f64)))); | |
| 219 | try expect(math.isNan(floor(math.nan(f64)))); | |
| 220 | } | |
| 221 | ||
| 222 | test "floor80.special" { | |
| 223 | try expect(__floorx(0.0) == 0.0); | |
| 224 | try expect(__floorx(-0.0) == -0.0); | |
| 225 | try expect(math.isPositiveInf(__floorx(math.inf(f80)))); | |
| 226 | try expect(math.isNegativeInf(__floorx(-math.inf(f80)))); | |
| 227 | try expect(math.isNan(__floorx(math.nan(f80)))); | |
| 228 | } | |
| 229 | ||
| 230 | test "floor128.special" { | |
| 231 | try expect(floorq(0.0) == 0.0); | |
| 232 | try expect(floorq(-0.0) == -0.0); | |
| 233 | try expect(math.isPositiveInf(floorq(math.inf(f128)))); | |
| 234 | try expect(math.isNegativeInf(floorq(-math.inf(f128)))); | |
| 235 | try expect(math.isNan(floorq(math.nan(f128)))); | |
| 236 | } |
lib/compiler_rt/floor_ceil.zig created+284| ... | ... | @@ -0,0 +1,284 @@ |
| 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/ceilf.c | |
| 5 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c | |
| 6 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/ceill.c | |
| 7 | //! | |
| 8 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c | |
| 9 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c | |
| 10 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/floorl.c | |
| 11 | ||
| 12 | const std = @import("std"); | |
| 13 | const math = std.math; | |
| 14 | const mem = std.mem; | |
| 15 | const expect = std.testing.expect; | |
| 16 | ||
| 17 | const common = @import("common.zig"); | |
| 18 | ||
| 19 | comptime { | |
| 20 | // floor | |
| 21 | @export(&__floorh, .{ .name = "__floorh", .linkage = common.linkage, .visibility = common.visibility }); | |
| 22 | @export(&floorf, .{ .name = "floorf", .linkage = common.linkage, .visibility = common.visibility }); | |
| 23 | @export(&floor, .{ .name = "floor", .linkage = common.linkage, .visibility = common.visibility }); | |
| 24 | @export(&__floorx, .{ .name = "__floorx", .linkage = common.linkage, .visibility = common.visibility }); | |
| 25 | if (common.want_ppc_abi) { | |
| 26 | @export(&floorq, .{ .name = "floorf128", .linkage = common.linkage, .visibility = common.visibility }); | |
| 27 | } | |
| 28 | @export(&floorq, .{ .name = "floorq", .linkage = common.linkage, .visibility = common.visibility }); | |
| 29 | @export(&floorl, .{ .name = "floorl", .linkage = common.linkage, .visibility = common.visibility }); | |
| 30 | ||
| 31 | // ceil | |
| 32 | @export(&__ceilh, .{ .name = "__ceilh", .linkage = common.linkage, .visibility = common.visibility }); | |
| 33 | @export(&ceilf, .{ .name = "ceilf", .linkage = common.linkage, .visibility = common.visibility }); | |
| 34 | @export(&ceil, .{ .name = "ceil", .linkage = common.linkage, .visibility = common.visibility }); | |
| 35 | @export(&__ceilx, .{ .name = "__ceilx", .linkage = common.linkage, .visibility = common.visibility }); | |
| 36 | if (common.want_ppc_abi) { | |
| 37 | @export(&ceilq, .{ .name = "ceilf128", .linkage = common.linkage, .visibility = common.visibility }); | |
| 38 | } | |
| 39 | @export(&ceilq, .{ .name = "ceilq", .linkage = common.linkage, .visibility = common.visibility }); | |
| 40 | @export(&ceill, .{ .name = "ceill", .linkage = common.linkage, .visibility = common.visibility }); | |
| 41 | } | |
| 42 | ||
| 43 | pub fn __floorh(x: f16) callconv(.c) f16 { | |
| 44 | return impl(f16, .floor, x); | |
| 45 | } | |
| 46 | ||
| 47 | pub fn floorf(x: f32) callconv(.c) f32 { | |
| 48 | return impl(f32, .floor, x); | |
| 49 | } | |
| 50 | ||
| 51 | pub fn floor(x: f64) callconv(.c) f64 { | |
| 52 | return impl(f64, .floor, x); | |
| 53 | } | |
| 54 | ||
| 55 | pub fn __floorx(x: f80) callconv(.c) f80 { | |
| 56 | return impl(f80, .floor, x); | |
| 57 | } | |
| 58 | ||
| 59 | pub fn floorq(x: f128) callconv(.c) f128 { | |
| 60 | return impl(f128, .floor, x); | |
| 61 | } | |
| 62 | ||
| 63 | pub fn floorl(x: c_longdouble) callconv(.c) c_longdouble { | |
| 64 | return impl(std.meta.Float(@bitSizeOf(c_longdouble)), .floor, x); | |
| 65 | } | |
| 66 | ||
| 67 | pub fn __ceilh(x: f16) callconv(.c) f16 { | |
| 68 | return impl(f16, .ceil, x); | |
| 69 | } | |
| 70 | ||
| 71 | pub fn ceilf(x: f32) callconv(.c) f32 { | |
| 72 | return impl(f32, .ceil, x); | |
| 73 | } | |
| 74 | ||
| 75 | pub fn ceil(x: f64) callconv(.c) f64 { | |
| 76 | return impl(f64, .ceil, x); | |
| 77 | } | |
| 78 | ||
| 79 | pub fn __ceilx(x: f80) callconv(.c) f80 { | |
| 80 | return impl(f80, .ceil, x); | |
| 81 | } | |
| 82 | ||
| 83 | pub fn ceilq(x: f128) callconv(.c) f128 { | |
| 84 | return impl(f128, .ceil, x); | |
| 85 | } | |
| 86 | ||
| 87 | pub fn ceill(x: c_longdouble) callconv(.c) c_longdouble { | |
| 88 | return impl(std.meta.Float(@bitSizeOf(c_longdouble)), .ceil, x); | |
| 89 | } | |
| 90 | ||
| 91 | inline fn impl(comptime T: type, comptime op: enum { floor, ceil }, x: T) T { | |
| 92 | const C = 1.0 / math.floatEps(T); | |
| 93 | const mantissa = math.floatMantissaBits(T); | |
| 94 | const mask = (1 << math.floatExponentBits(T)) - 1; | |
| 95 | const bias = (1 << (math.floatExponentBits(T) - 1)) - 1; | |
| 96 | ||
| 97 | const bits = @bitSizeOf(T); | |
| 98 | const U = @Int(.unsigned, bits); | |
| 99 | var u: U = @bitCast(x); | |
| 100 | switch (T) { | |
| 101 | f16, f32 => { | |
| 102 | const e = @as(@Int(.signed, bits), @intCast((u >> mantissa) & mask)) - bias; | |
| 103 | if (e >= mantissa) return x; | |
| 104 | ||
| 105 | if (e >= 0) { | |
| 106 | const m = (@as(U, 1) << @intCast(mantissa - e)) - 1; | |
| 107 | if (u & m == 0) return x; | |
| 108 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 109 | if (u >> bits - 1 == @intFromBool(op == .floor)) u += m; | |
| 110 | return @bitCast(u & ~m); | |
| 111 | } else { | |
| 112 | if (common.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120); | |
| 113 | return switch (op) { | |
| 114 | .floor => if (u >> bits - 1 == 0) 0.0 else if (u << 1 != 0) -1.0 else x, | |
| 115 | .ceil => if (u >> bits - 1 != 0) -0.0 else if (u << 1 != 0) 1.0 else x, | |
| 116 | }; | |
| 117 | } | |
| 118 | }, | |
| 119 | f64, f80, f128 => { | |
| 120 | const e = (u >> mantissa) & mask; | |
| 121 | if (e >= bias + math.floatFractionalBits(T) or x == 0) return x; | |
| 122 | ||
| 123 | const positive = u >> @bitSizeOf(T) - 1 == 0; | |
| 124 | const y: T = if (positive) | |
| 125 | x + C - C - x | |
| 126 | else | |
| 127 | x - C + C - x; | |
| 128 | ||
| 129 | if (e <= bias - 1) { | |
| 130 | if (common.want_float_exceptions) mem.doNotOptimizeAway(y); | |
| 131 | return switch (op) { | |
| 132 | .floor => if (positive) 0.0 else -1.0, | |
| 133 | .ceil => if (positive) 1.0 else -0.0, | |
| 134 | }; | |
| 135 | } | |
| 136 | switch (op) { | |
| 137 | .floor => if (y > 0) return x + y - 1, | |
| 138 | .ceil => if (y < 0) return x + y + 1, | |
| 139 | } | |
| 140 | return x + y; | |
| 141 | }, | |
| 142 | else => unreachable, | |
| 143 | } | |
| 144 | } | |
| 145 | ||
| 146 | test "floor16" { | |
| 147 | try expect(__floorh(1.3) == 1.0); | |
| 148 | try expect(__floorh(-1.3) == -2.0); | |
| 149 | try expect(__floorh(0.2) == 0.0); | |
| 150 | } | |
| 151 | ||
| 152 | test "floor32" { | |
| 153 | try expect(floorf(1.3) == 1.0); | |
| 154 | try expect(floorf(-1.3) == -2.0); | |
| 155 | try expect(floorf(0.2) == 0.0); | |
| 156 | } | |
| 157 | ||
| 158 | test "floor64" { | |
| 159 | try expect(floor(1.3) == 1.0); | |
| 160 | try expect(floor(-1.3) == -2.0); | |
| 161 | try expect(floor(0.2) == 0.0); | |
| 162 | } | |
| 163 | ||
| 164 | test "floor80" { | |
| 165 | try expect(__floorx(1.3) == 1.0); | |
| 166 | try expect(__floorx(-1.3) == -2.0); | |
| 167 | try expect(__floorx(0.2) == 0.0); | |
| 168 | } | |
| 169 | ||
| 170 | test "floor128" { | |
| 171 | try expect(floorq(1.3) == 1.0); | |
| 172 | try expect(floorq(-1.3) == -2.0); | |
| 173 | try expect(floorq(0.2) == 0.0); | |
| 174 | } | |
| 175 | ||
| 176 | test "floor16.special" { | |
| 177 | try expect(__floorh(0.0) == 0.0); | |
| 178 | try expect(__floorh(-0.0) == -0.0); | |
| 179 | try expect(math.isPositiveInf(__floorh(math.inf(f16)))); | |
| 180 | try expect(math.isNegativeInf(__floorh(-math.inf(f16)))); | |
| 181 | try expect(math.isNan(__floorh(math.nan(f16)))); | |
| 182 | } | |
| 183 | ||
| 184 | test "floor32.special" { | |
| 185 | try expect(floorf(0.0) == 0.0); | |
| 186 | try expect(floorf(-0.0) == -0.0); | |
| 187 | try expect(math.isPositiveInf(floorf(math.inf(f32)))); | |
| 188 | try expect(math.isNegativeInf(floorf(-math.inf(f32)))); | |
| 189 | try expect(math.isNan(floorf(math.nan(f32)))); | |
| 190 | } | |
| 191 | ||
| 192 | test "floor64.special" { | |
| 193 | try expect(floor(0.0) == 0.0); | |
| 194 | try expect(floor(-0.0) == -0.0); | |
| 195 | try expect(math.isPositiveInf(floor(math.inf(f64)))); | |
| 196 | try expect(math.isNegativeInf(floor(-math.inf(f64)))); | |
| 197 | try expect(math.isNan(floor(math.nan(f64)))); | |
| 198 | } | |
| 199 | ||
| 200 | test "floor80.special" { | |
| 201 | try expect(__floorx(0.0) == 0.0); | |
| 202 | try expect(__floorx(-0.0) == -0.0); | |
| 203 | try expect(math.isPositiveInf(__floorx(math.inf(f80)))); | |
| 204 | try expect(math.isNegativeInf(__floorx(-math.inf(f80)))); | |
| 205 | try expect(math.isNan(__floorx(math.nan(f80)))); | |
| 206 | } | |
| 207 | ||
| 208 | test "floor128.special" { | |
| 209 | try expect(floorq(0.0) == 0.0); | |
| 210 | try expect(floorq(-0.0) == -0.0); | |
| 211 | try expect(math.isPositiveInf(floorq(math.inf(f128)))); | |
| 212 | try expect(math.isNegativeInf(floorq(-math.inf(f128)))); | |
| 213 | try expect(math.isNan(floorq(math.nan(f128)))); | |
| 214 | } | |
| 215 | ||
| 216 | test "ceil16" { | |
| 217 | try expect(__ceilh(1.3) == 2.0); | |
| 218 | try expect(__ceilh(-1.3) == -1.0); | |
| 219 | try expect(__ceilh(0.2) == 1.0); | |
| 220 | } | |
| 221 | ||
| 222 | test "ceil32" { | |
| 223 | try expect(ceilf(1.3) == 2.0); | |
| 224 | try expect(ceilf(-1.3) == -1.0); | |
| 225 | try expect(ceilf(0.2) == 1.0); | |
| 226 | } | |
| 227 | ||
| 228 | test "ceil64" { | |
| 229 | try expect(ceil(1.3) == 2.0); | |
| 230 | try expect(ceil(-1.3) == -1.0); | |
| 231 | try expect(ceil(0.2) == 1.0); | |
| 232 | } | |
| 233 | ||
| 234 | test "ceil80" { | |
| 235 | try expect(__ceilx(1.3) == 2.0); | |
| 236 | try expect(__ceilx(-1.3) == -1.0); | |
| 237 | try expect(__ceilx(0.2) == 1.0); | |
| 238 | } | |
| 239 | ||
| 240 | test "ceil128" { | |
| 241 | try expect(ceilq(1.3) == 2.0); | |
| 242 | try expect(ceilq(-1.3) == -1.0); | |
| 243 | try expect(ceilq(0.2) == 1.0); | |
| 244 | } | |
| 245 | ||
| 246 | test "ceil16.special" { | |
| 247 | try expect(__ceilh(0.0) == 0.0); | |
| 248 | try expect(__ceilh(-0.0) == -0.0); | |
| 249 | try expect(math.isPositiveInf(__ceilh(math.inf(f16)))); | |
| 250 | try expect(math.isNegativeInf(__ceilh(-math.inf(f16)))); | |
| 251 | try expect(math.isNan(__ceilh(math.nan(f16)))); | |
| 252 | } | |
| 253 | ||
| 254 | test "ceil32.special" { | |
| 255 | try expect(ceilf(0.0) == 0.0); | |
| 256 | try expect(ceilf(-0.0) == -0.0); | |
| 257 | try expect(math.isPositiveInf(ceilf(math.inf(f32)))); | |
| 258 | try expect(math.isNegativeInf(ceilf(-math.inf(f32)))); | |
| 259 | try expect(math.isNan(ceilf(math.nan(f32)))); | |
| 260 | } | |
| 261 | ||
| 262 | test "ceil64.special" { | |
| 263 | try expect(ceil(0.0) == 0.0); | |
| 264 | try expect(ceil(-0.0) == -0.0); | |
| 265 | try expect(math.isPositiveInf(ceil(math.inf(f64)))); | |
| 266 | try expect(math.isNegativeInf(ceil(-math.inf(f64)))); | |
| 267 | try expect(math.isNan(ceil(math.nan(f64)))); | |
| 268 | } | |
| 269 | ||
| 270 | test "ceil80.special" { | |
| 271 | try expect(__ceilx(0.0) == 0.0); | |
| 272 | try expect(__ceilx(-0.0) == -0.0); | |
| 273 | try expect(math.isPositiveInf(__ceilx(math.inf(f80)))); | |
| 274 | try expect(math.isNegativeInf(__ceilx(-math.inf(f80)))); | |
| 275 | try expect(math.isNan(__ceilx(math.nan(f80)))); | |
| 276 | } | |
| 277 | ||
| 278 | test "ceil128.special" { | |
| 279 | try expect(ceilq(0.0) == 0.0); | |
| 280 | try expect(ceilq(-0.0) == -0.0); | |
| 281 | try expect(math.isPositiveInf(ceilq(math.inf(f128)))); | |
| 282 | try expect(math.isNegativeInf(ceilq(-math.inf(f128)))); | |
| 283 | try expect(math.isNan(ceilq(math.nan(f128)))); | |
| 284 | } |
test/behavior/floatop.zig+18| ... | ... | @@ -1298,6 +1298,24 @@ test "@ceil f80/f128/c_longdouble" { |
| 1298 | 1298 | try comptime testCeil(c_longdouble); |
| 1299 | 1299 | } |
| 1300 | 1300 | |
| 1301 | test "@ceil f80 maxInt(u64)" { | |
| 1302 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1303 | if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest; | |
| 1304 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | |
| 1305 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 1306 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 1307 | ||
| 1308 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { | |
| 1309 | // https://github.com/ziglang/zig/issues/12602 | |
| 1310 | return error.SkipZigTest; | |
| 1311 | } | |
| 1312 | ||
| 1313 | var x: u64 = std.math.maxInt(u64); | |
| 1314 | x = x; | |
| 1315 | const float: f80 = @floatFromInt(x); | |
| 1316 | try std.testing.expect(float == @ceil(float)); | |
| 1317 | } | |
| 1318 | ||
| 1301 | 1319 | fn testCeil(comptime T: type) !void { |
| 1302 | 1320 | var two_point_one: T = 2.1; |
| 1303 | 1321 | try expect(@ceil(two_point_one) == 3.0); |