Divide¶
Level: 201 · for anyone with a hex editor open
One line: X[i] /= Operand is the first operation in the list where the dropdown changes the bytes — F0 divided by 2 is 78 read as 240 and F8 read as −16 — and because the manual writes it in C, it rounds toward zero, which is neither Python's // nor a right shift.
What the dialog does¶
The manual writes Divide as X[i] /= Operand (Hex Operations ↗). Add, Subtract and Multiply never needed to know what number a value held: they moved bits, and two's complement made the signed and unsigned readings the same bits. Division has to know the number. So Treat Data As, which until now chose only a width, starts choosing a result.
In Python¶
Verified output of divide_py.py — regenerated by tools/run_examples.py, never hand-typed.
1. ONE DROPDOWN, TWO QUOTIENTS
------------------------------------------------------------------------
Divide f0 80 ff 7f by 2:
Unsigned Byte reads 240, 128, 255, 127 writes 78 40 7f 3f
Signed Byte reads -16, -128, -1, 127 writes f8 c0 00 3f
Byte values that divide differently: 128 of 256,
and they are exactly 80 to FF: True
Those are the bytes the two settings read as different numbers. The
first four operations never had to know the number; Divide does.
2. C ROUNDS TOWARD ZERO, AND PYTHON'S // ROUNDS DOWN
------------------------------------------------------------------------
The manual writes Divide as C's /=, and C drops the fraction, which
rounds toward zero. Python's // rounds toward minus infinity. They
agree until the quotient is negative:
a / b C // as a Signed Byte, C and //
7 / 2 3 3 03 and 03
-7 / 2 -3 -4 fd and fc
7 / -2 -3 -4 fd and fc
-1 / 2 0 -1 00 and ff
3. DIVIDE 2 IS NOT SHIFT RIGHT 1
------------------------------------------------------------------------
Shift Right 1 on a negative Signed Byte copies the sign bit in -- on
every compiler this library runs -- which rounds toward minus
infinity, where Divide rounds toward zero:
values where Divide 2 and Shift Right 1 differ 64 of 256
every one of them negative and odd True
-7, through Divide 2 and Shift Right 1 -3 and -4
4. DIVIDE 0
------------------------------------------------------------------------
There is no byte to write. This program's answer is Python's:
Divide 0 on 10 ZeroDivisionError
C leaves integer division by zero undefined, and the manual does not
say what the dialog does instead.
5. A FLOAT QUOTIENT IS ROUNDED TO ITS TYPE
------------------------------------------------------------------------
As Float or Double, Divide is floating-point division. 1 / 3:
Float ab aa aa 3e reads back 0.3333333432674408
Double 55 55 55 55 55 55 d5 3f reads back 0.3333333333333333
Neither is a third. Each is the nearest number its bits can hold,
and a Float and a Double are nearest in different places.
One dropdown, two quotients¶
Section 1 divides F0 80 FF 7F by 2 both ways. As Unsigned Byte those are 240, 128, 255 and 127, and the quotients are 78 40 7F 3F. As Signed Byte they are −16, −128, −1 and 127, and the quotients are F8 C0 00 3F. Over all 256 bytes, exactly the 128 from 80 to FF divide differently — the bytes the two settings read as different numbers. 7F is 127 either way and gives the same answer either way.
Which way a quotient rounds¶
C's integer division drops the fraction, which rounds toward zero: −7 / 2 is −3. Python's // rounds toward minus infinity: −7 // 2 is −4. Section 2 shows them agreeing on every quotient that is not negative and parting on every negative one that is not whole — FD against FC as a Signed Byte. A Python program that reproduces the dialog therefore cannot use //, which is why each program in this chapter that divides defines a four-line c_div().
Section 3 is the same fact meeting Shift Right. On every compiler this library runs, shifting a negative Signed Byte right copies the sign bit in, and that rounds toward minus infinity, like //: −7 >> 1 is −4. So Divide 2 and Shift Right 1 disagree on 64 of the 256 Signed Bytes, every negative odd one, and replacing one with the other is safe only where a value cannot be negative.
Dividing by zero, and by a float¶
There is no byte to write for Divide 0, and C leaves integer division by zero undefined. Section 4's answer is Python's ZeroDivisionError; what the dialog does is not in the manual, and is worth finding out on a copy of a file. As Float and Double, Divide is floating-point division, and section 5 shows the one thing to expect: 1/3 is a third in neither type, and the two types round it in different places.
What the manual does not say¶
- What Divide writes for an operand of 0.
- What the most negative signed value divided by −1 becomes: the answer, +128 in a Signed Byte, does not fit, and C leaves it undefined.
- Which types the dialog accepts for Divide.
If you are coming from Python or ABAP¶
Python. // is floor division, and int(a / b) truncates only while a float can hold the numbers exactly, so neither is C's division in general. On integers of any size, C's quotient is abs(a) // abs(b), negated when the signs differ — c_div() in the program above. divmod() returns the floor pair, which is Modulus's concern.
ABAP. (Not machine-checked — CI cannot run ABAP.) ABAP gives three answers where C gives one, all on one page of the keyword documentation ↗. In an integer calculation / rounds commercially where, as the page says, most languages cut the decimal places off. DIV is the integer part chosen so that MOD is never negative, which for a negative dividend and a positive divisor rounds down, like Python's //. And division by zero raises an exception, except 0 divided by 0, which the same page says gives 0.
Try it¶
- Type
F0 80 FF 7Finto a scratch file and Divide 2 as Unsigned Byte; undo, and Divide 2 as Signed Byte. Predict each byte from section 1 before you look. - On a copy of a file, Divide a few bytes by 0 and note what the dialog says or writes. Then try the most negative Signed Short,
00 80little-endian, divided by −1. - Divide 1.0 by 3 as Float in one copy and as Double in another, and compare what the Inspector shows for each.
- Take a run of negative Signed Bytes, Divide 2 in one copy and Shift Right 1 in another, and compare the two files with Tools > Compare. Every byte that differs should have started as a negative odd number.
See also¶
- Shift Right — the division that rounds the other way
- Modulus — the remainder, and whose sign it takes
- Multiply — why Divide cannot undo it
- Arithmetic has its own width —
>>against/on negative values, in four languages