authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2025-11-28 19:33:09-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-08 00:45:52+01:00
log938efe4aab09b5806cd3e9903619b95a5f122f36
treecbd969a877bc2590ca212d4b5ba17eb3c711e636
parent52e0f7870695f11eea7700df9a5c07025dc7d5a2

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.

6 files changed, 304 insertions(+), 476 deletions(-)

CMakeLists.txt+1-2
......@@ -221,7 +221,6 @@ set(ZIG_STAGE2_SOURCES
221221 lib/compiler_rt/aulldiv.zig
222222 lib/compiler_rt/aullrem.zig
223223 lib/compiler_rt/bswap.zig
224 lib/compiler_rt/ceil.zig
225224 lib/compiler_rt/clear_cache.zig
226225 lib/compiler_rt/cmp.zig
227226 lib/compiler_rt/cmpdf2.zig
......@@ -312,7 +311,7 @@ set(ZIG_STAGE2_SOURCES
312311 lib/compiler_rt/floatuntisf.zig
313312 lib/compiler_rt/floatuntitf.zig
314313 lib/compiler_rt/floatuntixf.zig
315 lib/compiler_rt/floor.zig
314 lib/compiler_rt/floor_ceil.zig
316315 lib/compiler_rt/fma.zig
317316 lib/compiler_rt/fmax.zig
318317 lib/compiler_rt/fmin.zig
lib/compiler_rt.zig+1-2
......@@ -231,12 +231,11 @@ comptime {
231231 _ = @import("compiler_rt/divtc3.zig");
232232
233233 // Math routines. Alphabetically sorted.
234 _ = @import("compiler_rt/ceil.zig");
235234 _ = @import("compiler_rt/cos.zig");
236235 _ = @import("compiler_rt/exp.zig");
237236 _ = @import("compiler_rt/exp2.zig");
238237 _ = @import("compiler_rt/fabs.zig");
239 _ = @import("compiler_rt/floor.zig");
238 _ = @import("compiler_rt/floor_ceil.zig");
240239 _ = @import("compiler_rt/fma.zig");
241240 _ = @import("compiler_rt/fmax.zig");
242241 _ = @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
8const std = @import("std");
9const builtin = @import("builtin");
10const arch = builtin.cpu.arch;
11const math = std.math;
12const mem = std.mem;
13const expect = std.testing.expect;
14const common = @import("common.zig");
15
16comptime {
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
28pub 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
48pub 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
68pub 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
99pub 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
128pub 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
157pub 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
168test "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
174test "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
180test "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
186test "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
192test "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
198test "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
206test "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
214test "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
222test "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
230test "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
8const std = @import("std");
9const builtin = @import("builtin");
10const math = std.math;
11const mem = std.mem;
12const expect = std.testing.expect;
13const arch = builtin.cpu.arch;
14const common = @import("common.zig");
15
16comptime {
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
28pub 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
47pub 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
66pub 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
97pub 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
128pub 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
157pub 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
168test "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
174test "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
180test "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
186test "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
192test "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
198test "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
206test "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
214test "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
222test "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
230test "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
12const std = @import("std");
13const math = std.math;
14const mem = std.mem;
15const expect = std.testing.expect;
16
17const common = @import("common.zig");
18
19comptime {
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
43pub fn __floorh(x: f16) callconv(.c) f16 {
44 return impl(f16, .floor, x);
45}
46
47pub fn floorf(x: f32) callconv(.c) f32 {
48 return impl(f32, .floor, x);
49}
50
51pub fn floor(x: f64) callconv(.c) f64 {
52 return impl(f64, .floor, x);
53}
54
55pub fn __floorx(x: f80) callconv(.c) f80 {
56 return impl(f80, .floor, x);
57}
58
59pub fn floorq(x: f128) callconv(.c) f128 {
60 return impl(f128, .floor, x);
61}
62
63pub fn floorl(x: c_longdouble) callconv(.c) c_longdouble {
64 return impl(std.meta.Float(@bitSizeOf(c_longdouble)), .floor, x);
65}
66
67pub fn __ceilh(x: f16) callconv(.c) f16 {
68 return impl(f16, .ceil, x);
69}
70
71pub fn ceilf(x: f32) callconv(.c) f32 {
72 return impl(f32, .ceil, x);
73}
74
75pub fn ceil(x: f64) callconv(.c) f64 {
76 return impl(f64, .ceil, x);
77}
78
79pub fn __ceilx(x: f80) callconv(.c) f80 {
80 return impl(f80, .ceil, x);
81}
82
83pub fn ceilq(x: f128) callconv(.c) f128 {
84 return impl(f128, .ceil, x);
85}
86
87pub fn ceill(x: c_longdouble) callconv(.c) c_longdouble {
88 return impl(std.meta.Float(@bitSizeOf(c_longdouble)), .ceil, x);
89}
90
91inline 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
146test "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
152test "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
158test "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
164test "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
170test "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
176test "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
184test "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
192test "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
200test "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
208test "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
216test "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
222test "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
228test "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
234test "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
240test "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
246test "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
254test "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
262test "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
270test "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
278test "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" {
12981298 try comptime testCeil(c_longdouble);
12991299}
13001300
1301test "@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
13011319fn testCeil(comptime T: type) !void {
13021320 var two_point_one: T = 2.1;
13031321 try expect(@ceil(two_point_one) == 3.0);