| author | |
| committer | |
| log | 3db130ff3d8175adce610f7805a149810cf7989d |
| tree | e9e330f1efdd587ef45be88b6f175447c32ad82f |
| parent | e382e7be2b7a4c40a8db78dca0de38141dad8c67 |
| signature |
- approach by Hacker's Delight with wrapping addition
- ca. 1.10x perf over the standard approach on my laptop
- tests with all combinations of min,max with -1,0,+1 and combinations of
sequences +-1,2,4..,max6 files changed, 277 insertions(+), 1 deletions(-)
lib/std/special/compiler_rt.zig+6| ... | @@ -106,6 +106,12 @@ comptime { | ... | @@ -106,6 +106,12 @@ comptime { |
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | // Integral arithmetic which returns if overflow | 108 | // Integral arithmetic which returns if overflow |
| 109 | const __addosi4 = @import("compiler_rt/addo.zig").__addosi4; | ||
| 110 | @export(__addosi4, .{ .name = "__addosi4", .linkage = linkage }); | ||
| 111 | const __addodi4 = @import("compiler_rt/addo.zig").__addodi4; | ||
| 112 | @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage }); | ||
| 113 | const __addoti4 = @import("compiler_rt/addo.zig").__addoti4; | ||
| 114 | @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage }); | ||
| 109 | const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; | 115 | const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; |
| 110 | @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); | 116 | @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); |
| 111 | const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; | 117 | const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; |
lib/std/special/compiler_rt/addo.zig created+38| ... | @@ -0,0 +1,38 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | |||
| 3 | // addo - add overflow | ||
| 4 | // * return a+%b. | ||
| 5 | // * return if a+b overflows => 1 else => 0 | ||
| 6 | // - addoXi4_generic as default | ||
| 7 | |||
| 8 | inline fn addoXi4_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 addition. | ||
| 14 | // Overflow in a+b+carry occurs, iff a and b have opposite signs | ||
| 15 | // and the sign of a+b+carry is the same as a (or equivalently b). | ||
| 16 | // Slower routine: res = ~(a ^ b) & ((sum ^ a) | ||
| 17 | // Faster routine: res = (sum ^ a) & (sum ^ b) | ||
| 18 | // Oerflow occured, iff (res < 0) | ||
| 19 | if (((sum ^ a) & (sum ^ b)) < 0) | ||
| 20 | overflow.* = 1; | ||
| 21 | return sum; | ||
| 22 | } | ||
| 23 | |||
| 24 | pub fn __addosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { | ||
| 25 | return addoXi4_generic(i32, a, b, overflow); | ||
| 26 | } | ||
| 27 | pub fn __addodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { | ||
| 28 | return addoXi4_generic(i64, a, b, overflow); | ||
| 29 | } | ||
| 30 | pub fn __addoti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { | ||
| 31 | return addoXi4_generic(i128, a, b, overflow); | ||
| 32 | } | ||
| 33 | |||
| 34 | test { | ||
| 35 | _ = @import("addosi4_test.zig"); | ||
| 36 | _ = @import("addodi4_test.zig"); | ||
| 37 | _ = @import("addoti4_test.zig"); | ||
| 38 | } | ||
lib/std/special/compiler_rt/addodi4_test.zig created+77| ... | @@ -0,0 +1,77 @@ | ||
| 1 | const addv = @import("addo.zig"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const testing = std.testing; | ||
| 4 | const math = std.math; | ||
| 5 | |||
| 6 | fn test__addodi4(a: i64, b: i64) !void { | ||
| 7 | var result_ov: c_int = undefined; | ||
| 8 | var expected_ov: c_int = undefined; | ||
| 9 | var result = addv.__addodi4(a, b, &result_ov); | ||
| 10 | var expected: i64 = simple_addodi4(a, b, &expected_ov); | ||
| 11 | try testing.expectEqual(expected, result); | ||
| 12 | try testing.expectEqual(expected_ov, result_ov); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn simple_addodi4(a: i64, b: i64, overflow: *c_int) i64 { | ||
| 16 | overflow.* = 0; | ||
| 17 | const min: i64 = math.minInt(i64); | ||
| 18 | const max: i64 = math.maxInt(i64); | ||
| 19 | if (((a > 0) and (b > max - a)) or | ||
| 20 | ((a < 0) and (b < min - a))) | ||
| 21 | overflow.* = 1; | ||
| 22 | return a +% b; | ||
| 23 | } | ||
| 24 | |||
| 25 | test "addodi4" { | ||
| 26 | const min: i64 = math.minInt(i64); | ||
| 27 | const max: i64 = math.maxInt(i64); | ||
| 28 | var i: i64 = 1; | ||
| 29 | while (i < max) : (i *|= 2) { | ||
| 30 | try test__addodi4(i, i); | ||
| 31 | try test__addodi4(-i, -i); | ||
| 32 | try test__addodi4(i, -i); | ||
| 33 | try test__addodi4(-i, i); | ||
| 34 | } | ||
| 35 | |||
| 36 | // edge cases | ||
| 37 | // 0 + 0 = 0 | ||
| 38 | // MIN + MIN overflow | ||
| 39 | // MAX + MAX overflow | ||
| 40 | // 0 + MIN MIN | ||
| 41 | // 0 + MAX MAX | ||
| 42 | // MIN + 0 MIN | ||
| 43 | // MAX + 0 MAX | ||
| 44 | // MIN + MAX -1 | ||
| 45 | // MAX + MIN -1 | ||
| 46 | try test__addodi4(0, 0); | ||
| 47 | try test__addodi4(min, min); | ||
| 48 | try test__addodi4(max, max); | ||
| 49 | try test__addodi4(0, min); | ||
| 50 | try test__addodi4(0, max); | ||
| 51 | try test__addodi4(min, 0); | ||
| 52 | try test__addodi4(max, 0); | ||
| 53 | try test__addodi4(min, max); | ||
| 54 | try test__addodi4(max, min); | ||
| 55 | |||
| 56 | // derived edge cases | ||
| 57 | // MIN+1 + MIN overflow | ||
| 58 | // MAX-1 + MAX overflow | ||
| 59 | // 1 + MIN = MIN+1 | ||
| 60 | // -1 + MIN overflow | ||
| 61 | // -1 + MAX = MAX-1 | ||
| 62 | // +1 + MAX overflow | ||
| 63 | // MIN + 1 = MIN+1 | ||
| 64 | // MIN + -1 overflow | ||
| 65 | // MAX + 1 overflow | ||
| 66 | // MAX + -1 = MAX-1 | ||
| 67 | try test__addodi4(min + 1, min); | ||
| 68 | try test__addodi4(max - 1, max); | ||
| 69 | try test__addodi4(1, min); | ||
| 70 | try test__addodi4(-1, min); | ||
| 71 | try test__addodi4(-1, max); | ||
| 72 | try test__addodi4(1, max); | ||
| 73 | try test__addodi4(min, 1); | ||
| 74 | try test__addodi4(min, -1); | ||
| 75 | try test__addodi4(max, -1); | ||
| 76 | try test__addodi4(max, 1); | ||
| 77 | } | ||
lib/std/special/compiler_rt/addosi4_test.zig created+78| ... | @@ -0,0 +1,78 @@ | ||
| 1 | const addv = @import("addo.zig"); | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__addosi4(a: i32, b: i32) !void { | ||
| 5 | var result_ov: c_int = undefined; | ||
| 6 | var expected_ov: c_int = undefined; | ||
| 7 | var result = addv.__addosi4(a, b, &result_ov); | ||
| 8 | var expected: i32 = simple_addosi4(a, b, &expected_ov); | ||
| 9 | try testing.expectEqual(expected, result); | ||
| 10 | try testing.expectEqual(expected_ov, result_ov); | ||
| 11 | } | ||
| 12 | |||
| 13 | fn simple_addosi4(a: i32, b: i32, overflow: *c_int) i32 { | ||
| 14 | overflow.* = 0; | ||
| 15 | const min: i32 = -2147483648; | ||
| 16 | const max: i32 = 2147483647; | ||
| 17 | if (((a > 0) and (b > max - a)) or | ||
| 18 | ((a < 0) and (b < min - a))) | ||
| 19 | overflow.* = 1; | ||
| 20 | return a +% b; | ||
| 21 | } | ||
| 22 | |||
| 23 | test "addosi4" { | ||
| 24 | // -2^31 <= i32 <= 2^31-1 | ||
| 25 | // 2^31 = 2147483648 | ||
| 26 | // 2^31-1 = 2147483647 | ||
| 27 | const min: i32 = -2147483648; | ||
| 28 | const max: i32 = 2147483647; | ||
| 29 | var i: i32 = 1; | ||
| 30 | while (i < max) : (i *|= 2) { | ||
| 31 | try test__addosi4(i, i); | ||
| 32 | try test__addosi4(-i, -i); | ||
| 33 | try test__addosi4(i, -i); | ||
| 34 | try test__addosi4(-i, i); | ||
| 35 | } | ||
| 36 | |||
| 37 | // edge cases | ||
| 38 | // 0 + 0 = 0 | ||
| 39 | // MIN + MIN overflow | ||
| 40 | // MAX + MAX overflow | ||
| 41 | // 0 + MIN MIN | ||
| 42 | // 0 + MAX MAX | ||
| 43 | // MIN + 0 MIN | ||
| 44 | // MAX + 0 MAX | ||
| 45 | // MIN + MAX -1 | ||
| 46 | // MAX + MIN -1 | ||
| 47 | try test__addosi4(0, 0); | ||
| 48 | try test__addosi4(min, min); | ||
| 49 | try test__addosi4(max, max); | ||
| 50 | try test__addosi4(0, min); | ||
| 51 | try test__addosi4(0, max); | ||
| 52 | try test__addosi4(min, 0); | ||
| 53 | try test__addosi4(max, 0); | ||
| 54 | try test__addosi4(min, max); | ||
| 55 | try test__addosi4(max, min); | ||
| 56 | |||
| 57 | // derived edge cases | ||
| 58 | // MIN+1 + MIN overflow | ||
| 59 | // MAX-1 + MAX overflow | ||
| 60 | // 1 + MIN = MIN+1 | ||
| 61 | // -1 + MIN overflow | ||
| 62 | // -1 + MAX = MAX-1 | ||
| 63 | // +1 + MAX overflow | ||
| 64 | // MIN + 1 = MIN+1 | ||
| 65 | // MIN + -1 overflow | ||
| 66 | // MAX + 1 overflow | ||
| 67 | // MAX + -1 = MAX-1 | ||
| 68 | try test__addosi4(min + 1, min); | ||
| 69 | try test__addosi4(max - 1, max); | ||
| 70 | try test__addosi4(1, min); | ||
| 71 | try test__addosi4(-1, min); | ||
| 72 | try test__addosi4(-1, max); | ||
| 73 | try test__addosi4(1, max); | ||
| 74 | try test__addosi4(min, 1); | ||
| 75 | try test__addosi4(min, -1); | ||
| 76 | try test__addosi4(max, -1); | ||
| 77 | try test__addosi4(max, 1); | ||
| 78 | } | ||
lib/std/special/compiler_rt/addoti4_test.zig created+77| ... | @@ -0,0 +1,77 @@ | ||
| 1 | const addv = @import("addo.zig"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const testing = std.testing; | ||
| 4 | const math = std.math; | ||
| 5 | |||
| 6 | fn test__addoti4(a: i128, b: i128) !void { | ||
| 7 | var result_ov: c_int = undefined; | ||
| 8 | var expected_ov: c_int = undefined; | ||
| 9 | var result = addv.__addoti4(a, b, &result_ov); | ||
| 10 | var expected: i128 = simple_addoti4(a, b, &expected_ov); | ||
| 11 | try testing.expectEqual(expected, result); | ||
| 12 | try testing.expectEqual(expected_ov, result_ov); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn simple_addoti4(a: i128, b: i128, overflow: *c_int) i128 { | ||
| 16 | overflow.* = 0; | ||
| 17 | const min: i128 = math.minInt(i128); | ||
| 18 | const max: i128 = math.maxInt(i128); | ||
| 19 | if (((a > 0) and (b > max - a)) or | ||
| 20 | ((a < 0) and (b < min - a))) | ||
| 21 | overflow.* = 1; | ||
| 22 | return a +% b; | ||
| 23 | } | ||
| 24 | |||
| 25 | test "addoti4" { | ||
| 26 | const min: i128 = math.minInt(i128); | ||
| 27 | const max: i128 = math.maxInt(i128); | ||
| 28 | var i: i128 = 1; | ||
| 29 | while (i < max) : (i *|= 2) { | ||
| 30 | try test__addoti4(i, i); | ||
| 31 | try test__addoti4(-i, -i); | ||
| 32 | try test__addoti4(i, -i); | ||
| 33 | try test__addoti4(-i, i); | ||
| 34 | } | ||
| 35 | |||
| 36 | // edge cases | ||
| 37 | // 0 + 0 = 0 | ||
| 38 | // MIN + MIN overflow | ||
| 39 | // MAX + MAX overflow | ||
| 40 | // 0 + MIN MIN | ||
| 41 | // 0 + MAX MAX | ||
| 42 | // MIN + 0 MIN | ||
| 43 | // MAX + 0 MAX | ||
| 44 | // MIN + MAX -1 | ||
| 45 | // MAX + MIN -1 | ||
| 46 | try test__addoti4(0, 0); | ||
| 47 | try test__addoti4(min, min); | ||
| 48 | try test__addoti4(max, max); | ||
| 49 | try test__addoti4(0, min); | ||
| 50 | try test__addoti4(0, max); | ||
| 51 | try test__addoti4(min, 0); | ||
| 52 | try test__addoti4(max, 0); | ||
| 53 | try test__addoti4(min, max); | ||
| 54 | try test__addoti4(max, min); | ||
| 55 | |||
| 56 | // derived edge cases | ||
| 57 | // MIN+1 + MIN overflow | ||
| 58 | // MAX-1 + MAX overflow | ||
| 59 | // 1 + MIN = MIN+1 | ||
| 60 | // -1 + MIN overflow | ||
| 61 | // -1 + MAX = MAX-1 | ||
| 62 | // +1 + MAX overflow | ||
| 63 | // MIN + 1 = MIN+1 | ||
| 64 | // MIN + -1 overflow | ||
| 65 | // MAX + 1 overflow | ||
| 66 | // MAX + -1 = MAX-1 | ||
| 67 | try test__addoti4(min + 1, min); | ||
| 68 | try test__addoti4(max - 1, max); | ||
| 69 | try test__addoti4(1, min); | ||
| 70 | try test__addoti4(-1, min); | ||
| 71 | try test__addoti4(-1, max); | ||
| 72 | try test__addoti4(1, max); | ||
| 73 | try test__addoti4(min, 1); | ||
| 74 | try test__addoti4(min, -1); | ||
| 75 | try test__addoti4(max, -1); | ||
| 76 | try test__addoti4(max, 1); | ||
| 77 | } | ||
lib/std/special/compiler_rt/mulo.zig+1-1| ... | @@ -3,7 +3,7 @@ const std = @import("std"); | ... | @@ -3,7 +3,7 @@ const std = @import("std"); |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | 4 | ||
| 5 | // mulo - multiplication overflow | 5 | // mulo - multiplication overflow |
| 6 | // * return a*b. | 6 | // * return a*%b. |
| 7 | // * return if a*b overflows => 1 else => 0 | 7 | // * return if a*b overflows => 1 else => 0 |
| 8 | // - muloXi4_genericSmall as default | 8 | // - muloXi4_genericSmall as default |
| 9 | // - muloXi4_genericFast for 2*bitsize <= usize | 9 | // - muloXi4_genericFast for 2*bitsize <= usize |