Odd C++ anamoly

Started by
16 comments, last by Brother Bob 14 years, 5 months ago
Quote:Original post by Guinnie
I did replace the "shl eax, 1" with "imul eax, 2" and got the same result as with the shift, but this could be due to an internal processor optimization (you know how they are).


Shifting left by 1 is exactly equivalent to multiplying by two (and so you'll get the same result).
Advertisement
I said that myself in one of my previous posts. :P What I meant was that I replaced it with imul to see if there was a difference in how it reacted to the "overflow". Depending on how the processor handles the arithmetic internally, it might have had a similar result as when multiplying by 3 (as it did in the float version). Even though the binary outcome is expected to be identical, it's interesting that the "ALU" seemingly reacts the same, including the sign switch and all. So what comes to mind is that maybe the processor internally performs a shl when multiplying by 2/4/etc, even when the instruction used is imul.
Maybe I'll simplify what I said with an example. Imagine you could only represent 10 numbers. You could think of them as the last digit in a number, for instance. How would you define the sum? You would say, 0+0 = 0, 0+1 = 1, ... (similarly for all results less than 10), 5+5 = 0, 5+6 = 1...

Why did I pick 5+6 = 1? Well, pick a number that ends in 5 and add a number that ends in 6, and you'll get a number that ends in 1. Similarly, 5*6 = 0, because a number that ends in 5 times a number that ends in 6 is a number that ends in 0.

The numbers {0,1,2,3,4,5,6,7,8,9} with the operations of addition and multiplication defined above are called Z/(10). Notice that you can as easily define these operations on the numbers {-5,-4,-3,-2,-1,0,1,2,3,4}. When operating modulo 10 (which is what we are doing here), -3 is the same thing as 7, because a number that is obtained as a multiple of 10 minus 3 can also be written as a multiple of 10 plus 7.

What I am saying is that `int' represents Z/(2^32). The operations are well defined, even when there is "overflow". And the results you are getting are exactly what you would expect.

Okay, I think I understand how int works now, though the crazy abstract math stuff Alvaro is throwing at me is going way over my head. You were correct, none of that makes any sense to me.
I'm not sure I yet undersand how constantly multiplying by 2 eventually produces 0's according to your mathematical explanation, but 3 doesn't. Why is this so?
Quote:Original post by Guinnie
I'm not sure I yet undersand how constantly multiplying by 2 eventually produces 0's according to your mathematical explanation, but 3 doesn't. Why is this so?


Technically, 2 is a nilpotent element of the ring Z/(2^32), while 3 is not, since it's a unit.

Let's try to explain this with integers modulo 8 (3-bit integers). We can build the addition table and the multiplication table:
+ | 0 1 2 3 4 5 6 7     + | 0 1 2 3 4 5 6 7--+----------------     --+----------------0 | 0 1 2 3 4 5 6 7     0 | 0 0 0 0 0 0 0 01 | 1 2 3 4 5 6 7 0     1 | 0 1 2 3 4 5 6 72 | 2 3 4 5 6 7 0 1     2 | 0 2 4 6 0 2 4 63 | 3 4 5 6 7 0 1 2     3 | 0 3 6 1 4 7 2 54 | 4 5 6 7 0 1 2 3     4 | 0 4 0 4 0 4 0 45 | 5 6 7 0 1 2 3 4     5 | 0 5 2 7 4 1 6 36 | 6 7 0 1 2 3 4 5     6 | 0 6 4 2 0 6 4 27 | 7 0 1 2 3 4 5 6     7 | 0 7 6 5 4 3 2 1

Remember that the way these tables should be interpret is this: 3*5=7 (mod. 8) because if a number x is a multiple of 8 plus 3 and a number y is a multiple of 8 plus 5, their product is a multiple of 8 plus 7:
x=8*X+3
y=8*Y+5
x*y = 64*X*Y+8*5*X+8*3*Y+15 = 8*(64*X*Y+5*X+3*Y+1)+7

Now look at what happens when you start with 1 and multiply by 2 a few times:
1*2=2 (mod. 8)
2*2=4 (mod. 8)
4*2=0 (mod. 8)
0*2=0 (mod. 8)
0*2=0 (mod. 8)
...

If you do this with 3, you'll never get to 0 because if you did get to 0 after n steps, you would get to the equality

3^n = 8*X, for some integer X.

We know that this is not possible because of the unique factorization of integers.

[Edited by - alvaro on December 6, 2009 11:07:23 PM]
I'm still not fully following, but I'll trust you know what you're on about. Would the result of the bit shift then be coincidental - or would they be linked on some mathematical level? Thus explaining why both shifting and multiplication overflow in the same way when multiplying by 2, 4 and so on.
Multiplication by the base (or a power thereof) is equal to a shift to the left, filling in with zeros to the right, and that applies to any base. Consider for example multiplication by 10 in decimal. That is the same as shifting the digits to the left and filling in a zero at the right. Same with multiplication by 2 in binary; shift to the left and fill in zeros at the right.

Once you start multiplying too much, the digits are shifted outside the allowed range and zeros are being filled in from the right. At some point, there are no digits left, other than the zeros you fill in.

Shifting to multiply is not really a coincidence, but rather a special case of multiplication when the factor you multiply with is a power of the base (2 for binary, 10 for decimal, for example).

This topic is closed to new replies.

Advertisement