umka-lang icon indicating copy to clipboard operation
umka-lang copied to clipboard

Overflow of `uintN`?

Open ske2004 opened this issue 1 month ago • 9 comments

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 avatar Nov 09 '25 01:11 ske2004

@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
}

vtereshkov avatar Nov 09 '25 08:11 vtereshkov

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 avatar Nov 09 '25 21:11 ske2004

@ske2004 In your subtractWithCarry(), what is the preferred value and type of r for you?

vtereshkov avatar Nov 09 '25 22:11 vtereshkov

@vtereshkov No preference actually! I operate on uint16's though, no particular reason.

ske2004 avatar Nov 09 '25 22:11 ske2004

@ske2004 In your example with 0 and 1, do you want r to be 65535 of type uint16?

vtereshkov avatar Nov 09 '25 22:11 vtereshkov

(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)

ske2004 avatar Nov 09 '25 22:11 ske2004

@ske2004 In your example with 0 and 1, do you want r to be 65535 of type uint16?

That's right

ske2004 avatar Nov 09 '25 22:11 ske2004

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 avatar Nov 28 '25 14:11 vtereshkov

@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.

ske2004 avatar Nov 28 '25 22:11 ske2004