Signed and Unsigned Integers

In mathematics, if i is an integer then ((i + 1) > i) is always true, and similarly ((i - 1) < i). In computing, they're almost always true, but for fixed width integers (e.g. 32-bit integers), they can be false due to overflow or underflow.

For signed 32-bit integers, the discontinuity happens at roughly +2 billion and -2 billion. For unsigned 32-bit integers, it‘s at roughly +4 billion and at zero. In practice, many human-scale numbers (such as the number of employees that a person manages, a domain name’s string length or a photo‘s height in pixels) are small enough that ±2 billion won’t ever be reached, but zero is obviously common (some employees don't manage anyone). In other programming languages, using signed integers means that, in practice, one can conveniently forget about the discontinuities, even when adding or subtracting other human-scale numbers, and this almost always works fine.

Wuffs is concerned about always working, not just almost always working. Wuffs programs always have to consider the discontinuities, wherever they may lie, so there is less advantage to working with signed integer types, and less disadvantage to working with unsigned integer types. Unlike in C and similar programming languages, there is no way to forget that (i - 1) might be very large, when i is zero.

The unsigned types are arguably more natural - after all, they are part of what mathematics calls the natural numbers - and stronger inferences can be made from them. For example, barring overflow, (x <= (x + i)) is trivially true if i is non-negative. Similarly, when bounds checking a slice or array expression like a[i], proving (i >= 0) is trivial when i has unsigned integer type.

Wuffs programs therefore usually work with unsigned integer types: base.u32 instead of base.i32. In fact, Wuffs' standard library, as of version 0.2 (December 2019), uses only unsigned integer types, yet still implements full-featured decoders for e.g. the GIF and ZLIB formats.

Signed Integers in Other Languages

Conversely, using C‘s or Go’s or Java‘s (signed) int type can sometimes be imperfect. For example, some libraries use int for an image’s width or height in pixels. This is undeniably convenient, but might not be able to represent a 3 billion pixel high image. It is possible to craft a PNG image that high, one that is perfectly valid according to the PNG specification.

Similarly, even when using the ‘correct’ width (width as in fixed width integer, not as in image pixel width), the difference between signed and unsigned can matter. For example, as Java doesn't have unsigned integer types, care must be taken when implementing the LZ4 compression format to exactly match uint32_t modular arithmetic.