round.zig (5307B) - Raw
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/roundf.c 5 //! https://git.musl-libc.org/cgit/musl/tree/src/math/round.c 6 7 const std = @import("std"); 8 const builtin = @import("builtin"); 9 const math = std.math; 10 const mem = std.mem; 11 const expect = std.testing.expect; 12 const arch = builtin.cpu.arch; 13 const common = @import("common.zig"); 14 15 pub const panic = common.panic; 16 17 comptime { 18 @export(&__roundh, .{ .name = "__roundh", .linkage = common.linkage, .visibility = common.visibility }); 19 @export(&roundf, .{ .name = "roundf", .linkage = common.linkage, .visibility = common.visibility }); 20 @export(&round, .{ .name = "round", .linkage = common.linkage, .visibility = common.visibility }); 21 @export(&__roundx, .{ .name = "__roundx", .linkage = common.linkage, .visibility = common.visibility }); 22 if (common.want_ppc_abi) { 23 @export(&roundq, .{ .name = "roundf128", .linkage = common.linkage, .visibility = common.visibility }); 24 } 25 @export(&roundq, .{ .name = "roundq", .linkage = common.linkage, .visibility = common.visibility }); 26 @export(&roundl, .{ .name = "roundl", .linkage = common.linkage, .visibility = common.visibility }); 27 } 28 29 pub fn __roundh(x: f16) callconv(.c) f16 { 30 // TODO: more efficient implementation 31 return @floatCast(roundf(x)); 32 } 33 34 pub fn roundf(x_: f32) callconv(.c) f32 { 35 const f32_toint = 1.0 / math.floatEps(f32); 36 37 var x = x_; 38 const u: u32 = @bitCast(x); 39 const e = (u >> 23) & 0xFF; 40 var y: f32 = undefined; 41 42 if (e >= 0x7F + 23) { 43 return x; 44 } 45 if (u >> 31 != 0) { 46 x = -x; 47 } 48 if (e < 0x7F - 1) { 49 if (common.want_float_exceptions) mem.doNotOptimizeAway(x + f32_toint); 50 return 0 * @as(f32, @bitCast(u)); 51 } 52 53 y = x + f32_toint - f32_toint - x; 54 if (y > 0.5) { 55 y = y + x - 1; 56 } else if (y <= -0.5) { 57 y = y + x + 1; 58 } else { 59 y = y + x; 60 } 61 62 if (u >> 31 != 0) { 63 return -y; 64 } else { 65 return y; 66 } 67 } 68 69 pub fn round(x_: f64) callconv(.c) f64 { 70 const f64_toint = 1.0 / math.floatEps(f64); 71 72 var x = x_; 73 const u: u64 = @bitCast(x); 74 const e = (u >> 52) & 0x7FF; 75 var y: f64 = undefined; 76 77 if (e >= 0x3FF + 52) { 78 return x; 79 } 80 if (u >> 63 != 0) { 81 x = -x; 82 } 83 if (e < 0x3ff - 1) { 84 if (common.want_float_exceptions) mem.doNotOptimizeAway(x + f64_toint); 85 return 0 * @as(f64, @bitCast(u)); 86 } 87 88 y = x + f64_toint - f64_toint - x; 89 if (y > 0.5) { 90 y = y + x - 1; 91 } else if (y <= -0.5) { 92 y = y + x + 1; 93 } else { 94 y = y + x; 95 } 96 97 if (u >> 63 != 0) { 98 return -y; 99 } else { 100 return y; 101 } 102 } 103 104 pub fn __roundx(x: f80) callconv(.c) f80 { 105 // TODO: more efficient implementation 106 return @floatCast(roundq(x)); 107 } 108 109 pub fn roundq(x_: f128) callconv(.c) f128 { 110 const f128_toint = 1.0 / math.floatEps(f128); 111 112 var x = x_; 113 const u: u128 = @bitCast(x); 114 const e = (u >> 112) & 0x7FFF; 115 var y: f128 = undefined; 116 117 if (e >= 0x3FFF + 112) { 118 return x; 119 } 120 if (u >> 127 != 0) { 121 x = -x; 122 } 123 if (e < 0x3FFF - 1) { 124 if (common.want_float_exceptions) mem.doNotOptimizeAway(x + f128_toint); 125 return 0 * @as(f128, @bitCast(u)); 126 } 127 128 y = x + f128_toint - f128_toint - x; 129 if (y > 0.5) { 130 y = y + x - 1; 131 } else if (y <= -0.5) { 132 y = y + x + 1; 133 } else { 134 y = y + x; 135 } 136 137 if (u >> 127 != 0) { 138 return -y; 139 } else { 140 return y; 141 } 142 } 143 144 pub fn roundl(x: c_longdouble) callconv(.c) c_longdouble { 145 switch (@typeInfo(c_longdouble).float.bits) { 146 16 => return __roundh(x), 147 32 => return roundf(x), 148 64 => return round(x), 149 80 => return __roundx(x), 150 128 => return roundq(x), 151 else => @compileError("unreachable"), 152 } 153 } 154 155 test "round32" { 156 try expect(roundf(1.3) == 1.0); 157 try expect(roundf(-1.3) == -1.0); 158 try expect(roundf(0.2) == 0.0); 159 try expect(roundf(1.8) == 2.0); 160 } 161 162 test "round64" { 163 try expect(round(1.3) == 1.0); 164 try expect(round(-1.3) == -1.0); 165 try expect(round(0.2) == 0.0); 166 try expect(round(1.8) == 2.0); 167 } 168 169 test "round128" { 170 try expect(roundq(1.3) == 1.0); 171 try expect(roundq(-1.3) == -1.0); 172 try expect(roundq(0.2) == 0.0); 173 try expect(roundq(1.8) == 2.0); 174 } 175 176 test "round32.special" { 177 try expect(roundf(0.0) == 0.0); 178 try expect(roundf(-0.0) == -0.0); 179 try expect(math.isPositiveInf(roundf(math.inf(f32)))); 180 try expect(math.isNegativeInf(roundf(-math.inf(f32)))); 181 try expect(math.isNan(roundf(math.nan(f32)))); 182 } 183 184 test "round64.special" { 185 try expect(round(0.0) == 0.0); 186 try expect(round(-0.0) == -0.0); 187 try expect(math.isPositiveInf(round(math.inf(f64)))); 188 try expect(math.isNegativeInf(round(-math.inf(f64)))); 189 try expect(math.isNan(round(math.nan(f64)))); 190 } 191 192 test "round128.special" { 193 try expect(roundq(0.0) == 0.0); 194 try expect(roundq(-0.0) == -0.0); 195 try expect(math.isPositiveInf(roundq(math.inf(f128)))); 196 try expect(math.isNegativeInf(roundq(-math.inf(f128)))); 197 try expect(math.isNan(roundq(math.nan(f128)))); 198 }