// ARITHMETIC · WHAT'S UNDERNEATH //
When 255 + 1 = 0
INT_MAX + 1 rolls over to a huge negative number. A byte in Java runs −128 to 127, not 0 to 255. Math.abs(Integer.MIN_VALUE) comes back negative. These look like language quirks. They're the same eight bits the 6502 in your Commodore 64 has always pushed around — just out in the open.
It wraps because there’s nowhere to carry
The 6502’s accumulator is eight bits wide. Add past 255 and the ninth bit has nowhere to land but the carry flag:
lda #$ff ; a = 255
clc
adc #$01 ; a = $00 — and the carry flag is now setThat’s INT_MAX + 1 in miniature — the exact same event, just in a wider register.
The bits don’t know if they’re signed
Here’s the part that catches people. The byte $80 is 128. It is also −128. The 6502 stores neither reading — only the bits. You decide which you meant by which flag you read afterwards:
lda #$7f ; +127
clc
adc #$01 ; a = $80
; unsigned -> 128 (carry clear, all fine)
; signed -> -128 (overflow! the V flag is set)This is why −1 is stored as $FF, why casting a byte to an int can ambush you, and why (unsigned)a < b compiles to a different branch than a < b. The comparison is the same subtraction underneath — only the flag you branch on changes: BCC for unsigned, a mix of N and V for signed.
Which is why abs(MIN) breaks
A signed byte holds −128 to +127. There is no +128 in it. So abs(−128) has no answer that fits — and comes back −128, the one value that is its own negative.
Math.abs(Integer.MIN_VALUE)is that exact corner, 24 bits wider. None of it is a bug in the language. It’s two’s complement — and the 6502 will show you the whole thing in three instructions.