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 compiler_rt = @import("../compiler_rt.zig");
18const symbol = compiler_rt.symbol;
19
20comptime {
21 // floor
22 symbol(&__floorh, "__floorh");
23 symbol(&floorf, "floorf");
24 symbol(&floor, "floor");
25 symbol(&__floorx, "__floorx");
26 symbol(&floorq, "floorf128");
27 symbol(&floorl, "floorl");
28
29 // ceil
30 symbol(&__ceilh, "__ceilh");
31 symbol(&ceilf, "ceilf");
32 symbol(&ceil, "ceil");
33 symbol(&__ceilx, "__ceilx");
34 symbol(&ceilq, "ceilf128");
35 symbol(&ceill, "ceill");
36}
37
38fn __floorh(x: compiler_rt.f16.Abi) callconv(.c) compiler_rt.f16.Abi {
39 return compiler_rt.f16.toAbi(floor_f16(compiler_rt.f16.fromAbi(x)));
40}
41pub fn floor_f16(x: f16) f16 {
42 return impl(f16, .floor, x);
43}
44
45fn floorf(x: compiler_rt.f32.Abi) callconv(.c) compiler_rt.f32.Abi {
46 return compiler_rt.f32.toAbi(floor_f32(compiler_rt.f32.fromAbi(x)));
47}
48pub fn floor_f32(x: f32) f32 {
49 return impl(f32, .floor, x);
50}
51
52fn floor(x: compiler_rt.f64.Abi) callconv(.c) compiler_rt.f64.Abi {
53 return compiler_rt.f64.toAbi(floor_f64(compiler_rt.f64.fromAbi(x)));
54}
55pub fn floor_f64(x: f64) f64 {
56 return impl(f64, .floor, x);
57}
58
59fn __floorx(x: compiler_rt.f80.Abi) callconv(.c) compiler_rt.f80.Abi {
60 return compiler_rt.f80.toAbi(floor_f80(compiler_rt.f80.fromAbi(x)));
61}
62pub fn floor_f80(x: f80) f80 {
63 return impl(f80, .floor, x);
64}
65
66fn floorq(x: compiler_rt.f128.Abi) callconv(.c) compiler_rt.f128.Abi {
67 return compiler_rt.f128.toAbi(floor_f128(compiler_rt.f128.fromAbi(x)));
68}
69pub fn floor_f128(x: f128) f128 {
70 return impl(f128, .floor, x);
71}
72
73pub fn floorl(x: c_longdouble) callconv(.c) c_longdouble {
74 switch (@typeInfo(c_longdouble).float.bits) {
75 64 => return floor_f64(x),
76 80 => return floor_f80(x),
77 128 => return floor_f128(x),
78 else => comptime unreachable,
79 }
80}
81
82fn __ceilh(x: compiler_rt.f16.Abi) callconv(.c) compiler_rt.f16.Abi {
83 return compiler_rt.f16.toAbi(ceil_f16(compiler_rt.f16.fromAbi(x)));
84}
85pub fn ceil_f16(x: f16) f16 {
86 return impl(f16, .ceil, x);
87}
88
89fn ceilf(x: compiler_rt.f32.Abi) callconv(.c) compiler_rt.f32.Abi {
90 return compiler_rt.f32.toAbi(ceil_f32(compiler_rt.f32.fromAbi(x)));
91}
92pub fn ceil_f32(x: f32) f32 {
93 return impl(f32, .ceil, x);
94}
95
96fn ceil(x: compiler_rt.f64.Abi) callconv(.c) compiler_rt.f64.Abi {
97 return compiler_rt.f64.toAbi(ceil_f64(compiler_rt.f64.fromAbi(x)));
98}
99pub fn ceil_f64(x: f64) f64 {
100 return impl(f64, .ceil, x);
101}
102
103fn __ceilx(x: compiler_rt.f80.Abi) callconv(.c) compiler_rt.f80.Abi {
104 return compiler_rt.f80.toAbi(ceil_f80(compiler_rt.f80.fromAbi(x)));
105}
106pub fn ceil_f80(x: f80) f80 {
107 return impl(f80, .ceil, x);
108}
109
110fn ceilq(x: compiler_rt.f128.Abi) callconv(.c) compiler_rt.f128.Abi {
111 return compiler_rt.f128.toAbi(ceil_f128(compiler_rt.f128.fromAbi(x)));
112}
113pub fn ceil_f128(x: f128) f128 {
114 return impl(f128, .ceil, x);
115}
116
117pub fn ceill(x: c_longdouble) callconv(.c) c_longdouble {
118 switch (@typeInfo(c_longdouble).float.bits) {
119 64 => return ceil_f64(x),
120 80 => return ceil_f80(x),
121 128 => return ceil_f128(x),
122 else => comptime unreachable,
123 }
124}
125
126inline fn impl(comptime T: type, comptime op: enum { floor, ceil }, x: T) T {
127 const C = 1.0 / math.floatEps(T);
128 const mantissa = math.floatMantissaBits(T);
129 const mask = (1 << math.floatExponentBits(T)) - 1;
130 const bias = (1 << (math.floatExponentBits(T) - 1)) - 1;
131
132 const bits = @bitSizeOf(T);
133 const U = @Int(.unsigned, bits);
134 var u: U = @bitCast(x);
135 switch (T) {
136 f16, f32 => {
137 const e = @as(@Int(.signed, bits), @intCast((u >> mantissa) & mask)) - bias;
138 if (e >= mantissa) return x;
139
140 if (e >= 0) {
141 const m = (@as(U, 1) << @intCast(mantissa - e)) - 1;
142 if (u & m == 0) return x;
143 if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
144 if (u >> bits - 1 == @intFromBool(op == .floor)) u += m;
145 return @bitCast(u & ~m);
146 } else {
147 if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1.0p120);
148 return switch (op) {
149 .floor => if (u >> bits - 1 == 0) 0.0 else if (u << 1 != 0) -1.0 else x,
150 .ceil => if (u >> bits - 1 != 0) -0.0 else if (u << 1 != 0) 1.0 else x,
151 };
152 }
153 },
154 f64, f80, f128 => {
155 const e = (u >> mantissa) & mask;
156 if (e >= bias + math.floatFractionalBits(T) or x == 0) return x;
157
158 const positive = u >> @bitSizeOf(T) - 1 == 0;
159 const y: T = if (positive)
160 x + C - C - x
161 else
162 x - C + C - x;
163
164 if (e <= bias - 1) {
165 if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(y);
166 return switch (op) {
167 .floor => if (positive) 0.0 else -1.0,
168 .ceil => if (positive) 1.0 else -0.0,
169 };
170 }
171 switch (op) {
172 .floor => if (y > 0) return x + y - 1,
173 .ceil => if (y < 0) return x + y + 1,
174 }
175 return x + y;
176 },
177 else => unreachable,
178 }
179}
180
181test floor_f16 {
182 try expect(floor_f16(1.3) == 1.0);
183 try expect(floor_f16(-1.3) == -2.0);
184 try expect(floor_f16(-0.2) == -1.0);
185 try expect(math.isPositiveZero(floor_f16(0.2)));
186 try expect(math.isPositiveZero(floor_f16(0.0)));
187 try expect(math.isNegativeZero(floor_f16(-0.0)));
188 try expect(math.isPositiveInf(floor_f16(math.inf(f16))));
189 try expect(math.isNegativeInf(floor_f16(-math.inf(f16))));
190 try expect(math.isNan(floor_f16(math.nan(f16))));
191}
192
193test floor_f32 {
194 try expect(floor_f32(1.3) == 1.0);
195 try expect(floor_f32(-1.3) == -2.0);
196 try expect(floor_f32(-0.2) == -1.0);
197 try expect(math.isPositiveZero(floor_f32(0.2)));
198 try expect(math.isPositiveZero(floor_f32(0.0)));
199 try expect(math.isNegativeZero(floor_f32(-0.0)));
200 try expect(math.isPositiveInf(floor_f32(math.inf(f32))));
201 try expect(math.isNegativeInf(floor_f32(-math.inf(f32))));
202 try expect(math.isNan(floor_f32(math.nan(f32))));
203}
204
205test floor_f64 {
206 try expect(floor_f64(1.3) == 1.0);
207 try expect(floor_f64(-1.3) == -2.0);
208 try expect(floor_f64(-0.2) == -1.0);
209 try expect(math.isPositiveZero(floor_f64(0.2)));
210 try expect(math.isPositiveZero(floor_f64(0.0)));
211 try expect(math.isNegativeZero(floor_f64(-0.0)));
212 try expect(math.isPositiveInf(floor_f64(math.inf(f64))));
213 try expect(math.isNegativeInf(floor_f64(-math.inf(f64))));
214 try expect(math.isNan(floor_f64(math.nan(f64))));
215}
216
217test floor_f80 {
218 try expect(floor_f80(1.3) == 1.0);
219 try expect(floor_f80(-1.3) == -2.0);
220 try expect(floor_f80(-0.2) == -1.0);
221 try expect(math.isPositiveZero(floor_f80(0.2)));
222 try expect(math.isPositiveZero(floor_f80(0.0)));
223 try expect(math.isNegativeZero(floor_f80(-0.0)));
224 try expect(math.isPositiveInf(floor_f80(math.inf(f80))));
225 try expect(math.isNegativeInf(floor_f80(-math.inf(f80))));
226 try expect(math.isNan(floor_f80(math.nan(f80))));
227}
228
229test floor_f128 {
230 try expect(floor_f128(1.3) == 1.0);
231 try expect(floor_f128(-1.3) == -2.0);
232 try expect(floor_f128(-0.2) == -1.0);
233 try expect(math.isPositiveZero(floor_f128(0.2)));
234 try expect(math.isPositiveZero(floor_f128(0.0)));
235 try expect(math.isNegativeZero(floor_f128(-0.0)));
236 try expect(math.isPositiveInf(floor_f128(math.inf(f128))));
237 try expect(math.isNegativeInf(floor_f128(-math.inf(f128))));
238 try expect(math.isNan(floor_f128(math.nan(f128))));
239}
240
241test ceil_f16 {
242 try expect(ceil_f16(1.3) == 2.0);
243 try expect(ceil_f16(-1.3) == -1.0);
244 try expect(ceil_f16(0.2) == 1.0);
245 try expect(math.isNegativeZero(ceil_f16(-0.2)));
246 try expect(math.isPositiveZero(ceil_f16(0.0)));
247 try expect(math.isNegativeZero(ceil_f16(-0.0)));
248 try expect(math.isPositiveInf(ceil_f16(math.inf(f16))));
249 try expect(math.isNegativeInf(ceil_f16(-math.inf(f16))));
250 try expect(math.isNan(ceil_f16(math.nan(f16))));
251}
252
253test ceil_f32 {
254 try expect(ceil_f32(1.3) == 2.0);
255 try expect(ceil_f32(-1.3) == -1.0);
256 try expect(ceil_f32(0.2) == 1.0);
257 try expect(math.isNegativeZero(ceil_f32(-0.2)));
258 try expect(math.isPositiveZero(ceil_f32(0.0)));
259 try expect(math.isNegativeZero(ceil_f32(-0.0)));
260 try expect(math.isPositiveInf(ceil_f32(math.inf(f32))));
261 try expect(math.isNegativeInf(ceil_f32(-math.inf(f32))));
262 try expect(math.isNan(ceil_f32(math.nan(f32))));
263}
264
265test ceil_f64 {
266 try expect(ceil_f64(1.3) == 2.0);
267 try expect(ceil_f64(-1.3) == -1.0);
268 try expect(ceil_f64(0.2) == 1.0);
269 try expect(math.isNegativeZero(ceil_f64(-0.2)));
270 try expect(math.isPositiveZero(ceil_f64(0.0)));
271 try expect(math.isNegativeZero(ceil_f64(-0.0)));
272 try expect(math.isPositiveInf(ceil_f64(math.inf(f64))));
273 try expect(math.isNegativeInf(ceil_f64(-math.inf(f64))));
274 try expect(math.isNan(ceil_f64(math.nan(f64))));
275}
276
277test ceil_f80 {
278 try expect(ceil_f80(1.3) == 2.0);
279 try expect(ceil_f80(-1.3) == -1.0);
280 try expect(ceil_f80(0.2) == 1.0);
281 try expect(math.isNegativeZero(ceil_f80(-0.2)));
282 try expect(math.isPositiveZero(ceil_f80(0.0)));
283 try expect(math.isNegativeZero(ceil_f80(-0.0)));
284 try expect(math.isPositiveInf(ceil_f80(math.inf(f80))));
285 try expect(math.isNegativeInf(ceil_f80(-math.inf(f80))));
286 try expect(math.isNan(ceil_f80(math.nan(f80))));
287}
288
289test ceil_f128 {
290 try expect(ceil_f128(1.3) == 2.0);
291 try expect(ceil_f128(-1.3) == -1.0);
292 try expect(ceil_f128(0.2) == 1.0);
293 try expect(math.isNegativeZero(ceil_f128(-0.2)));
294 try expect(math.isPositiveZero(ceil_f128(0.0)));
295 try expect(math.isNegativeZero(ceil_f128(-0.0)));
296 try expect(math.isPositiveInf(ceil_f128(math.inf(f128))));
297 try expect(math.isNegativeInf(ceil_f128(-math.inf(f128))));
298 try expect(math.isNan(ceil_f128(math.nan(f128))));
299}