| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); |
| 3 | |
| 4 | const compiler_rt = @import("../compiler_rt.zig"); |
| 5 | const symbol = compiler_rt.symbol; |
| 6 | |
| 7 | const Unordered = if (builtin.cpu.arch == .avr) |
| 8 | i8 |
| 9 | else if (builtin.cpu.arch.isAARCH64()) |
| 10 | i32 |
| 11 | else if (builtin.target.cTypeBitSize(.long).? >= builtin.target.ptrBitWidth()) |
| 12 | c_long |
| 13 | else |
| 14 | c_longlong; |
| 15 | pub const Order = enum(Unordered) { lt = -1, eq = 0, gt = 1 }; |
| 16 | const SparcOrder = enum(i32) { eq = 0, lt = 1, gt = 2, un = 3 }; |
| 17 | |
| 18 | comptime { |
| 19 | symbol(&__cmphf2, "__cmphf2"); |
| 20 | symbol(&__cmphf2, "__eqhf2"); |
| 21 | symbol(&__cmphf2, "__nehf2"); |
| 22 | symbol(&__cmphf2, "__lthf2"); |
| 23 | symbol(&__cmphf2, "__lehf2"); |
| 24 | symbol(&__gehf2, "__gthf2"); |
| 25 | symbol(&__gehf2, "__gehf2"); |
| 26 | symbol(&__unordhf2, "__unordhf2"); |
| 27 | |
| 28 | if (compiler_rt.want_aeabi) { |
| 29 | symbol(&__aeabi_fcmpeq, "__aeabi_fcmpeq"); |
| 30 | symbol(&__aeabi_fcmplt, "__aeabi_fcmplt"); |
| 31 | symbol(&__aeabi_fcmple, "__aeabi_fcmple"); |
| 32 | symbol(&__aeabi_fcmpgt, "__aeabi_fcmpgt"); |
| 33 | symbol(&__aeabi_fcmpge, "__aeabi_fcmpge"); |
| 34 | symbol(&__aeabi_fcmpun, "__aeabi_fcmpun"); |
| 35 | |
| 36 | symbol(&__aeabi_dcmpeq, "__aeabi_dcmpeq"); |
| 37 | symbol(&__aeabi_dcmplt, "__aeabi_dcmplt"); |
| 38 | symbol(&__aeabi_dcmple, "__aeabi_dcmple"); |
| 39 | symbol(&__aeabi_dcmpgt, "__aeabi_dcmpgt"); |
| 40 | symbol(&__aeabi_dcmpge, "__aeabi_dcmpge"); |
| 41 | symbol(&__aeabi_dcmpun, "__aeabi_dcmpun"); |
| 42 | } else { |
| 43 | symbol(&__cmpsf2, "__cmpsf2"); |
| 44 | symbol(&__cmpsf2, "__eqsf2"); |
| 45 | symbol(&__cmpsf2, "__nesf2"); |
| 46 | symbol(&__cmpsf2, "__ltsf2"); |
| 47 | symbol(&__cmpsf2, "__lesf2"); |
| 48 | symbol(&__gesf2, "__gtsf2"); |
| 49 | symbol(&__gesf2, "__gesf2"); |
| 50 | symbol(&__unordsf2, "__unordsf2"); |
| 51 | |
| 52 | symbol(&__cmpdf2, "__cmpdf2"); |
| 53 | symbol(&__cmpdf2, "__eqdf2"); |
| 54 | symbol(&__cmpdf2, "__nedf2"); |
| 55 | symbol(&__cmpdf2, "__ltdf2"); |
| 56 | symbol(&__cmpdf2, "__ledf2"); |
| 57 | symbol(&__gedf2, "__gtdf2"); |
| 58 | symbol(&__gedf2, "__gedf2"); |
| 59 | symbol(&__unorddf2, "__unorddf2"); |
| 60 | } |
| 61 | |
| 62 | symbol(&__cmpxf2, "__cmpxf2"); |
| 63 | symbol(&__cmpxf2, "__eqxf2"); |
| 64 | symbol(&__cmpxf2, "__nexf2"); |
| 65 | symbol(&__cmpxf2, "__ltxf2"); |
| 66 | symbol(&__cmpxf2, "__lexf2"); |
| 67 | symbol(&__gexf2, "__gtxf2"); |
| 68 | symbol(&__gexf2, "__gexf2"); |
| 69 | symbol(&__unordxf2, "__unordxf2"); |
| 70 | |
| 71 | if (compiler_rt.want_ppc_abi) { |
| 72 | symbol(&__cmptf2, "__eqkf2"); |
| 73 | symbol(&__cmptf2, "__nekf2"); |
| 74 | symbol(&__cmptf2, "__ltkf2"); |
| 75 | symbol(&__cmptf2, "__lekf2"); |
| 76 | symbol(&__getf2, "__gtkf2"); |
| 77 | symbol(&__getf2, "__gekf2"); |
| 78 | symbol(&__unordtf2, "__unordkf2"); |
| 79 | } else if (compiler_rt.want_sparc64_abi) { |
| 80 | symbol(&_Qp_cmp, "_Qp_cmp"); |
| 81 | symbol(&_Qp_feq, "_Qp_feq"); |
| 82 | symbol(&_Qp_fne, "_Qp_fne"); |
| 83 | symbol(&_Qp_flt, "_Qp_flt"); |
| 84 | symbol(&_Qp_fle, "_Qp_fle"); |
| 85 | symbol(&_Qp_fgt, "_Qp_fgt"); |
| 86 | symbol(&_Qp_fge, "_Qp_fge"); |
| 87 | } else if (compiler_rt.want_sparc32_abi) { |
| 88 | symbol(&_Q_cmp, "_Q_cmp"); |
| 89 | symbol(&_Q_feq, "_Q_feq"); |
| 90 | symbol(&_Q_fne, "_Q_fne"); |
| 91 | symbol(&_Q_flt, "_Q_flt"); |
| 92 | symbol(&_Q_fle, "_Q_fle"); |
| 93 | symbol(&_Q_fgt, "_Q_fgt"); |
| 94 | symbol(&_Q_fge, "_Q_fge"); |
| 95 | } else { |
| 96 | symbol(&__cmptf2, "__cmptf2"); |
| 97 | symbol(&__cmptf2, "__eqtf2"); |
| 98 | symbol(&__cmptf2, "__netf2"); |
| 99 | symbol(&__cmptf2, "__lttf2"); |
| 100 | symbol(&__cmptf2, "__letf2"); |
| 101 | symbol(&__getf2, "__gttf2"); |
| 102 | symbol(&__getf2, "__getf2"); |
| 103 | symbol(&__unordtf2, "__unordtf2"); |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | fn __cmphf2(a: compiler_rt.f16.Abi, b: compiler_rt.f16.Abi) callconv(.c) Order { |
| 108 | return cmp_f16(compiler_rt.f16.fromAbi(a), compiler_rt.f16.fromAbi(b)) orelse .gt; |
| 109 | } |
| 110 | fn __gehf2(a: compiler_rt.f16.Abi, b: compiler_rt.f16.Abi) callconv(.c) Order { |
| 111 | return cmp_f16(compiler_rt.f16.fromAbi(a), compiler_rt.f16.fromAbi(b)) orelse .lt; |
| 112 | } |
| 113 | fn __unordhf2(a: compiler_rt.f16.Abi, b: compiler_rt.f16.Abi) callconv(.c) Unordered { |
| 114 | return @intFromBool(unord_f16(compiler_rt.f16.fromAbi(a), compiler_rt.f16.fromAbi(b))); |
| 115 | } |
| 116 | pub fn cmp_f16(a: f16, b: f16) ?Order { |
| 117 | return cmpf2(f16, a, b); |
| 118 | } |
| 119 | pub fn unord_f16(a: f16, b: f16) bool { |
| 120 | return unord(f16, a, b); |
| 121 | } |
| 122 | |
| 123 | fn __cmpsf2(a: compiler_rt.f32.Abi, b: compiler_rt.f32.Abi) callconv(.c) Order { |
| 124 | return cmp_f32(compiler_rt.f32.fromAbi(a), compiler_rt.f32.fromAbi(b)) orelse .gt; |
| 125 | } |
| 126 | fn __aeabi_fcmpeq(a: f32, b: f32) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 127 | return @intFromBool(cmp_f32(a, b) == .eq); |
| 128 | } |
| 129 | fn __aeabi_fcmplt(a: f32, b: f32) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 130 | return @intFromBool(cmp_f32(a, b) == .lt); |
| 131 | } |
| 132 | fn __aeabi_fcmple(a: f32, b: f32) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 133 | return @intFromBool(cmp_f32(a, b) orelse .gt != .gt); |
| 134 | } |
| 135 | fn __gesf2(a: compiler_rt.f32.Abi, b: compiler_rt.f32.Abi) callconv(.c) Order { |
| 136 | return cmp_f32(compiler_rt.f32.fromAbi(a), compiler_rt.f32.fromAbi(b)) orelse .lt; |
| 137 | } |
| 138 | fn __aeabi_fcmpge(a: f32, b: f32) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 139 | return @intFromBool(cmp_f32(a, b) orelse .lt != .lt); |
| 140 | } |
| 141 | fn __aeabi_fcmpgt(a: f32, b: f32) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 142 | return @intFromBool(cmp_f32(a, b) == .gt); |
| 143 | } |
| 144 | fn __unordsf2(a: compiler_rt.f32.Abi, b: compiler_rt.f32.Abi) callconv(.c) Unordered { |
| 145 | return @intFromBool(unord_f32(compiler_rt.f32.fromAbi(a), compiler_rt.f32.fromAbi(b))); |
| 146 | } |
| 147 | fn __aeabi_fcmpun(a: f32, b: f32) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 148 | return @intFromBool(unord_f32(a, b)); |
| 149 | } |
| 150 | pub fn cmp_f32(a: f32, b: f32) ?Order { |
| 151 | return cmpf2(f32, a, b); |
| 152 | } |
| 153 | pub fn unord_f32(a: f32, b: f32) bool { |
| 154 | return unord(f32, a, b); |
| 155 | } |
| 156 | |
| 157 | fn __cmpdf2(a: compiler_rt.f64.Abi, b: compiler_rt.f64.Abi) callconv(.c) Order { |
| 158 | return cmp_f64(compiler_rt.f64.fromAbi(a), compiler_rt.f64.fromAbi(b)) orelse .gt; |
| 159 | } |
| 160 | fn __aeabi_dcmpeq(a: f64, b: f64) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 161 | return @intFromBool(cmp_f64(a, b) == .eq); |
| 162 | } |
| 163 | fn __aeabi_dcmplt(a: f64, b: f64) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 164 | return @intFromBool(cmp_f64(a, b) == .lt); |
| 165 | } |
| 166 | fn __aeabi_dcmple(a: f64, b: f64) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 167 | return @intFromBool(cmp_f64(a, b) orelse .gt != .gt); |
| 168 | } |
| 169 | fn __gedf2(a: compiler_rt.f64.Abi, b: compiler_rt.f64.Abi) callconv(.c) Order { |
| 170 | return cmp_f64(compiler_rt.f64.fromAbi(a), compiler_rt.f64.fromAbi(b)) orelse .lt; |
| 171 | } |
| 172 | fn __aeabi_dcmpge(a: f64, b: f64) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 173 | return @intFromBool(cmp_f64(a, b) orelse .lt != .lt); |
| 174 | } |
| 175 | fn __aeabi_dcmpgt(a: f64, b: f64) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 176 | return @intFromBool(cmp_f64(a, b) == .gt); |
| 177 | } |
| 178 | fn __unorddf2(a: compiler_rt.f64.Abi, b: compiler_rt.f64.Abi) callconv(.c) Unordered { |
| 179 | return @intFromBool(unord_f64(compiler_rt.f64.fromAbi(a), compiler_rt.f64.fromAbi(b))); |
| 180 | } |
| 181 | fn __aeabi_dcmpun(a: f64, b: f64) callconv(.{ .arm_aapcs = .{} }) i32 { |
| 182 | return @intFromBool(unord_f64(a, b)); |
| 183 | } |
| 184 | pub fn cmp_f64(a: f64, b: f64) ?Order { |
| 185 | return cmpf2(f64, a, b); |
| 186 | } |
| 187 | pub fn unord_f64(a: f64, b: f64) bool { |
| 188 | return unord(f64, a, b); |
| 189 | } |
| 190 | |
| 191 | fn __cmpxf2(a: compiler_rt.f80.Abi, b: compiler_rt.f80.Abi) callconv(.c) Order { |
| 192 | return cmp_f80(compiler_rt.f80.fromAbi(a), compiler_rt.f80.fromAbi(b)) orelse .gt; |
| 193 | } |
| 194 | fn __gexf2(a: compiler_rt.f80.Abi, b: compiler_rt.f80.Abi) callconv(.c) Order { |
| 195 | return cmp_f80(compiler_rt.f80.fromAbi(a), compiler_rt.f80.fromAbi(b)) orelse .lt; |
| 196 | } |
| 197 | fn __unordxf2(a: compiler_rt.f80.Abi, b: compiler_rt.f80.Abi) callconv(.c) Unordered { |
| 198 | return @intFromBool(unord_f80(compiler_rt.f80.fromAbi(a), compiler_rt.f80.fromAbi(b))); |
| 199 | } |
| 200 | pub fn cmp_f80(a: f80, b: f80) ?Order { |
| 201 | const a_rep = std.math.F80.fromFloat(a); |
| 202 | const b_rep = std.math.F80.fromFloat(b); |
| 203 | const sig_bits = std.math.floatMantissaBits(f80); |
| 204 | const int_bit = 0x8000000000000000; |
| 205 | const sign_bit = 0x8000; |
| 206 | const special_exp = 0x7FFF; |
| 207 | |
| 208 | // If either a or b is NaN, they are unordered. |
| 209 | if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or |
| 210 | (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0)) |
| 211 | return null; |
| 212 | |
| 213 | // If a and b are both zeros, they are equal. |
| 214 | if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0) |
| 215 | return .eq; |
| 216 | |
| 217 | if (@intFromBool(a_rep.exp == b_rep.exp) & @intFromBool(a_rep.fraction == b_rep.fraction) != 0) { |
| 218 | return .eq; |
| 219 | } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) { |
| 220 | // signs are different |
| 221 | if (@as(i16, @bitCast(a_rep.exp)) < @as(i16, @bitCast(b_rep.exp))) { |
| 222 | return .lt; |
| 223 | } else { |
| 224 | return .gt; |
| 225 | } |
| 226 | } else { |
| 227 | const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits); |
| 228 | const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits); |
| 229 | if ((a_fraction < b_fraction) == (a_rep.exp & sign_bit == 0)) { |
| 230 | return .lt; |
| 231 | } else { |
| 232 | return .gt; |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | pub fn unord_f80(a: f80, b: f80) bool { |
| 237 | return unord(f80, a, b); |
| 238 | } |
| 239 | |
| 240 | fn __cmptf2(a: compiler_rt.f128.Abi, b: compiler_rt.f128.Abi) callconv(.c) Order { |
| 241 | return cmp_f128(compiler_rt.f128.fromAbi(a), compiler_rt.f128.fromAbi(b)) orelse .gt; |
| 242 | } |
| 243 | fn __getf2(a: compiler_rt.f128.Abi, b: compiler_rt.f128.Abi) callconv(.c) Order { |
| 244 | return cmp_f128(compiler_rt.f128.fromAbi(a), compiler_rt.f128.fromAbi(b)) orelse .lt; |
| 245 | } |
| 246 | fn __unordtf2(a: compiler_rt.f128.Abi, b: compiler_rt.f128.Abi) callconv(.c) Unordered { |
| 247 | return @intFromBool(unord_f128(compiler_rt.f128.fromAbi(a), compiler_rt.f128.fromAbi(b))); |
| 248 | } |
| 249 | fn _Qp_cmp(a: *const f128, b: *const f128) callconv(.c) SparcOrder { |
| 250 | return switch (cmp_f128(a.*, b.*) orelse return .un) { |
| 251 | .lt => .lt, |
| 252 | .eq => .eq, |
| 253 | .gt => .gt, |
| 254 | }; |
| 255 | } |
| 256 | fn _Qp_feq(a: *const f128, b: *const f128) callconv(.c) i32 { |
| 257 | return @intFromBool(cmp_f128(a.*, b.*) == .eq); |
| 258 | } |
| 259 | fn _Qp_fne(a: *const f128, b: *const f128) callconv(.c) i32 { |
| 260 | return @intFromBool(cmp_f128(a.*, b.*) != .eq); |
| 261 | } |
| 262 | fn _Qp_flt(a: *const f128, b: *const f128) callconv(.c) i32 { |
| 263 | return @intFromBool(cmp_f128(a.*, b.*) == .lt); |
| 264 | } |
| 265 | fn _Qp_fle(a: *const f128, b: *const f128) callconv(.c) i32 { |
| 266 | return @intFromBool((cmp_f128(a.*, b.*) orelse .gt) != .gt); |
| 267 | } |
| 268 | fn _Qp_fgt(a: *const f128, b: *const f128) callconv(.c) i32 { |
| 269 | return @intFromBool(cmp_f128(a.*, b.*) == .gt); |
| 270 | } |
| 271 | fn _Qp_fge(a: *const f128, b: *const f128) callconv(.c) i32 { |
| 272 | return @intFromBool((cmp_f128(a.*, b.*) orelse .lt) != .lt); |
| 273 | } |
| 274 | fn _Q_cmp(a: f128, b: f128) callconv(.c) SparcOrder { |
| 275 | return switch (cmp_f128(a, b) orelse return .un) { |
| 276 | .lt => .lt, |
| 277 | .eq => .eq, |
| 278 | .gt => .gt, |
| 279 | }; |
| 280 | } |
| 281 | fn _Q_feq(a: f128, b: f128) callconv(.c) i32 { |
| 282 | return @intFromBool(cmp_f128(a, b) == .eq); |
| 283 | } |
| 284 | fn _Q_fne(a: f128, b: f128) callconv(.c) i32 { |
| 285 | return @intFromBool(cmp_f128(a, b) != .eq); |
| 286 | } |
| 287 | fn _Q_flt(a: f128, b: f128) callconv(.c) i32 { |
| 288 | return @intFromBool(cmp_f128(a, b) == .lt); |
| 289 | } |
| 290 | fn _Q_fle(a: f128, b: f128) callconv(.c) i32 { |
| 291 | return @intFromBool((cmp_f128(a, b) orelse .gt) != .gt); |
| 292 | } |
| 293 | fn _Q_fgt(a: f128, b: f128) callconv(.c) i32 { |
| 294 | return @intFromBool(cmp_f128(a, b) == .gt); |
| 295 | } |
| 296 | fn _Q_fge(a: f128, b: f128) callconv(.c) i32 { |
| 297 | return @intFromBool((cmp_f128(a, b) orelse .lt) != .lt); |
| 298 | } |
| 299 | pub fn cmp_f128(a: f128, b: f128) ?Order { |
| 300 | return cmpf2(f128, a, b); |
| 301 | } |
| 302 | pub fn unord_f128(a: f128, b: f128) bool { |
| 303 | return unord(f128, a, b); |
| 304 | } |
| 305 | |
| 306 | inline fn cmpf2(comptime T: type, a: T, b: T) ?Order { |
| 307 | const bits = @typeInfo(T).float.bits; |
| 308 | const srep_t = @Int(.signed, bits); |
| 309 | const rep_t = @Int(.unsigned, bits); |
| 310 | |
| 311 | const significandBits = std.math.floatMantissaBits(T); |
| 312 | const exponentBits = std.math.floatExponentBits(T); |
| 313 | const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); |
| 314 | const absMask = signBit - 1; |
| 315 | const infT = comptime std.math.inf(T); |
| 316 | const infRep = @as(rep_t, @bitCast(infT)); |
| 317 | |
| 318 | const aInt = @as(srep_t, @bitCast(a)); |
| 319 | const bInt = @as(srep_t, @bitCast(b)); |
| 320 | const aAbs = @as(rep_t, @bitCast(aInt)) & absMask; |
| 321 | const bAbs = @as(rep_t, @bitCast(bInt)) & absMask; |
| 322 | |
| 323 | // If either a or b is NaN, they are unordered. |
| 324 | if (aAbs > infRep or bAbs > infRep) return null; |
| 325 | |
| 326 | // If a and b are both zeros, they are equal. |
| 327 | if ((aAbs | bAbs) == 0) return .eq; |
| 328 | |
| 329 | // If at least one of a and b is positive, we get the same result comparing |
| 330 | // a and b as signed integers as we would with a floating-point compare. |
| 331 | if ((aInt & bInt) >= 0) { |
| 332 | if (aInt < bInt) { |
| 333 | return .lt; |
| 334 | } else if (aInt == bInt) { |
| 335 | return .eq; |
| 336 | } else return .gt; |
| 337 | } else { |
| 338 | // Otherwise, both are negative, so we need to flip the sense of the |
| 339 | // comparison to get the correct result. (This assumes a twos- or ones- |
| 340 | // complement integer representation; if integers are represented in a |
| 341 | // sign-magnitude representation, then this flip is incorrect). |
| 342 | if (aInt > bInt) { |
| 343 | return .lt; |
| 344 | } else if (aInt == bInt) { |
| 345 | return .eq; |
| 346 | } else return .gt; |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | test cmp_f80 { |
| 351 | try std.testing.expect(cmp_f80(1.0, 1.0) == .eq); |
| 352 | try std.testing.expect(cmp_f80(0.0, -0.0) == .eq); |
| 353 | try std.testing.expect(cmp_f80(2.0, 4.0) == .lt); |
| 354 | try std.testing.expect(cmp_f80(2.0, -4.0) == .gt); |
| 355 | try std.testing.expect(cmp_f80(-2.0, -4.0) == .gt); |
| 356 | try std.testing.expect(cmp_f80(-2.0, 4.0) == .lt); |
| 357 | } |
| 358 | |
| 359 | inline fn unord(comptime T: type, a: T, b: T) bool { |
| 360 | const rep_t = @Int(.unsigned, @typeInfo(T).float.bits); |
| 361 | |
| 362 | const significandBits = std.math.floatMantissaBits(T); |
| 363 | const exponentBits = std.math.floatExponentBits(T); |
| 364 | const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); |
| 365 | const absMask = signBit - 1; |
| 366 | const infRep = @as(rep_t, @bitCast(std.math.inf(T))); |
| 367 | |
| 368 | const aAbs: rep_t = @as(rep_t, @bitCast(a)) & absMask; |
| 369 | const bAbs: rep_t = @as(rep_t, @bitCast(b)) & absMask; |
| 370 | |
| 371 | return aAbs > infRep or bAbs > infRep; |
| 372 | } |
| 373 | |
| 374 | test { |
| 375 | _ = @import("comparesf2_test.zig"); |
| 376 | _ = @import("comparedf2_test.zig"); |
| 377 | } |