| 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/truncf.c |
| 5 | //! https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c |
| 6 | |
| 7 | const std = @import("std"); |
| 8 | const math = std.math; |
| 9 | const mem = std.mem; |
| 10 | const expect = std.testing.expect; |
| 11 | |
| 12 | const compiler_rt = @import("../compiler_rt.zig"); |
| 13 | const symbol = compiler_rt.symbol; |
| 14 | |
| 15 | comptime { |
| 16 | symbol(&__trunch, "__trunch"); |
| 17 | symbol(&truncf, "truncf"); |
| 18 | symbol(&trunc, "trunc"); |
| 19 | symbol(&__truncx, "__truncx"); |
| 20 | symbol(&truncq, "truncf128"); |
| 21 | symbol(&truncl, "truncl"); |
| 22 | } |
| 23 | |
| 24 | fn __trunch(x: compiler_rt.f16.Abi) callconv(.c) compiler_rt.f16.Abi { |
| 25 | return compiler_rt.f16.toAbi(trunc_f16(compiler_rt.f16.fromAbi(x))); |
| 26 | } |
| 27 | pub fn trunc_f16(x: f16) f16 { |
| 28 | // TODO: more efficient implementation |
| 29 | return @floatCast(trunc_f32(x)); |
| 30 | } |
| 31 | |
| 32 | fn truncf(x: compiler_rt.f32.Abi) callconv(.c) compiler_rt.f32.Abi { |
| 33 | return compiler_rt.f32.toAbi(trunc_f32(compiler_rt.f32.fromAbi(x))); |
| 34 | } |
| 35 | pub fn trunc_f32(x: f32) f32 { |
| 36 | const u: u32 = @bitCast(x); |
| 37 | var e = @as(i32, @intCast(((u >> 23) & 0xFF))) - 0x7F + 9; |
| 38 | var m: u32 = undefined; |
| 39 | |
| 40 | if (e >= 23 + 9) { |
| 41 | return x; |
| 42 | } |
| 43 | if (e < 9) { |
| 44 | e = 1; |
| 45 | } |
| 46 | |
| 47 | m = @as(u32, math.maxInt(u32)) >> @intCast(e); |
| 48 | if (u & m == 0) { |
| 49 | return x; |
| 50 | } else { |
| 51 | if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1p120); |
| 52 | return @bitCast(u & ~m); |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | fn trunc(x: compiler_rt.f64.Abi) callconv(.c) compiler_rt.f64.Abi { |
| 57 | return compiler_rt.f64.toAbi(trunc_f64(compiler_rt.f64.fromAbi(x))); |
| 58 | } |
| 59 | pub fn trunc_f64(x: f64) f64 { |
| 60 | const u: u64 = @bitCast(x); |
| 61 | var e = @as(i32, @intCast(((u >> 52) & 0x7FF))) - 0x3FF + 12; |
| 62 | var m: u64 = undefined; |
| 63 | |
| 64 | if (e >= 52 + 12) { |
| 65 | return x; |
| 66 | } |
| 67 | if (e < 12) { |
| 68 | e = 1; |
| 69 | } |
| 70 | |
| 71 | m = @as(u64, math.maxInt(u64)) >> @intCast(e); |
| 72 | if (u & m == 0) { |
| 73 | return x; |
| 74 | } else { |
| 75 | if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1p120); |
| 76 | return @bitCast(u & ~m); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | fn __truncx(x: compiler_rt.f80.Abi) callconv(.c) compiler_rt.f80.Abi { |
| 81 | return compiler_rt.f80.toAbi(trunc_f80(compiler_rt.f80.fromAbi(x))); |
| 82 | } |
| 83 | pub fn trunc_f80(x: f80) f80 { |
| 84 | // TODO: more efficient implementation |
| 85 | return @floatCast(trunc_f128(x)); |
| 86 | } |
| 87 | |
| 88 | fn truncq(x: compiler_rt.f128.Abi) callconv(.c) compiler_rt.f128.Abi { |
| 89 | return compiler_rt.f128.toAbi(trunc_f128(compiler_rt.f128.fromAbi(x))); |
| 90 | } |
| 91 | pub fn trunc_f128(x: f128) f128 { |
| 92 | const u: u128 = @bitCast(x); |
| 93 | var e = @as(i32, @intCast(((u >> 112) & 0x7FFF))) - 0x3FFF + 16; |
| 94 | var m: u128 = undefined; |
| 95 | |
| 96 | if (e >= 112 + 16) { |
| 97 | return x; |
| 98 | } |
| 99 | if (e < 16) { |
| 100 | e = 1; |
| 101 | } |
| 102 | |
| 103 | m = @as(u128, math.maxInt(u128)) >> @intCast(e); |
| 104 | if (u & m == 0) { |
| 105 | return x; |
| 106 | } else { |
| 107 | if (compiler_rt.want_float_exceptions) mem.doNotOptimizeAway(x + 0x1p120); |
| 108 | return @bitCast(u & ~m); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | pub fn truncl(x: c_longdouble) callconv(.c) c_longdouble { |
| 113 | switch (@typeInfo(c_longdouble).float.bits) { |
| 114 | 64 => return trunc_f64(x), |
| 115 | 80 => return trunc_f80(x), |
| 116 | 128 => return trunc_f128(x), |
| 117 | else => comptime unreachable, |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | test trunc_f16 { |
| 122 | try expect(trunc_f16(1.3) == 1.0); |
| 123 | try expect(trunc_f16(-1.3) == -1.0); |
| 124 | try expect(math.isPositiveZero(trunc_f16(0.2))); |
| 125 | try expect(math.isNegativeZero(trunc_f16(-0.2))); |
| 126 | try expect(math.isPositiveZero(trunc_f16(0.0))); |
| 127 | try expect(math.isNegativeZero(trunc_f16(-0.0))); |
| 128 | try expect(math.isPositiveInf(trunc_f16(math.inf(f32)))); |
| 129 | try expect(math.isNegativeInf(trunc_f16(-math.inf(f32)))); |
| 130 | try expect(math.isNan(trunc_f16(math.nan(f32)))); |
| 131 | } |
| 132 | |
| 133 | test trunc_f32 { |
| 134 | try expect(trunc_f32(1.3) == 1.0); |
| 135 | try expect(trunc_f32(-1.3) == -1.0); |
| 136 | try expect(math.isPositiveZero(trunc_f32(0.2))); |
| 137 | try expect(math.isNegativeZero(trunc_f32(-0.2))); |
| 138 | try expect(math.isPositiveZero(trunc_f32(0.0))); |
| 139 | try expect(math.isNegativeZero(trunc_f32(-0.0))); |
| 140 | try expect(math.isPositiveInf(trunc_f32(math.inf(f32)))); |
| 141 | try expect(math.isNegativeInf(trunc_f32(-math.inf(f32)))); |
| 142 | try expect(math.isNan(trunc_f32(math.nan(f32)))); |
| 143 | } |
| 144 | |
| 145 | test trunc_f64 { |
| 146 | try expect(trunc_f64(1.3) == 1.0); |
| 147 | try expect(trunc_f64(-1.3) == -1.0); |
| 148 | try expect(math.isPositiveZero(trunc_f64(0.2))); |
| 149 | try expect(math.isNegativeZero(trunc_f64(-0.2))); |
| 150 | try expect(math.isPositiveZero(trunc_f64(0.0))); |
| 151 | try expect(math.isNegativeZero(trunc_f64(-0.0))); |
| 152 | try expect(math.isPositiveInf(trunc_f64(math.inf(f64)))); |
| 153 | try expect(math.isNegativeInf(trunc_f64(-math.inf(f64)))); |
| 154 | try expect(math.isNan(trunc_f64(math.nan(f64)))); |
| 155 | } |
| 156 | |
| 157 | test trunc_f80 { |
| 158 | try expect(trunc_f80(1.3) == 1.0); |
| 159 | try expect(trunc_f80(-1.3) == -1.0); |
| 160 | try expect(math.isPositiveZero(trunc_f80(0.2))); |
| 161 | try expect(math.isNegativeZero(trunc_f80(-0.2))); |
| 162 | try expect(math.isPositiveZero(trunc_f80(0.0))); |
| 163 | try expect(math.isNegativeZero(trunc_f80(-0.0))); |
| 164 | try expect(math.isPositiveInf(trunc_f80(math.inf(f64)))); |
| 165 | try expect(math.isNegativeInf(trunc_f80(-math.inf(f64)))); |
| 166 | try expect(math.isNan(trunc_f80(math.nan(f64)))); |
| 167 | } |
| 168 | |
| 169 | test trunc_f128 { |
| 170 | try expect(trunc_f128(1.3) == 1.0); |
| 171 | try expect(trunc_f128(-1.3) == -1.0); |
| 172 | try expect(math.isPositiveZero(trunc_f128(0.2))); |
| 173 | try expect(math.isNegativeZero(trunc_f128(-0.2))); |
| 174 | try expect(math.isPositiveZero(trunc_f128(0.0))); |
| 175 | try expect(math.isNegativeZero(trunc_f128(-0.0))); |
| 176 | try expect(math.isPositiveInf(trunc_f128(math.inf(f128)))); |
| 177 | try expect(math.isNegativeInf(trunc_f128(-math.inf(f128)))); |
| 178 | try expect(math.isNan(trunc_f128(math.nan(f128)))); |
| 179 | } |