authorgravatar for shawn@git.icuShawn Landden <shawn@git.icu> 2019-11-04 18:46:33-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-16 01:41:26-05:00
log143603b39faa55f42fffa36d859b380bf1b13984
treee50103bb317fd86aff95ded9b9918c65c5aa82f4
parent2c7a2aefbfd0dbab190f912b4fbcbda96fb5ac44
signature Commit is signed but in an unrecognized format.

std: lessThan and greaterThan between signed and unsigned

It is a deviation from C, but I think we should consider making this the behavior of the operators. See #2133

1 files changed, 100 insertions(+), 0 deletions(-)

lib/std/math.zig+100
...@@ -943,3 +943,103 @@ test "math.mulWide" {...@@ -943,3 +943,103 @@ test "math.mulWide" {
943 testing.expect(mulWide(i8, 5, -5) == -25);943 testing.expect(mulWide(i8, 5, -5) == -25);
944 testing.expect(mulWide(u8, 100, 100) == 10000);944 testing.expect(mulWide(u8, 100, 100) == 10000);
945}945}
946
947// not to be confused with std.mem.Compare
948pub const CompareOperator = enum {
949 lessThan,
950 lessThanOrEqual,
951 equal,
952 greaterThan,
953 greaterThanOrEqual,
954};
955
956pub fn compare(a: var, comptime op: CompareOperator, b: var) bool {
957 const A = @typeOf(a);
958 const B = @typeOf(b);
959 if (@typeId(@typeOf(a)) != .Int or @typeId(@typeOf(b)) != .Int) @compileError("only integers supported");
960 if (A.is_signed == B.is_signed) {
961 return switch (op) {
962 .lessThan => a < b,
963 .lessThanOrEqual => a <= b,
964 .equal => a == b,
965 .greaterThan => a > b,
966 .greaterThanOrEqual => a >= b,
967 };
968 }
969 const Signed = if (A.is_signed) A else B;
970 const Unsigned = if (B.is_signed) A else B;
971 const signedMoreBits = Signed.bit_count > Unsigned.bit_count;
972 const bits = if (signedMoreBits) Signed.bit_count else Unsigned.bit_count;
973 if (signedMoreBits) {
974 const T = @IntType(true, bits);
975 if (A.is_signed) {
976 return switch (op) {
977 .lessThan => a < @intCast(T, b),
978 .lessThanOrEqual => a <= @intCast(T, b),
979 .equal => a == @intCast(T, b),
980 .greaterThan => a > @intCast(T, b),
981 .greaterThanOrEqual => a >= @intCast(T, b),
982 };
983 } else {
984 return switch (op) {
985 .lessThan => @intCast(T, a) < b,
986 .lessThanOrEqual => @intCast(T, a) <= b,
987 .equal => @intCast(T, a) == b,
988 .greaterThan => @intCast(T, a) > b,
989 .greaterThanOrEqual => @intCast(T, a) >= b,
990 };
991 }
992 }
993 if (A.is_signed) {
994 const U = @IntType(false, A.bit_count);
995 const T = @IntType(false, A.bit_count - 1);
996 switch (op) {
997 .lessThan => if (a < 0 or maxInt(A) < b) return true,
998 .lessThanOrEqual => if (a <= 0 or maxInt(A) <= b) return true,
999 .equal => if (A.bit_count <= B.bit_count and a < 0) return false,
1000 else => {},
1001 }
1002 return switch (op) {
1003 .lessThan => @truncate(T, @bitCast(U, a)) < b,
1004 .lessThanOrEqual => @truncate(T, @bitCast(U, a)) <= b,
1005 .equal => @truncate(T, @bitCast(U, a)) == b,
1006 .greaterThan => @truncate(T, @bitCast(U, a)) > b,
1007 .greaterThanOrEqual => @truncate(T, @bitCast(U, a)) >= b,
1008 };
1009 } else {
1010 const U = @IntType(false, B.bit_count);
1011 const T = @IntType(false, B.bit_count - 1);
1012 switch (op) {
1013 .greaterThan => if (0 > b or a > maxInt(B)) return true,
1014 .greaterThanOrEqual => if (0 >= b or a >= maxInt(B)) return true,
1015 .equal => if (A.bit_count >= B.bit_count and 0 > b) return false,
1016 else => {},
1017 }
1018 return switch (op) {
1019 .lessThan => a < @truncate(T, @bitCast(U, b)),
1020 .lessThanOrEqual => a <= @truncate(T, @bitCast(U, b)),
1021 .equal => a == @truncate(T, @bitCast(U, b)),
1022 .greaterThan => a > @truncate(T, @bitCast(U, b)),
1023 .greaterThanOrEqual => a >= @truncate(T, @bitCast(U, b)),
1024 };
1025 }
1026}
1027
1028test "math.lessThan, et al < <= > >= between signed and unsigned" {
1029 testing.expect(compare(i8(-1), .lessThan, u8(255)));
1030 testing.expect(!compare(i8(-1), .greaterThanOrEqual, u8(255)));
1031 testing.expect(compare(u8(255), .greaterThan, i8(-1)));
1032 testing.expect(!compare(u8(255), .lessThanOrEqual, i8(-1)));
1033 testing.expect(compare(i8(-1), .lessThan, u9(255)));
1034 testing.expect(!compare(i8(-1), .greaterThanOrEqual, u9(255)));
1035 testing.expect(compare(u9(255), .greaterThan, i8(-1)));
1036 testing.expect(!compare(u9(255), .lessThanOrEqual, i8(-1)));
1037 testing.expect(compare(i9(-1), .lessThan, u8(255)));
1038 testing.expect(!compare(i9(-1), .greaterThanOrEqual, u8(255)));
1039 testing.expect(compare(u8(255), .greaterThan, i9(-1)));
1040 testing.expect(!compare(u8(255), .lessThanOrEqual, i9(-1)));
1041 testing.expect(compare(u8(1), .lessThan, u8(2)));
1042 testing.expect(@bitCast(u8, i8(-1)) == u8(255));
1043 testing.expect(!compare(u8(255), .equal, i8(-1)));
1044 testing.expect(compare(u8(1), .equal, u8(1)));
1045}