comparef.zig (4582B) - Raw
1 const std = @import("std"); 2 3 pub const LE = enum(i32) { 4 Less = -1, 5 Equal = 0, 6 Greater = 1, 7 8 const Unordered: LE = .Greater; 9 }; 10 11 pub const GE = enum(i32) { 12 Less = -1, 13 Equal = 0, 14 Greater = 1, 15 16 const Unordered: GE = .Less; 17 }; 18 19 pub inline fn cmpf2(comptime T: type, comptime RT: type, a: T, b: T) RT { 20 const bits = @typeInfo(T).float.bits; 21 const srep_t = std.meta.Int(.signed, bits); 22 const rep_t = std.meta.Int(.unsigned, bits); 23 24 const significandBits = std.math.floatMantissaBits(T); 25 const exponentBits = std.math.floatExponentBits(T); 26 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); 27 const absMask = signBit - 1; 28 const infT = comptime std.math.inf(T); 29 const infRep = @as(rep_t, @bitCast(infT)); 30 31 const aInt = @as(srep_t, @bitCast(a)); 32 const bInt = @as(srep_t, @bitCast(b)); 33 const aAbs = @as(rep_t, @bitCast(aInt)) & absMask; 34 const bAbs = @as(rep_t, @bitCast(bInt)) & absMask; 35 36 // If either a or b is NaN, they are unordered. 37 if (aAbs > infRep or bAbs > infRep) return RT.Unordered; 38 39 // If a and b are both zeros, they are equal. 40 if ((aAbs | bAbs) == 0) return .Equal; 41 42 // If at least one of a and b is positive, we get the same result comparing 43 // a and b as signed integers as we would with a floating-point compare. 44 if ((aInt & bInt) >= 0) { 45 if (aInt < bInt) { 46 return .Less; 47 } else if (aInt == bInt) { 48 return .Equal; 49 } else return .Greater; 50 } else { 51 // Otherwise, both are negative, so we need to flip the sense of the 52 // comparison to get the correct result. (This assumes a twos- or ones- 53 // complement integer representation; if integers are represented in a 54 // sign-magnitude representation, then this flip is incorrect). 55 if (aInt > bInt) { 56 return .Less; 57 } else if (aInt == bInt) { 58 return .Equal; 59 } else return .Greater; 60 } 61 } 62 63 pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { 64 const a_rep = std.math.F80.fromFloat(a); 65 const b_rep = std.math.F80.fromFloat(b); 66 const sig_bits = std.math.floatMantissaBits(f80); 67 const int_bit = 0x8000000000000000; 68 const sign_bit = 0x8000; 69 const special_exp = 0x7FFF; 70 71 // If either a or b is NaN, they are unordered. 72 if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or 73 (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0)) 74 return RT.Unordered; 75 76 // If a and b are both zeros, they are equal. 77 if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0) 78 return .Equal; 79 80 if (@intFromBool(a_rep.exp == b_rep.exp) & @intFromBool(a_rep.fraction == b_rep.fraction) != 0) { 81 return .Equal; 82 } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) { 83 // signs are different 84 if (@as(i16, @bitCast(a_rep.exp)) < @as(i16, @bitCast(b_rep.exp))) { 85 return .Less; 86 } else { 87 return .Greater; 88 } 89 } else { 90 const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits); 91 const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits); 92 if ((a_fraction < b_fraction) == (a_rep.exp & sign_bit == 0)) { 93 return .Less; 94 } else { 95 return .Greater; 96 } 97 } 98 } 99 100 test "cmp_f80" { 101 inline for (.{ LE, GE }) |RT| { 102 try std.testing.expect(cmp_f80(RT, 1.0, 1.0) == RT.Equal); 103 try std.testing.expect(cmp_f80(RT, 0.0, -0.0) == RT.Equal); 104 try std.testing.expect(cmp_f80(RT, 2.0, 4.0) == RT.Less); 105 try std.testing.expect(cmp_f80(RT, 2.0, -4.0) == RT.Greater); 106 try std.testing.expect(cmp_f80(RT, -2.0, -4.0) == RT.Greater); 107 try std.testing.expect(cmp_f80(RT, -2.0, 4.0) == RT.Less); 108 } 109 } 110 111 pub inline fn unordcmp(comptime T: type, a: T, b: T) i32 { 112 const rep_t = std.meta.Int(.unsigned, @typeInfo(T).float.bits); 113 114 const significandBits = std.math.floatMantissaBits(T); 115 const exponentBits = std.math.floatExponentBits(T); 116 const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); 117 const absMask = signBit - 1; 118 const infRep = @as(rep_t, @bitCast(std.math.inf(T))); 119 120 const aAbs: rep_t = @as(rep_t, @bitCast(a)) & absMask; 121 const bAbs: rep_t = @as(rep_t, @bitCast(b)) & absMask; 122 123 return @intFromBool(aAbs > infRep or bAbs > infRep); 124 } 125 126 test { 127 _ = @import("comparesf2_test.zig"); 128 _ = @import("comparedf2_test.zig"); 129 }