noobie
August 9, 2026, 6:40am
1
Hi, I’m new to Zig and trying to understand this.
This version crashes:
const std = @import("std");
pub fn main() void {
const num = -2; // -4, -6, ...
std.debug.print("{}\n", .{@mod(num, 2)});
switch (@mod(num, 2)) {
0 => std.debug.print("Even\n", .{}),
1 => std.debug.print("Odd\n", .{}),
else => unreachable,
}
}
Output:
0
thread panic: reached unreachable code
What I’m seeing:
Negative even const values → prints 0 but then crashes
Negative odd values (like -11 ) → works (Odd )
All non-negative values I tested → work fine
If I change const to var , it works:
const std = @import("std");
pub fn main() void {
var num: i32 = -2;
_ = #
std.debug.print("{}\n", .{@mod(num, 2)});
switch (@mod(num, 2)) {
0 => std.debug.print("Even\n", .{}),
1 => std.debug.print("Odd\n", .{}),
else => unreachable,
}
}
Output:
0
Even
Zig version: 0.16.0
Am I misunderstanding something about @mod or switch with const , or is this unexpected behavior?
noobie
August 9, 2026, 6:58am
2
Small update: this isn’t just % 2.
I’m seeing the same crash for other divisors when the value is a negative multiple.
Example:
const std = @import("std");
pub fn main() void {
const num = -6;
std.debug.print("{}\n", .{@mod(num, 3)});
switch (@mod(num, 3)) {
0 => std.debug.print("0\n", .{}),
1 => std.debug.print("1\n", .{}),
2 => std.debug.print("2\n", .{}),
else => unreachable,
}
}
Output:
0
thread panic: reached unreachable code
So pattern seems to be:
negative multiples of the divisor (like -6 % 3, -4 % 2) → crash
others work fine
switching const → var makes it work
Does this help narrow it down?
noobie
August 9, 2026, 9:07am
4
tried something along those lines, but this seems to contradict the “negative 0” idea.
const std = @import("std");
pub fn main() void {
const num = -2;
std.debug.print("{}\n", .{@mod(num, 2)});
switch (@mod(num, 2)) {
0...1 => std.debug.print("fine\n", .{}),
else => unreachable,
}
}
Output:
0
fine
Here it still prints 0 , but the switch works fine when I use a range (0...1 ).
So it doesn’t look like the value itself is different (like a “negative 0”), but something about how the exact 0 case is handled?
rpkak
August 9, 2026, 9:19am
5
Without the PR I linked, the compiler saves positive zero and negative zero as two different internal values. Mostly these two values act the same way, but when using a switch prong which is not a range, the compiler uses this compare function, if the switch condition is comptime known. This function checks if the compiler internal values are the same.
Because both values represent 0, they act in the same way when you print them.
When using a range (and the condition is comptime known), the compiler uses the bigint order function to check if the switch condition is in the range. This already handles the -0 case.
3 Likes