Куда нужно округлять width*height/2?

Тема в разделе "LANGS.C", создана пользователем Black_mirror, 4 ноя 2009.

  1. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    В книжке для этой строчки дан следующий код:
    Код (Text):
    1. mov edx,[width]
    2. imul edx,[height]
    3. mov eax,edx
    4. sar eax,31;зачем тут два сдвига я не понимаю
    5. shr eax,31
    6. lea eax,[eax+edx+1]
    7. sar eax;тут видимо имеется ввиду sar eax,1
    Переменные и результат имею тип int, компилятор который такое генерит не указан. А меня интересует куда должно выполняться округление по стандарту, и как должен выглядеть дизассемблированный код?
     
  2. CyberManiac

    CyberManiac New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2003
    Сообщения:
    2.473
    Адрес:
    Russia
    Black_mirror
    (число).5 математики округляют к ближайшему чётному, бухгалтеры - строго вверх, программисты графических интерфейсов - почти всегда вниз (отбрасывают дробную часть), т.к. быстрее. Если заведомо известно, что одно из значений чётное, отдельные источники рекомендовали сначала разделить чётное на 2 сдвигом, и только потом умножать. Есть ли сейчас от этого фокуса какая-нибудь польза - не знаю.
     
  3. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    CyberManiac
    Здесь округляется уже произведение, но куда именно я совсем не понимаю, у меня получается, что произведение делится и округляется так:
    7FFFFFFFh -> C0000000h;потому что sar
    7FFFFFFEh -> 3FFFFFFFh
    7FFFFFFDh -> 3FFFFFFFh
    ....
    3 -> 2
    2 -> 1
    1 -> 1
    0 -> 0
    -1 -> 0
    -2 -> 0
    -3 -> -1 - такое я еще могу понять, но вот соседние строки ни в какие ворота не лезут
    -4 -> -1
    ...
    80000003h -> C0000002h
    80000002h -> C0000002h
    80000001h -> C0000001h
    80000000h -> C0000001h
     
  4. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Код (Text):
    1. sar eax,31;зачем тут два сдвига я не понимаю
    2. shr eax,31
    эквивалентно
    Код (Text):
    1. shr eax,31
    Код округляет от нуля.
    Т.е. положительные в большую сторону, отрицательные в меньшую.
    По модулю в большую сторону.
    Получается:
    if(mul_rslt>=0) rslt=(mul_rslt+1)>>1;
    else rslt=(mul_rslt+2)>>1; // rslt=(mul_rslt>>1)+1
     
  5. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    cppasm
    вот этот +2 меня и смущает, на мой взгляд -2/2 не должно быть равно 0!
     
  6. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Да, я ошибся.
    Округляется к плюс бесконечности.
    Причём округляется - не совсем правильное слово.
    Положительные - округляются в большую сторону.
    Отрицательные тоже округляются к большему, с той только разницей что если число делится нацело, результат будет на единицу больше.
    Псевдокод примерно такой:

    Код (Text):
    1. if(x>=0) rslt=round_up(x/2);
    2. else
    3. {
    4.   if(x % 2) rslt=round_up(x/2); // нечётное
    5.   else rslt=x/2+1;                   // это странно как-то...
    6. }
    Да и вообще код странный какой-то. Откуда он?
    То что два сдвига там лишние (эквивалентны одному), это 100%.
     
  7. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    Black_mirror
    очевидно, что один из них написал сам программист (т.к. используются int, то очевидно, что sar), второй же вставил компилятор, чтобы использовать в команде lea (но очевидно не додумался убрать, оптимизацию выключили, или этот случай не учитывается компилятором)
     
  8. cppasm

    cppasm New Member

    Публикаций:
    0
    Регистрация:
    18 июл 2006
    Сообщения:
    923
    Неа :)
    Я тут недавно наткнулся чисто случайно.
    Код чисто шаблонный, и нужен для деления на степени двойки знаковых чисел.
    А для деления на 2 один сдвиг там получается лишний.
    Вот код который генерирует GCC для ARM:

    деление на 2
    Код (Text):
    1.     lsr r3, r0, #31
    2.     add r0, r3, r0
    3.     asr r0, r0, #1
    деление на 4
    Код (Text):
    1.     asr r3, r0, #31
    2.     lsr r3, r3, #30
    3.     add r0, r3, r0
    4.     asr r0, r0, #2
    На х86 будет примерно так:

    деление на 2
    Код (Text):
    1.     mov ebx, eax
    2.     shr ebx, 31
    3.     add eax, ebx
    4.     sar eax, 1
    деление на 4
    Код (Text):
    1.     mov ebx, eax
    2.     sar ebx, 31
    3.     shr ebx, 30
    4.     add eax, ebx
    5.     sar eax,2
    Код очень похож на тот, который был в первом посте.
    Но в отличии от него округляет правильно :)