Красивое "выравнивание" числа

Тема в разделе "WASM.A&O", создана пользователем Broken Sword, 10 ноя 2005.

  1. Broken Sword

    Broken Sword Robert

    Публикаций:
    0
    Регистрация:
    30 авг 2002
    Сообщения:
    433
    Дано: число A

    Найти: число B, являющееся числом A, выровненным на границу N в большую сторону.



    Пример: A = 7E70h, N = 200h. B = 8000h



    Алгоритм ((7E70 div 200h) + 1)*200h не предлагать :).
     
  2. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Broken Sword



    Ну тогда половинным делением :))
     
  3. _G3

    _G3 New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    41
    Адрес:
    Russia
    если N кратно степени двойки, то можно так:

    (A+N) AND (-N)
     
  4. bogrus

    bogrus Active Member

    Публикаций:
    0
    Регистрация:
    24 окт 2003
    Сообщения:
    1.338
    Адрес:
    ukraine
    Да, типа того
    Код (Text):
    1.             mov     eax,0x7e70
    2.             mov     ecx,0x200
    3.             add     eax,ecx
    4.             neg     ecx
    5.             and     eax,ecx
     
  5. Dimson

    Dimson New Member

    Публикаций:
    0
    Регистрация:
    7 июл 2005
    Сообщения:
    59
    Адрес:
    Russia
    (A+N-1)&(~(N-1))
     
  6. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    _G3



    Имел ввиду (A+(N-1)) AND -N наверно?

    Если ты просто будешь прибавлять A+N то A будет бессмысленно увеличена даже в том случае когда уже выравнена по N.
     
  7. _G3

    _G3 New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    41
    Адрес:
    Russia
    The Svin

    да, но Dimson уже поправил

    ошибка пошла от самого sword'а

    я от его алгоритма отталкивался и не заметил
     
  8. bogrus

    bogrus Active Member

    Публикаций:
    0
    Регистрация:
    24 окт 2003
    Сообщения:
    1.338
    Адрес:
    ukraine
    Так правильно?
    Код (Text):
    1.             mov     eax,0x7e70
    2.             mov     ecx,0x200
    3.             lea     eax,[eax+ecx-1]
    4.             neg     ecx
    5.             and     eax,ecx
     
  9. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    Обычно когда встаёт вопрос о выравнивании на степень двойки то сама степень двойки предпологается как константа. Т.е. её не нужно никуда загружать.

    пусть в eax любое число которое нужно выравнять по 200h

    тогда:
    Код (Text):
    1.  
    2. add eax,(200h-1)
    3. and eax,-200h
    4.  




    Почему это "работает" понятно? Или требуются разъяснения?
     
  10. Elusory Jo

    Elusory Jo New Member

    Публикаций:
    0
    Регистрация:
    26 янв 2006
    Сообщения:
    30
    Адрес:
    Moscow
    Хмм... Разъясните пожалуйста...
     
  11. doctor_Ice

    doctor_Ice New Member

    Публикаций:
    0
    Регистрация:
    21 мар 2005
    Сообщения:
    845
    Адрес:
    Russia
    1+1ffh=200h and -200h=200h выровнено

    200h+1ffh=3ffh and -200h=200h выровнено



    если и теперь непонятно смотри описание команды and
     
  12. Elusory Jo

    Elusory Jo New Member

    Публикаций:
    0
    Регистрация:
    26 янв 2006
    Сообщения:
    30
    Адрес:
    Moscow
    если и теперь непонятно смотри описание команды and



    :-D
     
  13. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    Я постараюсь объяснить максимально подробно. При этом наверняка будет сказано, то что уже понятно. Поэтому не нужно говорить после "это и так понятно", так как мне пока неизвестно, что из то что "и так понятно" в данный момент непонятно :)



    Итак предметная область - округление числа в большую сторону.

    Задача1: Сколько нужно 10и литровых вёдер воды, чтобы разом отнести 25 литров.

    Ответ: 3и ведра. 2а для 20 литров, и одно оставшееся для 5и. Отметим что 25:10=2 остаток 5.

    Задача2: Сколько нужно 10и литровых вёдер воды, чтобы разом отнести 20 литров.

    Ответ: 2а ведра.

    Отметим что 20:10=2 остаток 0.

    В задаче1 могло быть не 25 литров а например любое количество x 20<x<30. И при этом ответ был бы тот же. Намеренно написано 20<x<30 а не 20<x<=30.

    Почему? Потому что хотелось отметить что при 20<x<30

    x:10=2 и остаток не равен 0.

    На основании замечаний в задаче1 и в задаче2 делаем простое (но тем не менее фундаментальное для нашей задачи) замечание. Если остаток есть - мы частое увеличиваем на 1.

    Если нет - частное и есть наш ответ.

    Теперь мы изменим чуть вопрос в задаче, так чтобы проблема была максимально похожа на то, что спрашивалось в этом топике.

    Мы должны спросить не "сколько 10и литровых вёдер понадобится", а "какова будет суммарная ёмкость 10и литровых вёдер, которые потребуются чтобы отнести разом 25 литров воды?"

    И ответ будет в первом случае 30 литров, во втором 20 литров. В этом случае мы округляли число вверх до кратности 10и. Т.е. до 10<sup>1</sup>

    Обратим внимание, что если нам данные давали бы в десятичной системе и округлять нужно было бы до кратности степени десятки, то мы моментально смогли бы решать задачу в уме.

    Например разом нужно было бы отнести 1234567 литров.

    И вёдра были бы тоже 10и литровые.

    В числе 1234567 видно глядя на младший разряд какой остаток будет от деления на 10. Нам даже не важно какой именно он будет - важно ли будет он вообще или нет.

    Т.е. равен или нет нулю. Если он не равен нулю то часть числа старше его (123456) мы увеличиваем на 1 получая вместо него 123457 а вместо последнего разряда пишем 0. Ответ 1234570.

    Если бы вёдра были 100литровые (10<sup>2</sup>) мы бы могли сделать то же за исключением того что если бы увидили что в двух младших раздядах 1234567 не ноль

    то мы увеличили бы на 1 старшую часть числа 1234567

    а нули записали бы в 2х младших разрядах. Если бы вёдра были ёмкостью 10<sup>n</sup> литров. То мы смотрели не ноль ли n младших раздядах и если нет - то увеличивали бы часть числа старших на 1 а в n младших писали бы 0.

    Т.е. при округлении к кратности 10<sup>n</sup> если в младших n разрядах больше нуля - увеличивается на 1 часть числа выше их а в сами n младшие разряды записывается 0.

    Обратим внимание что если в n младших разрядах уже 0, то вторая часть операции "записать в n младших разрядов 0" - ничего не испортит даже если число уже кратно. Важно лишь если оно уже кратно не выполнять первую часть - увеличения части старше n младших разрядов на 1.

    А сейчас давай зададимся вопросом

    А нельзя ли эту самую операцию "посмотреть в n младших разрядов и если в них не 0 увеличить на 1 часть числа старше n младших разрядов" заменить каким то простым арифметическим действием, которое если в n младших раздядах 0 НЕ увеличит часть числа выше их а если не ноль - Обязательно увеличит и обязательно на 1 какое бы отличное от ноль число там не находилось?

    Если мы например в задаче с 10и литровыми вёдрами прибавили к количеству воды 9?

    Если бы в младшем разряде был 0 то старшая часть числа бы не изменилась. Пример 120 + 9 = 129

    А будь там в младшем разряде любое число x от 1 до 9 (т.е. не равное ноль мы получили бы в ответе 12x+9=13y где y было бы значение = x-1

    Т.е. мы могли бы прибавить 9 к числу и после этого записать 0 в младший разряд даже не зная - делится ли оно нацело на 10 или нет - и всё равно получили бы правильный ответ.

    Обратим внимание что мы округляли до кратности 10<sup>1</sup> а прибавляли 9 т.е. 10<sup>1</sup> -1

    А если бы вёдра были 100 литровые мы округляли бы до 10<sup>2</sup> а прибавляли бы 99 т.е. 10<sup>2</sup>-1

    После чего мы сбрасывали бы 2а младших разряда в 0.

    Понятно что если бы вёдра были 10<sup>n</sup> литровые то прибавляли бы мы 10<sup>n</sup>-1 т.е. состоящее n 9ок.

    Теперь понимание обретённое в 10и чной систем переносим в двоичную.

    Если мы округлям двоичное число x до 2<sup>n</sup>, то также если в у числа x в n младших разрядах не 0 то часть числа выше их мы увеличиваем на 1 а в n младших разрядов пишим нули.

    Например x=100101 мы округлям до 2<sup>2</sup>

    в млаших разрядах 01 значит старшую часть числа 1001 мы увеличить должны до 1010 а в младшую записать 00

    получаем 100101->101000

    Опять же если мы прибавим при таком округлении к x2<sup>2</sup>-1 т.е. 3 или в бинарном виде 11 то старшая часть числа также увеличилась бы на 1, причём только тогда когда в двух младших разрядах было бы любое число кроме нуля.

    100101+11=101000

    ...

    100111+11=101010

    но

    100100+11=100111

    Т.е прибавив 11 к любому числу и сбросив после этого в 0 последние два биты мы получаем округлённое вверх до кратности 2<sup>2</sup> число, неважно было оно уже округлено(в этом случае число будет останется равно результату "округления") или нет.



    Теперь о последнем - о записи.

    200h - это в бинарном виде 10 0000 0000 т.е. 2<sup>9</sup>. 200h -1 это 2<sup>9</sup> - 1. Т.е. бинарное число из 9 единиц (по аналогии с десятичной где 10<sup>9</sup> -1 было бы в десятичной записи числом из 9 девяток).

    Число -200h это соответсвенно -2<sup>9</sup>.

    And на такое число именно выполняет операцию сброса 9и младших разрядов в 0. Бинарный рисунок -1*2<sup>n</sup> таков, что в n младших разрядах у него нули а в остальных единицы. Так как x and 1 = x и x and 0 = 0 то n младших разрядов при AND на такое число сбрасываются в 0, а старшие какими были такими и остаются.

    Почему такой бинарный рисунок у 2<sup>n</sup> c отрицательным знаком нужно объяснять?