Source my answer in:
Is this expression correct in C preprocessor
I\'m a little bit out of my forte here, and I\'m trying to understand how this particular o
The first part of the algorithm is multiplying by an approximation to the reciprocal of 7. In this case, we're approximating computing the reciprocal with an integer multiplication and a right bit-shift.
First, we see the value -1840700269
(octal -015555555555
) as a 32-bit integer. If you read this as an unsigned 32 bit integer, it has value 2454267027
(octal 22222222223
). It turns out that 2454267027 / 2^34
is a very close integer approximation to 1/7
.
Why do we pick this number and this particular power of 2? The larger the integers we use, the closer the approximation is. In this case, 2454267027
seems to be the largest integer (satisfying the above property) with which you can multiply a signed 32-bit int without overflowing a 64-bit int.
Next, if we immediately right shift with >> 34
and store the result in a 32-bit int, we're going to lose the accuracy in the two lowest-order bits. Those bits are necessary to determining the right floor for integer division.
I'm not sure the second line was translated correctly from the x86 code. At that point, temp
is approximately num * 4/7
, so num * 4/7 + num
to that and bit-shifting is going to give you approximately num * 1/7 + num * 1/4
, a quite large error.
For example, take as input 57, where 57 // 7 = 8
. I verified the below in code as well:
57 * 2454267027 = 139893220539
139893220539 >> 32 = 32
(approx 57 * 4/7 = 32.5714...
at this point)32 + 57 = 89
89 >> 2 = 22
(huh?? Nowhere close to 8
at this point.)Anyway, for the last line, it is an adjustment we make after computing signed integer division this way. I quote from the section from Hacker's delight on signed division:
The code most naturally computes the floor division result, so we need a correction to make it compute the conventional truncated toward 0 result. This can be done with three computational instructions by adding to the dividend if the dividend is negative.
In this case (referring to your other post) it seems you are doing a signed shift, so it will subtract -1
in the case of a negative number; giving the result of +1
.
This isn't even all you can do; here's an even crazier blog post about how to divide by 7 with just a single multiplication.