Negate u32 which is i32::MAX+1

I had to do ASCII to i32 parsing on an embedded target, and I build up the value in a u32 which has enough range to cover 0..i32::MAX+1.

However, when I try to stuff that back into an i32, the simple "-(value as i32)" works EXCEPT if the value is i32::MAX+1 which is a valid negative number (abs(i32::MIN) == abs(i32::MAX+1)).

What silly stupid thing am I missing to do this negation? Do I really have to cast to an i64 just to negate this because of one single value? That seems very silly.

Thanks.




fn main() {
    let a: u32 = i32::MAX as u32;
    let b: u32 = a + 1;

    println!("a:{} b:{}", a, b);

    let c: i32 = -(a as i32);
    println!("c: {}", c);
    
    let d: i32 = -(b as i32);
    println!("d: {}", d);

    println!("OK");
}

(Playground)

Output:

a:2147483647 b:2147483648
c: -2147483647

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 3.38s
     Running `target/debug/playground`
thread 'main' panicked at 'attempt to negate with overflow', src/main.rs:13:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Unfortunately yes, because Rust panics on integer overflow. This was precisely the issue pointed out by Linus Torvalds when he was considering making Rust a language parts of the Linux kernel could be written in.

You can use wrapping_neg():

fn main()
{
    let a: u32 = i32::MAX as u32;
    let b: u32 = a + 1;
    println!("{}", a as i32);
    println!("{}", b as i32);
    println!("{}", (a as i32).wrapping_neg());
    println!("{}", (b as i32).wrapping_neg());
}

gives

2147483647
-2147483648
-2147483647
-2147483648
7 Likes

I think Linus may have been talking about allocation panics not overflow panics.
https://lkml.org/lkml/2021/4/14/1099

3 Likes

Yes you're right. My bad. :sweat_smile:

You have a valid u32 which cannot fit in i32. What do you want to happen when you do the conversion?

2 Likes

The negation of the u32 does fit in i32. @gvissers's answer is of course correct. Another way to do this is:

a.wrapping_neg() as i32

You can avoid this corner case by taking advantage of the fact integers in Rust use twos complement:

fn wrapping_negation(n: i32) -> i32 {
    (!n).wrapping_add(1)
}

All numbers are negated except i32::MIN, which maps to itself.

Edit: Of course zero also maps to itself.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.