So here is the resulting function applyShift2
and some tests:
const std = @import("std");
// make it a function to make it easier to discuss alternative functions
fn applyShift(orig: usize, shift: isize) usize {
const s: isize = shift;
var u: usize = orig;
if (s < 0)
u -|= @intCast(-s)
else
u +|= @intCast(s);
return u;
}
fn applyShift2(orig: usize, shift: isize) usize {
const s: isize = shift;
var u: usize = orig;
if (s < 0)
u -|= @abs(s)
else
u +|= @intCast(s);
return u;
}
// like 2 but without the var
pub fn applyShift3(u: usize, s: isize) usize {
return if (s < 0) u -| @abs(s) else u +| @as(usize, @intCast(s));
}
const shiftFn = *const fn (usize, isize) usize;
test "overflow and underflow" {
const t = std.testing;
const utils = struct {
fn testIt(f: shiftFn) !void {
try t.expectEqual(@as(usize, 0), f(0, 0));
try t.expectEqual(@as(usize, @intCast(std.math.maxInt(isize))), f(0, std.math.maxInt(isize)));
try t.expectEqual(@as(usize, 0), f(0, std.math.minInt(isize)));
try t.expectEqual(@as(usize, std.math.maxInt(usize)), f(std.math.maxInt(usize), 0));
try t.expectEqual(@as(usize, std.math.maxInt(usize)), f(std.math.maxInt(usize), std.math.maxInt(isize)));
}
};
// try utils.testIt(applyShift); // integer overflow
try utils.testIt(applyShift2);
try utils.testIt(applyShift3);
}
I wonder if there is some other test that should be added, I currently can’t come up with more.
The applyShift3
is like 2 but using if expression to avoid the var and I quite like how it becomes a fairly readable oneliner.