Zig DISCUSSION

Zig panics with integer overflow on a u8 counter: how do I get wrapping or saturating arithmetic?

Started by sa2016 Zig integer overflowwrapping arithmeticsaturating arithmeticruntime safetyembedded counters
4 replies 248 views 5 participants
Latest activity · 30 Sep 2026

Zig panics with integer overflow on a u8 counter: how do I get wrapping or saturating arithmetic?

sa2016 Zig Forum
#1

I am porting firmware from C in which a uint8_t sequence number simply rolls over from 255 to 0, and a 16-bit timer value is subtracted to get elapsed ticks. In Zig, seq += 1; on a u8 holding 255 stops the Debug build with a panic about integer overflow, and the same happens with now - start when the timer has wrapped.

Why is this an error for unsigned types when C defines it, what happens in release builds, and what is the correct way to write arithmetic that is supposed to wrap?

Community replies 4

Re: Zig panics with integer overflow on a u8 counter: how do I get wrapping or saturating arithmetic?

#2

In Zig the ordinary operators +, - and * state that overflow is not expected, for signed and unsigned types alike. Overflow is then illegal behaviour: Debug and ReleaseSafe builds insert a check and panic, while ReleaseFast and ReleaseSmall remove the check and the result is undefined, so you cannot count on it wrapping there either.

When you want modular arithmetic you say so with the wrapping operators +%, -% and *%: seq +%= 1; takes 255 to 0 in every build mode.

Re: Zig panics with integer overflow on a u8 counter: how do I get wrapping or saturating arithmetic?

#3

The timer case works out exactly as in C once you use -%. With a u16 tick counter, start = 65530 and now = 4, now -% start is 4 - 65530 modulo 65536, which is 10 ticks.

The result is correct as long as the real interval is shorter than one full counter period and both operands have the same unsigned width as the hardware counter. If the counter is 16 bits, do the subtraction in u16 before widening the result, not after.

Re: Zig panics with integer overflow on a u8 counter: how do I get wrapping or saturating arithmetic?

#4

There are two more families. Saturating operators clamp at the limits: +| on a u8 gives 250 + 10 = 255, useful for things like a PWM duty value or a signal level where wrapping would be wrong.

For values coming from outside, where overflow is a real error to handle, std.math.add(u8, a, b) returns error.Overflow instead of panicking, and the builtin @addWithOverflow gives you the wrapped result together with an overflow bit. The builtin's exact form has changed between releases, so check the language reference for your compiler version.

Re: Zig panics with integer overflow on a u8 counter: how do I get wrapping or saturating arithmetic?

#5

Often the better fix is a wider type rather than wrapping. If two u8 samples are averaged, (a + b) / 2 overflows for 200 and 100, because the sum 300 is computed in 8 bits. Widen one operand first: (@as(u16, a) + b) / 2 gives 150, and the result can be narrowed again with @intCast, which is itself checked in safe builds.

Values known at compile time are checked by the compiler: const x: u8 = 200 + 100; is rejected outright instead of failing at run time.

TEP COMMUNITY