Intentionally overflow on shift?


#1

Doing x << y (where x: usize and y: u32) panics on debug builds if y is larger than the word size. I want the normal C behavior: this results in all 0s. However, it doesn’t look like any of the usize methods support this. The best I can come up with is:

let res = {
    let (res, overflow) = x.overflowing_shl(y);
    if overflow { 0 } else { res }
};

This will probably be decently fast in practice, but it’s pretty ugly (and probably not as fast as a single shift). Is there anything else I can do?


#2

x.checked_shl(y).unwrap_or(0)


#3

The C behavior is undefined (section 6.5.7 of the C99 standard, still there in C11), in part because common processors can’t decide what to do in this case (I believe x86 does the shift modulo-wordsize, where ARMv7 has the behavior you describe until the RHS crosses 255, and I think AArch64 changed this).

So you’re going to be paying some runtime cost on most machines to get what you want. The code @birkenfeld suggests is clean, and if you want to avoid boilerplating it, put it in a new Trait and impl it for usize.


#4

Huh fair enough. I’ve always been under the impression that it was well-defined (maybe in languages other than C?). Anyway, I’ll do that; thanks!


#5

Most languages I’ve seen that define it (such as .Net) take the shift amount modulo the size of the number, since that’s what x86 does. (Which is fast, if not useful…)


#6

Hmm. I feel like I’ve definitely written code that assumes that the overflowing shift is valid (and results in 0), but it’s possible that I just wrote code that used that assumption to justify its correctness in edge cases that, in practice, I never tested it with.


#7

No worries, you’re not the only one! I’ve found fairly serious bugs in shipping software because of this common misunderstanding.

Which is why I love Rust’s default on this. :wink: