Modulus¶
Level: 201 · for anyone with a hex editor open
One line: X[i] = X[i] % Operand keeps the remainder, and the sign of a remainder is a rule each language picks — the manual writes C, where it follows the value being divided — so Modulus 16 is the low hex digit of an Unsigned Byte and not of a Signed one, and −7 % 3 is −1 in C, 2 in Python and 2 in ABAP.
What the dialog does¶
The manual writes Modulus as X[i] = X[i] % Operand and glosses it as the remainder after dividing by the operand (Hex Operations ↗). It is Divide's other half: whatever rule chooses the quotient also chooses the remainder, because the two have to add back up to the value. C's quotient rounds toward zero, so C's remainder takes the sign of the value.
In Python¶
Verified output of modulus_py.py — regenerated by tools/run_examples.py, never hand-typed.
1. MODULUS 16 KEEPS THE LOW HEX DIGIT
------------------------------------------------------------------------
café is 63 61 66 c3 a9. Modulus 16, as Unsigned Byte:
03 01 06 03 09
Modulus 16 == Binary And 0F, all 256 Unsigned Bytes: True
Sixteen is a power of two, so the remainder is the low four bits,
which is the right-hand hex digit.
2. UNTIL THE BYTE IS SIGNED
------------------------------------------------------------------------
c3 a9 as Signed Byte is -61 and -87. Modulus 16 by each language's rule:
C's % f3 f9 -13, -7
Python's % 03 09 3, 9
The manual writes Modulus in C, so the first row is the rule it
names, and the low hex digit is no longer what comes out.
3. WHOSE SIGN THE REMAINDER TAKES
------------------------------------------------------------------------
a % b C Python
7 % 3 1 1
-7 % 3 -1 2
7 % -3 1 -2
-7 % -3 -1 -1
C's remainder has the sign of a, Python's the sign of b. Neither is
wrong: each is the one that fits its own division, over every pair
of Signed Bytes:
a == b * c_div(a, b) + c_mod(a, b) True
a == b * (a // b) + a % b True
4. MODULUS 0
------------------------------------------------------------------------
Modulus 0 on 10 ZeroDivisionError
A remainder after dividing by zero is undefined in C as well, and
the manual does not say what the dialog writes.
Modulus 16 is the low hex digit, when the byte is unsigned¶
Sixteen is a power of two, so the remainder after dividing by it is the four bits a division would shift away: the right-hand hex digit. Section 1 takes café to 03 01 06 03 09 and checks that Modulus 16 matches Binary And 0F on all 256 Unsigned Bytes. That equivalence is what makes a Modulus by a power of two useful in a hex editor, and section 2 shows where it stops. As a Signed Byte, C3 is −61; C's −61 % 16 is −13, written F3, and Python's is 3, written 03. The low hex digit comes out only under Python's rule, and Python's is not the rule the manual names.
Whose sign the remainder takes¶
Section 3 is four divisions in two languages. C's remainder has the sign of the value divided and Python's the sign of the divisor, so they agree when the two signs agree and differ when they do not. Neither is a mistake: each is the only remainder that fits its own language's quotient, and the program checks both identities over every pair of Signed Bytes. The trap is porting half of the pair — a C % beside a Python //, or the reverse — and getting a remainder that no longer adds back up to the value.
What the manual does not say¶
- What Modulus writes for an operand of 0. C leaves it undefined; section 4 is Python refusing.
- Whether the dialog offers Modulus for Float and Double, where C has no
%operator at all.
If you are coming from Python or ABAP¶
Python. % and // are a matched pair that follow the divisor's sign, and math.fmod() follows the dividend's, as C does: math.fmod(-7, 3) is −1.0. For C's rule on integers of any size, take the remainder from a C quotient — a - b * c_div(a, b), which is c_mod() in the program. And for the low digit of an unsigned byte, b & 0x0F says what you mean more plainly than b % 16.
ABAP. (Not machine-checked — CI cannot run ABAP.) ABAP's MOD follows neither: its remainder is never negative, and lies between zero and the size of the right operand ↗, and DIV is defined to fit it. So −7 MOD 3 is 2, as in Python, and −7 MOD −3 is 2 as well, where both C and Python give −1. A remainder copied between the three languages has to bring its sign rule along.
Try it¶
- Select the bytes of a short UTF-8 word, apply Modulus 16 as Unsigned Byte, and read the original's right-hand hex digits in what is left.
- Repeat on a copy as Signed Byte and find every byte whose result differs. Each one started at
80or above. - Type
F9, which is −7 as a Signed Byte, and apply Modulus 3 as Signed Byte. Predict the byte from section 3's table first. - On a copy of a file, try Modulus 0 and note what the dialog does.
See also¶
- Divide — the quotient this remainder belongs to
- Binary And — And
0F, the same digit without the sign question - Shift Right — the other half of splitting a byte into its hex digits
- Counting in hexadecimal — why the right-hand digit is what is left after the sixteens