Numeric Overflow

Oftentimes, programming languages assign sizes to certain numeric types: in C, the 8-bit unsigned integer is called uint8_t, and can represent values between 0 and 255. If something larger than 255 is assigned to a variable of this type, it rolls over, the result being the modulus. So if we assigned 260 to a uint8_t, we would actually get 5 instead. If we were to do this to an int8_t (the 8-bit signed integer that represents values from -127 to 127), the results are undefined; a C compiler has license to do whatever it likes.

This has a couple of unfortunate effects. The first is you constantly need to estimate what values may be stored in your variables, which generally causes most programmers to use int everywhere. The second is when numbers behave unexpectedly (roll over or explode), they can cause program misbehavior, which often leads to a crash or a security issue.

Ki has two number types that are not like this: int and uint. They are intended to be the default integer types. Instead of overflowing, they just keep growing (either positively or negatively). The downside is that they become about half as fast as a regular integer once they exceed the bit-width of the host machine.

The other integer types (s8, s16, s32, s64, u8, u16, u32 and u64) don't do this, rather they overflow by default. Signed overflow is well-defined in Ki; it doesn't share that problem with C.

You can change the overflow behavior of both int and the sized integer types at a program level or an expression level. Here are your options:

  • @overflow.: calls the function x with the pre-overflow value
  • @overflowfn: used to define an overflow handling function instead of fn.
  • @set_overflow_handler : sets the default overflow handler to the function x.
  • @overflow.wrap: wraparound
  • @overflow.limit: floor/ceiling
  • @overflow.bignum: convert to bignum (probably default policy)
  • @overflow.error: runtime error
  • @overflow(, ): custom function