literal out of range for i32
the literal 0xFFFF_FFFF (decimal 4294967295) does not fit into the type i32 and will become -1i32
consider using the type u32 instead #[deny(overflowing_literals)] on by defaultrustcClick for full compiler diagnostic
Im just trying to assign -1 to this i32, i gave it a 4 byte hex value to assign it to this 4 byte int, why is it giving an error? How is this an actual overflow?
Also this applies to masks
let mut a:i32 = 1;
a &= 0xFFFF_FFFF;
gives the same error even though im using it as a mask and not assigning it, so how can it even overflow?
Technically this is not an error but a lint that is set to deny (throw compile time error) by default. If you know what you are doing, you can allow the overflowing_literals lint to avoid the compile-time error:
#![allow(overflowing_literals)]
fn main() {
let a:i32 = 0xFFFF_FFFF;
}
To expand @Michael-F-Bryan point, you wouldn't write something like 0xFFFFFFFF for a signed integer as decimal (you'd write -1 instead or -0x0000_00001 in hex):
As someone who understands two's complement, there are definitely situations where I would write 0xFFFF_FFFF instead of -0x0000_0001. More frequently than I've used -0x.
The behavior of println!("{i:#x}"); (or X or b or o) is consistent with the sign being implicit in the literal; the documentation says "negative values are formatted as the two’s complement representation".
In fact, it seems there's no way to get the formatter to output -0x0000_0001 automatically, so you can't round-trip with the lint active. Perhaps a use case for the - flag.
(But if I was in a situation where I wanted to use 0xFFFF_FFFF, I'd just disable the lint / ignore signed overflows if available. The truncated p-adic two's complement representation has some nice qualities.)
to get AH value of the register, its easy in C (obviously its extremely prone to errors if you dont know what you are doing), i want to do the same thing in Rust
Does it makes sense to count it as overflow? I like to be let known if im actually overflowing (as in data being truncated), is there any other option besides disabling overflowing_literals for this case? (negative hex value assignment)
why does it matter if im applying a bit mask using an unsigned integer hex value to a signed integer? im not assigning it and thus overflowing, just comparing bits
Not to me with my background (knows 2s complement, would consider lack of such understanding a hole to be filled, considers use of hex to signal "bits are relevant and I know what they mean"), but I could entertain an argument the lint helps others. I think the sublint would be nice.
Registers are int32, they can hold negative values, if i were to use u32 and i want to do
EAX = 4;
EBX = 5;
EAX -= EBX;
this would throw an error because EAX wouldnt be able to hold negative values.
I would need to check if the second operand is bigger than the first and manually apply 2s complement which is really annoying, and there are a lot of cases different operations that can take positive and negative values so its gonna be a nightmare to implement.
Im extremely knew to Rust, could you please explain to me this line? let a:i32 = 0xFFFF_FFFF_u32 as _;
i would have tried (which is clearly not gonna work) let a:i32 = 0xFFFF_FFFF as u32;
what is _? what is it being cast to? why can you put u_32 at the end of a hex value? is it like -1i32?
Use .wrapping_sub and you'll be fine. (Or just make the register type num::Wrapping<u32>.)
They're twos-complement -- as you know from trying to use 0xFFFFFFFF for -1! -- so addition, subtraction, and multiplication do exactly the same thing for signed or unsigned numbers.
Then if you need something like division where signedness matters, you'll need to case the one register type to the other one to be able to implement the other instruction anyway.
0xFFFF_FFFF_u32 is the same as 0xFFFFFFFFu32, a literal of type u32. Omitting the type suffix will infer the (integer) type instead. In this case though, it infers i32 and triggers the lint. You can intersperse _ arbitrarily in the value for readability (but the type suffix, if present, must be complete).
In x86 (and every other architecture I know), a register is neither signed nor unsigned. The register just stores 32 bits and it's the instructions you execute which dictate whether those bits should be interpreted as signed or unsigned. Your VM will need to cater for both scenarios.
One way to do this would be to define your own register type with the correct size and alignment, and provide convenience functions for interpreting it as a certain signededness.