| author | |
| committer | |
| log | fc59a0406157dbd0704cf9f05cd04b6a8c87d7df |
| tree | 828950c75e372509e67f607a2588bd22c3b6a1fb |
| parent | a15d2d582b4015af0f72caa2b5f09b7e665e2c1e |
- approach by Hacker's Delight with wrapping subtraction
- performance expected to be similar to addo
- tests with all relevant combinations of min,max with -1,0,+1 and all
combinations of sequences +-1,2,4..,max6 files changed, 289 insertions(+), 1 deletions(-)
lib/std/special/compiler_rt.zig+6| ... | ... | @@ -112,6 +112,12 @@ comptime { |
| 112 | 112 | @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage }); |
| 113 | 113 | const __addoti4 = @import("compiler_rt/addo.zig").__addoti4; |
| 114 | 114 | @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage }); |
| 115 | const __subosi4 = @import("compiler_rt/subo.zig").__subosi4; | |
| 116 | @export(__subosi4, .{ .name = "__subosi4", .linkage = linkage }); | |
| 117 | const __subodi4 = @import("compiler_rt/subo.zig").__subodi4; | |
| 118 | @export(__subodi4, .{ .name = "__subodi4", .linkage = linkage }); | |
| 119 | const __suboti4 = @import("compiler_rt/subo.zig").__suboti4; | |
| 120 | @export(__suboti4, .{ .name = "__suboti4", .linkage = linkage }); | |
| 115 | 121 | const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; |
| 116 | 122 | @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); |
| 117 | 123 | const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; |
lib/std/special/compiler_rt/addo.zig+1-1| ... | ... | @@ -15,7 +15,7 @@ inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST |
| 15 | 15 | // and the sign of a+b+carry is the same as a (or equivalently b). |
| 16 | 16 | // Slower routine: res = ~(a ^ b) & ((sum ^ a) |
| 17 | 17 | // Faster routine: res = (sum ^ a) & (sum ^ b) |
| 18 | // Oerflow occured, iff (res < 0) | |
| 18 | // Overflow occured, iff (res < 0) | |
| 19 | 19 | if (((sum ^ a) & (sum ^ b)) < 0) |
| 20 | 20 | overflow.* = 1; |
| 21 | 21 | return sum; |
lib/std/special/compiler_rt/subo.zig created+38| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | ||
| 3 | // subo - subtract overflow | |
| 4 | // * return a-%b. | |
| 5 | // * return if a-b overflows => 1 else => 0 | |
| 6 | // - suboXi4_generic as default | |
| 7 | ||
| 8 | inline fn suboXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { | |
| 9 | @setRuntimeSafety(builtin.is_test); | |
| 10 | overflow.* = 0; | |
| 11 | var sum: ST = a -% b; | |
| 12 | // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract | |
| 13 | // Let sum = a -% b == a - b - carry == wraparound subtraction. | |
| 14 | // Overflow in a-b-carry occurs, iff a and b have opposite signs | |
| 15 | // and the sign of a-b-carry is opposite of a (or equivalently same as b). | |
| 16 | // Faster routine: res = (a ^ b) & (sum ^ a) | |
| 17 | // Slower routine: res = (sum^a) & ~(sum^b) | |
| 18 | // Overflow occured, iff (res < 0) | |
| 19 | if (((a ^ b) & (sum ^ a)) < 0) | |
| 20 | overflow.* = 1; | |
| 21 | return sum; | |
| 22 | } | |
| 23 | ||
| 24 | pub fn __subosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { | |
| 25 | return suboXi4_generic(i32, a, b, overflow); | |
| 26 | } | |
| 27 | pub fn __subodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { | |
| 28 | return suboXi4_generic(i64, a, b, overflow); | |
| 29 | } | |
| 30 | pub fn __suboti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { | |
| 31 | return suboXi4_generic(i128, a, b, overflow); | |
| 32 | } | |
| 33 | ||
| 34 | test { | |
| 35 | _ = @import("subosi4_test.zig"); | |
| 36 | _ = @import("subodi4_test.zig"); | |
| 37 | _ = @import("suboti4_test.zig"); | |
| 38 | } |
lib/std/special/compiler_rt/subodi4_test.zig created+81| ... | ... | @@ -0,0 +1,81 @@ |
| 1 | const subo = @import("subo.zig"); | |
| 2 | const std = @import("std"); | |
| 3 | const testing = std.testing; | |
| 4 | const math = std.math; | |
| 5 | ||
| 6 | fn test__subodi4(a: i64, b: i64) !void { | |
| 7 | var result_ov: c_int = undefined; | |
| 8 | var expected_ov: c_int = undefined; | |
| 9 | var result = subo.__subodi4(a, b, &result_ov); | |
| 10 | var expected: i64 = simple_subodi4(a, b, &expected_ov); | |
| 11 | try testing.expectEqual(expected, result); | |
| 12 | try testing.expectEqual(expected_ov, result_ov); | |
| 13 | } | |
| 14 | ||
| 15 | // 2 cases on evaluating `a-b`: | |
| 16 | // 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a<min+b | |
| 17 | // 2. `a-b` may overflow, iff b<0 && a>0 and a-b > max <=> a>max+b | |
| 18 | // `-b` evaluation may overflow, iff b==min, but this is handled by the hardware | |
| 19 | pub fn simple_subodi4(a: i64, b: i64, overflow: *c_int) i64 { | |
| 20 | overflow.* = 0; | |
| 21 | const min: i64 = math.minInt(i64); | |
| 22 | const max: i64 = math.maxInt(i64); | |
| 23 | if (((b > 0) and (a < min + b)) or | |
| 24 | ((b < 0) and (a > max + b))) | |
| 25 | overflow.* = 1; | |
| 26 | return a -% b; | |
| 27 | } | |
| 28 | ||
| 29 | test "subodi3" { | |
| 30 | const min: i64 = math.minInt(i64); | |
| 31 | const max: i64 = math.maxInt(i64); | |
| 32 | var i: i64 = 1; | |
| 33 | while (i < max) : (i *|= 2) { | |
| 34 | try test__subodi4(i, i); | |
| 35 | try test__subodi4(-i, -i); | |
| 36 | try test__subodi4(i, -i); | |
| 37 | try test__subodi4(-i, i); | |
| 38 | } | |
| 39 | ||
| 40 | // edge cases | |
| 41 | // 0 - 0 = 0 | |
| 42 | // MIN - MIN = 0 | |
| 43 | // MAX - MAX = 0 | |
| 44 | // 0 - MIN overflow | |
| 45 | // 0 - MAX = MIN+1 | |
| 46 | // MIN - 0 = MIN | |
| 47 | // MAX - 0 = MAX | |
| 48 | // MIN - MAX overflow | |
| 49 | // MAX - MIN overflow | |
| 50 | try test__subodi4(0, 0); | |
| 51 | try test__subodi4(min, min); | |
| 52 | try test__subodi4(max, max); | |
| 53 | try test__subodi4(0, min); | |
| 54 | try test__subodi4(0, max); | |
| 55 | try test__subodi4(min, 0); | |
| 56 | try test__subodi4(max, 0); | |
| 57 | try test__subodi4(min, max); | |
| 58 | try test__subodi4(max, min); | |
| 59 | ||
| 60 | // derived edge cases | |
| 61 | // MIN+1 - MIN = 1 | |
| 62 | // MAX-1 - MAX = -1 | |
| 63 | // 1 - MIN overflow | |
| 64 | // -1 - MIN = MAX | |
| 65 | // -1 - MAX = MIN | |
| 66 | // +1 - MAX = MIN+2 | |
| 67 | // MIN - 1 overflow | |
| 68 | // MIN - -1 = MIN+1 | |
| 69 | // MAX - 1 = MAX-1 | |
| 70 | // MAX - -1 overflow | |
| 71 | try test__subodi4(min + 1, min); | |
| 72 | try test__subodi4(max - 1, max); | |
| 73 | try test__subodi4(1, min); | |
| 74 | try test__subodi4(-1, min); | |
| 75 | try test__subodi4(-1, max); | |
| 76 | try test__subodi4(1, max); | |
| 77 | try test__subodi4(min, 1); | |
| 78 | try test__subodi4(min, -1); | |
| 79 | try test__subodi4(max, -1); | |
| 80 | try test__subodi4(max, 1); | |
| 81 | } |
lib/std/special/compiler_rt/subosi4_test.zig created+82| ... | ... | @@ -0,0 +1,82 @@ |
| 1 | const subo = @import("subo.zig"); | |
| 2 | const testing = @import("std").testing; | |
| 3 | ||
| 4 | fn test__subosi4(a: i32, b: i32) !void { | |
| 5 | var result_ov: c_int = undefined; | |
| 6 | var expected_ov: c_int = undefined; | |
| 7 | var result = subo.__subosi4(a, b, &result_ov); | |
| 8 | var expected: i32 = simple_subosi4(a, b, &expected_ov); | |
| 9 | try testing.expectEqual(expected, result); | |
| 10 | try testing.expectEqual(expected_ov, result_ov); | |
| 11 | } | |
| 12 | ||
| 13 | // 2 cases on evaluating `a-b`: | |
| 14 | // 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a<min+b | |
| 15 | // 2. `a-b` may overflow, iff b<0 && a>0 and a-b > max <=> a>max+b | |
| 16 | // `-b` evaluation may overflow, iff b==min, but this is handled by the hardware | |
| 17 | pub fn simple_subosi4(a: i32, b: i32, overflow: *c_int) i32 { | |
| 18 | overflow.* = 0; | |
| 19 | const min: i32 = -2147483648; | |
| 20 | const max: i32 = 2147483647; | |
| 21 | if (((b > 0) and (a < min + b)) or | |
| 22 | ((b < 0) and (a > max + b))) | |
| 23 | overflow.* = 1; | |
| 24 | return a -% b; | |
| 25 | } | |
| 26 | ||
| 27 | test "subosi3" { | |
| 28 | // -2^31 <= i32 <= 2^31-1 | |
| 29 | // 2^31 = 2147483648 | |
| 30 | // 2^31-1 = 2147483647 | |
| 31 | const min: i32 = -2147483648; | |
| 32 | const max: i32 = 2147483647; | |
| 33 | var i: i32 = 1; | |
| 34 | while (i < max) : (i *|= 2) { | |
| 35 | try test__subosi4(i, i); | |
| 36 | try test__subosi4(-i, -i); | |
| 37 | try test__subosi4(i, -i); | |
| 38 | try test__subosi4(-i, i); | |
| 39 | } | |
| 40 | ||
| 41 | // edge cases | |
| 42 | // 0 - 0 = 0 | |
| 43 | // MIN - MIN = 0 | |
| 44 | // MAX - MAX = 0 | |
| 45 | // 0 - MIN overflow | |
| 46 | // 0 - MAX = MIN+1 | |
| 47 | // MIN - 0 = MIN | |
| 48 | // MAX - 0 = MAX | |
| 49 | // MIN - MAX overflow | |
| 50 | // MAX - MIN overflow | |
| 51 | try test__subosi4(0, 0); | |
| 52 | try test__subosi4(min, min); | |
| 53 | try test__subosi4(max, max); | |
| 54 | try test__subosi4(0, min); | |
| 55 | try test__subosi4(0, max); | |
| 56 | try test__subosi4(min, 0); | |
| 57 | try test__subosi4(max, 0); | |
| 58 | try test__subosi4(min, max); | |
| 59 | try test__subosi4(max, min); | |
| 60 | ||
| 61 | // derived edge cases | |
| 62 | // MIN+1 - MIN = 1 | |
| 63 | // MAX-1 - MAX = -1 | |
| 64 | // 1 - MIN overflow | |
| 65 | // -1 - MIN = MAX | |
| 66 | // -1 - MAX = MIN | |
| 67 | // +1 - MAX = MIN+2 | |
| 68 | // MIN - 1 overflow | |
| 69 | // MIN - -1 = MIN+1 | |
| 70 | // MAX - 1 = MAX-1 | |
| 71 | // MAX - -1 overflow | |
| 72 | try test__subosi4(min + 1, min); | |
| 73 | try test__subosi4(max - 1, max); | |
| 74 | try test__subosi4(1, min); | |
| 75 | try test__subosi4(-1, min); | |
| 76 | try test__subosi4(-1, max); | |
| 77 | try test__subosi4(1, max); | |
| 78 | try test__subosi4(min, 1); | |
| 79 | try test__subosi4(min, -1); | |
| 80 | try test__subosi4(max, -1); | |
| 81 | try test__subosi4(max, 1); | |
| 82 | } |
lib/std/special/compiler_rt/suboti4_test.zig created+81| ... | ... | @@ -0,0 +1,81 @@ |
| 1 | const subo = @import("subo.zig"); | |
| 2 | const std = @import("std"); | |
| 3 | const testing = std.testing; | |
| 4 | const math = std.math; | |
| 5 | ||
| 6 | fn test__suboti4(a: i128, b: i128) !void { | |
| 7 | var result_ov: c_int = undefined; | |
| 8 | var expected_ov: c_int = undefined; | |
| 9 | var result = subo.__suboti4(a, b, &result_ov); | |
| 10 | var expected: i128 = simple_suboti4(a, b, &expected_ov); | |
| 11 | try testing.expectEqual(expected, result); | |
| 12 | try testing.expectEqual(expected_ov, result_ov); | |
| 13 | } | |
| 14 | ||
| 15 | // 2 cases on evaluating `a-b`: | |
| 16 | // 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a<min+b | |
| 17 | // 2. `a-b` may overflow, iff b<0 && a>0 and a-b > max <=> a>max+b | |
| 18 | // `-b` evaluation may overflow, iff b==min, but this is handled by the hardware | |
| 19 | pub fn simple_suboti4(a: i128, b: i128, overflow: *c_int) i128 { | |
| 20 | overflow.* = 0; | |
| 21 | const min: i128 = math.minInt(i128); | |
| 22 | const max: i128 = math.maxInt(i128); | |
| 23 | if (((b > 0) and (a < min + b)) or | |
| 24 | ((b < 0) and (a > max + b))) | |
| 25 | overflow.* = 1; | |
| 26 | return a -% b; | |
| 27 | } | |
| 28 | ||
| 29 | test "suboti3" { | |
| 30 | const min: i128 = math.minInt(i128); | |
| 31 | const max: i128 = math.maxInt(i128); | |
| 32 | var i: i128 = 1; | |
| 33 | while (i < max) : (i *|= 2) { | |
| 34 | try test__suboti4(i, i); | |
| 35 | try test__suboti4(-i, -i); | |
| 36 | try test__suboti4(i, -i); | |
| 37 | try test__suboti4(-i, i); | |
| 38 | } | |
| 39 | ||
| 40 | // edge cases | |
| 41 | // 0 - 0 = 0 | |
| 42 | // MIN - MIN = 0 | |
| 43 | // MAX - MAX = 0 | |
| 44 | // 0 - MIN overflow | |
| 45 | // 0 - MAX = MIN+1 | |
| 46 | // MIN - 0 = MIN | |
| 47 | // MAX - 0 = MAX | |
| 48 | // MIN - MAX overflow | |
| 49 | // MAX - MIN overflow | |
| 50 | try test__suboti4(0, 0); | |
| 51 | try test__suboti4(min, min); | |
| 52 | try test__suboti4(max, max); | |
| 53 | try test__suboti4(0, min); | |
| 54 | try test__suboti4(0, max); | |
| 55 | try test__suboti4(min, 0); | |
| 56 | try test__suboti4(max, 0); | |
| 57 | try test__suboti4(min, max); | |
| 58 | try test__suboti4(max, min); | |
| 59 | ||
| 60 | // derived edge cases | |
| 61 | // MIN+1 - MIN = 1 | |
| 62 | // MAX-1 - MAX = -1 | |
| 63 | // 1 - MIN overflow | |
| 64 | // -1 - MIN = MAX | |
| 65 | // -1 - MAX = MIN | |
| 66 | // +1 - MAX = MIN+2 | |
| 67 | // MIN - 1 overflow | |
| 68 | // MIN - -1 = MIN+1 | |
| 69 | // MAX - 1 = MAX-1 | |
| 70 | // MAX - -1 overflow | |
| 71 | try test__suboti4(min + 1, min); | |
| 72 | try test__suboti4(max - 1, max); | |
| 73 | try test__suboti4(1, min); | |
| 74 | try test__suboti4(-1, min); | |
| 75 | try test__suboti4(-1, max); | |
| 76 | try test__suboti4(1, max); | |
| 77 | try test__suboti4(min, 1); | |
| 78 | try test__suboti4(min, -1); | |
| 79 | try test__suboti4(max, -1); | |
| 80 | try test__suboti4(max, 1); | |
| 81 | } |