Skip to content

lang::wrapping_arithmetic

Wrapping arithmetic operators +%, -%, *% and their compound forms +%=, -%=, *%=: two's-complement wrapping at the operands' declared integer width, on every tier and in every build profile. Plain +, -, and * keep their overflow check, and wrapping_add / wrapping_mul are not methods (GT0087).

The operators

a +% b, a -% b, and a *% b add, subtract, and multiply two values of one integer type with two's-complement wrapping at that type's declared width. The answer is the same on the bytecode VM, the JIT, and a native build, in a debug and a release profile alike. +%=, -%=, and *%= are the compound forms.

fn djb2(text: String) -> u32 {
    let mut hash: u32 = 5381
    for b in text.bytes() {
        hash = (hash << 5) +% hash +% b as u32
    }
    hash
}

fn main() {
    let mut countdown: u8 = 2
    countdown -%= 5
    println("{} {}", djb2("hello"), countdown)  // 261238937 253
}
Expression Value
250 as u8 +% 10 4
0 as u8 -% 1 255
200 as u8 *% 2 144
-128 as i8 -% 1 127

Why a separate spelling

Plain +, -, and * keep their overflow check, so an arithmetic step that is meant to wrap - a hash, a checksum, a pseudo-random step, a ring index - says so where it is written, and means the same thing in every build profile.

Operand types and precedence

Both operands share one integer type: i8-i64, u8-u64, isize, or usize. A byte literal joins an integer operand as it does for +. A float or String operand reports GT0003. The operators bind like the ones they wrap: *% with * (level 5), and +% and -% with + and - (level 6). On the integer lanes of a lane vector they wrap lane by lane.

No wrapping methods

x.wrapping_add(y) and x.wrapping_mul(y) are not integer methods. A call reports GT0087, and gos check --fix rewrites it to x +% y or x *% y, including a chain of calls.

In the REPL, %info +% (and -%, *%, +%=, -%=, *%=) describes each operator with an example. See examples/wrapping_hash.gos for a complete program.