10

I do not understand what is happening in this code. The C code is:

#include <stdio.h>

int main()
{
    const int mul = 100;
    int x;
    printf_s("Input a number\r\n");
    scanf_s("%i", &x);
    printf_s("%i/%i = %i\r\n", x, mul, x / mul);
    return 0;
}

I expected that the resulting assembly will be some simple shifts and add/sub operations, but there are some magic constants like 51EB851Fh, multiplications, etc. What is happening here?

; int __cdecl main()
_main proc near

x= dword ptr -8
var_4= dword ptr -4

push    ebp
mov     ebp, esp
sub     esp, 8
mov     eax, ___security_cookie
xor     eax, ebp
mov     [ebp+var_4], eax
push    offset Format   ; "Input a number\r\n"
call    ds:__imp__printf_s
lea     eax, [ebp+x]
push    eax
push    offset aI       ; "%i"
call    ds:__imp__scanf_s
mov     ecx, [ebp+x]
mov     eax, 51EB851Fh
imul    ecx
sar     edx, 5
mov     eax, edx
shr     eax, 1Fh
add     eax, edx
push    eax
push    64h
push    ecx
push    offset aIII     ; "%i/%i = %i\r\n"
call    ds:__imp__printf_s
mov     ecx, [ebp+var_4]
add     esp, 1Ch
xor     ecx, ebp        ; cookie
xor     eax, eax
call    @__security_check_cookie@4 ; __security_check_cookie(x)
mov     esp, ebp
pop     ebp
retn
_main endp
nobody
  • 19,814
  • 17
  • 56
  • 77
Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151

2 Answers2

13

Processors are not very good at dividing, an idiv can take between 11 and 18 cycles. As opposed to shifts and multiplies, they usually only take a single cycle.

So the optimizer replaced your division by a multiplication using fixed-point math, taking advantage of a 32-bit multiply producing a 64-bit result into edx:eax. Back-of-the-envelope: n / 100 == n * 0.32 / 32 == n * (0.32 * pow(2,32)) / 32 / pow(2,32). Those divisions are very cheap, just a right-shift. And the multiplier becomes 0.32 * pow(2,32) ~= 1374389535 == 0x51EB851F

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • According to Intel specs, imul takes 3 cycles, do you mean that it takes 1 cycle too or it refers to addition only? – Alex Zhukovskiy Jul 08 '14 at 14:26
  • 3
    It takes 1 cycle on Sandy Bridge. Use Agner Fog's instruction tables to get it broken down by architecture and addressing mode. – Hans Passant Jul 08 '14 at 14:29
0

51EB851Fh seems to be the magic number for divider for dividing through 100

Source: http://masm32.com/board/index.php?topic=1906.0 Reply #2

Bur0k
  • 136
  • 1
  • 8