Overflow of `uintN`?
fn main() {
a := uint16(0x0)
b := uint16(0x1)
r := a-b
printf("Hello, World! %d\n", r)
}
As far as I tested, it only happens on uintN's (not uint) ~~and only happens on subtraction.~~ nevermind, sorry.
@ske2004 Should it not happen? -1 doesn't fit into uint16. And it happens with addition as well:
fn main() {
a := uint16(0xFFFF)
b := uint16(0x1)
r := a+b
printf("Hello, World! %d\n", r) // Overflow of uint16
}
Oh, I'm not sure why I didn't see it. I remember testing yesterday and it only happened on subtraction. I wasn't careful then.
@vtereshkov It doesn't happen when you do it inline:
fn main() {
a := uint16(0xFFFF)
b := uint16(0x1)
printf("Hello, World! %d\n", a+b)
}
I think there should be a way to do operations or cast without overflow checks. I have a (somewhat niche) use case where it's inconvenient without it:
fn subtractWithCarry(a: uint8, b: uint8): bool {
// promote to next highest bit length
r := uint16(a)-uint16(b)
return r&0x100 > 0
}
This works if you use uint instead, but why?
fn subtractWithCarry(a: uint8, b: uint8): bool {
// promote to next highest bit length
r := uint(uint16(a)-uint16(b))
return r&0x100 > 0
}
@ske2004 In your subtractWithCarry(), what is the preferred value and type of r for you?
@vtereshkov No preference actually! I operate on uint16's though, no particular reason.
@ske2004 In your example with 0 and 1, do you want r to be 65535 of type uint16?
(Well, the reason was that I operate on uint8's, and I chose uint16 as the next logical unit, but I could as well choose uint. I didn't have this issue in mind when I wrote that code)
Yet another proposal from those familiar with Zig and C#: a - b is a runtime error, unchecked(a - b) wraps around to 65535. Even if I like it, I don't know how to implement it easily.
@vtereshkov unchecked wrapper sounds like a nightmare. This would add an additional check if it wraps code at runtime. Zig's custom operator approach is better because it's operation local. Right now I'm thinking of sort of unchecked[T] type instead.
So unchecked[int8](uint8Val) casts to int8 without checks and this can be realized at compile time unlike checking if we're in some unchecked context at run time.
var a, b: unchecked[uint8] = 128, 200; r := a + b here r should be unchecked[uint8] and so on.
What should happen when you add unchecked type to a checked one, should it be checked or not? For now I'd rather it be a compile error, I think only experience can tell.
From the above, converting unchecked types to checked will need explicit conversion.