Skip to content

Multiply

Level: 201 · for anyone with a hex editor open

One line: X[i] *= Operand keeps only the low bits of a product that is usually wider than the value, so signed and unsigned write the same bytes, Multiply 2 is Shift Left 1 and can never write an odd byte, and Multiply 3 is undone by Multiply 171 — not by Divide 3.

What the dialog does

The manual writes Multiply as X[i] *= Operand (Hex Operations). The product of two eight-bit numbers can need sixteen bits, so Multiply throws away more than any other arithmetic in the list: everything above the width of the value. The programs keep the low bits, which is C's rule for unsigned arithmetic and the assumption the chapter page makes throughout.

In Python

Verified output of multiply_py.py — regenerated by tools/run_examples.py, never hand-typed.

1. THE PRODUCT KEEPS ITS LOW BITS
------------------------------------------------------------------------
   F0 x 2 is 480 as an Unsigned Byte and -32 as a Signed one. Both
   answers end in the same eight bits:

     Unsigned Byte  e0
     Signed Byte    e0

   Every byte, every operand 0-127: the same byte 32,768 times of 32,768.

2. MULTIPLY 2 IS SHIFT LEFT 1, AND IT LOSES THE TOP BIT
------------------------------------------------------------------------
     Multiply 2 == Shift Left 1, all 256 bytes          True
     different bytes Multiply 2 can write               128 of 256
     00 x 2 and 80 x 2                                  00 and 00

   Half the byte values can never come out of a Multiply 2, and every
   one that does came from two different inputs.

3. MULTIPLY 3 IS UNDONE BY MULTIPLY 171, NOT BY DIVIDE 3
------------------------------------------------------------------------
   Multiply every byte by 3, then try to get the file back:

     then Multiply 171 (AB)   restores 256 of 256
     then Divide 3            restores  86 of 256, bytes 00-55
     ...which are the bytes whose product never wrapped: True

   3 x 171 is 513, which is 2 x 256 + 1: within eight bits, 1. So
   Multiply 171 undoes Multiply 3 on every byte, and Divide 3 only on
   the ones that did not overflow.

   Multipliers with a partner like 171: 128 of 256, all of them odd:
   True. An even multiplier clears the low bit and loses the top one.

4. MULTIPLY 256 MOVES A WHOLE BYTE
------------------------------------------------------------------------
   0x1234 x 256 is 0x123400, and a Short keeps 0x3400:

     Unsigned Short, little   34 12 -> 00 34
     Unsigned Short, big      12 34 -> 34 00

   The same byte moved to the high end of the value both times. On
   screen, that is rightward in one row and leftward in the other.

5. A FLOAT DOUBLES IN ITS EXPONENT
------------------------------------------------------------------------
   As Float, Multiply 2 is floating-point multiplication. Nothing wraps
   and nothing is kept low; the exponent field goes up by one:

     1.0  00 00 80 3f -> 00 00 00 40   reads back 2.0
     3.0  00 00 40 40 -> 00 00 c0 40   reads back 6.0
     0.1  cd cc cc 3d -> cd cc 4c 3e   reads back 0.20000000298023224

The low half is the same for both readings

F0 × 2 is 480 as an Unsigned Byte and −32 as a Signed Byte, and both end in the bits 1110 0000. Section 1 checks every byte against every operand both types can hold and finds no exception: as with addition, two's-complement multiplication produces the same low bits whether the values are read signed or not. The part the two readings disagree about is the high half, and a Multiply that keeps only the low half never sees it.

Multiply 2 is a shift, and it forgets

Section 2 confirms that Multiply 2 and Shift Left 1 write the same byte for all 256 inputs, then counts what Multiply 2 can write at all: 128 values, every one even. 00 and 80 both become 00, and each of the 128 outputs comes from two inputs, so after a Multiply 2 no operation in the dialog can tell which byte was there before.

An odd multiplier can be undone

Section 3 is the surprising one. Multiply every byte by 3 and Divide 3 gets back only the 86 bytes whose product never passed 255, 00 to 55. Multiply 171 gets back all 256, because 3 × 171 is 513, which is 2 × 256 + 1, and within eight bits that is 1. Every odd multiplier has a partner like that — the program counts 128 of them, and all are odd — and no even one does, because an even multiplier has already shifted a bit off the top. In arithmetic that wraps at 256, dividing by 3 is a multiplication.

A float is not a wider integer

Section 5 multiplies Floats by 2. What changes is the exponent field, which in a little-endian Float lives in the last byte and the top bit of the one before it: 1.0 is 00 00 80 3F and 2.0 is 00 00 00 40, and for 0.1 the first two bytes are identical before and after. As Float or Double, Multiply is floating-point multiplication, and nothing about keeping low bits applies.

What the manual does not say

  • What a product that does not fit becomes; the programs keep the low bits.
  • Which types the dialog accepts for Multiply. Section 5 shows what floating-point multiplication does to the bytes, not what the dialog does.

If you are coming from Python or ABAP

Python. (b * k) & 0xFF is Multiply as Unsigned Byte, and pow(3, -1, 256) computes the partner directly — it returns 171, and raises ValueError for an even number, which has no partner to return. For a float's bytes, unpack, multiply and pack: struct.pack('<f', struct.unpack('<f', b)[0] * 2). There is no byte-level shortcut, because a float's bytes are not a number in positional notation.

ABAP. (Not machine-checked — CI cannot run ABAP.) An integer product outside the type's range raises CX_SY_ARITHMETIC_OVERFLOW rather than keeping the low half, so the Multiply 171 trick needs a MOD 256 after each product. ABAP's MOD is never negative ↗, which is the remainder the trick wants.

Try it

  1. Select sixteen bytes of a scratch file, Multiply 3 as Unsigned Byte, then Multiply 171 (0xAB), and check the file is back. Now use Divide 3 as the second step and find the first byte it gets wrong.
  2. Multiply 2 a copy of any binary file as Unsigned Byte and open Tools > Histogram/Entropy. Half the columns should be empty.
  3. Put a Float in four bytes, Multiply 2, and compare the Inspector's Unsigned Int reading before and after. Which bits changed?
  4. Multiply 34 12 by 256 as Unsigned Short under each Endian setting, and say which way the byte moved on screen, and why.

See also