Микроупражнение: числа в буквы (как в Excel)

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

  1. captain cobalt

    captain cobalt New Member

    Публикаций:
    0
    Регистрация:
    21 дек 2003
    Сообщения:
    222
    Адрес:
    /ru/perm
    Перевести натуральное число в буквенный вид, используемый программами электронных таблиц для обозначения столбцов.



    То, есть, для натуральных чисел 1, 2,... 26, 27, 28,... 702, 703, 704,...

    программа должна возвращать A, B,... Z, AA, AB,... ZZ, AAA, AAB,...



    Программа должна правильно обрабатывать все положительные значения из диапазона unsigned dword.



    Эта задача чем-то похожа на вычисление записи числа в позиционной системе счисления.

    Но чем-то и отличается. :)
     
  2. ash

    ash New Member

    Публикаций:
    0
    Регистрация:
    9 ноя 2004
    Сообщения:
    52
    Адрес:
    Latvia
    А по-моему, это - задача о переходе от десятичной системы счисления к двадцатишестиричной.



    Хмм.. почему нельзя удалить сообщение? Не учёл того, что если A=1, то AA=11... Но, тем не менее, задача именно такова, с небольшенькой поправкой.
     
  3. flankerx

    flankerx New Member

    Публикаций:
    0
    Регистрация:
    2 июл 2004
    Сообщения:
    423
    Адрес:
    Moscow, Russia




    а по-моему -- это задача о ппереходе из 2^32-ичной системы счисления к 26-ричной.
     
  4. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Не совсем: тут нет нуля :)
     
  5. bogrus

    bogrus Active Member

    Публикаций:
    0
    Регистрация:
    24 окт 2003
    Сообщения:
    1.338
    Адрес:
    ukraine
    Можно попробовать начать с этого, если работает правильно (0xFFFFFFFF='MWLQKWU'), то потом избавится от деления, переходов и т.д.
    Код (Text):
    1. ;==========================================
    2. buffer      rb      8
    3. ;==========================================
    4.             mov     eax,-1 ; unsigned dword
    5.             mov     edi,buffer+7
    6. ;==========================================
    7.             mov     ecx,26
    8. excel:      xor     edx,edx
    9.             div     ecx
    10.             test    edx,edx
    11.             jnz     @F
    12.             add     edx,ecx
    13.             dec     eax
    14. @@:         dec     edi
    15.             add     edx,64
    16.             mov     byte [edi],dl
    17.             test    eax,eax
    18.             jnz     excel
    19. ;==========================================
    20. edi         =       offset 'MWLQKWU'
     
  6. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Что-то мне подсказывает, что не должно быть так просто :)

    видимо "правильное" решение будет вообще без цикла...



    пока переделал: в edi адрес начала буфера (22 байта с учётом cld)

    но здесь строка исскуственно разворачивается.. интересно бы это селать без стэка...


    Код (Text):
    1.  
    2. ;           mov     edi, buffer
    3. ;==========================================
    4.             cld
    5.             push    -'A'
    6.             push    'Z'-'A'+1
    7.             pop     ecx
    8. @@:         dec     eax
    9.             xor     edx,edx
    10.             div     ecx
    11.             push    edx
    12.             test    eax,eax
    13.             jnz     @b
    14. @@:         pop     eax
    15.             add     al,'A'
    16.             stos    byte [edi]
    17.             jnz     @b
    18.  
     
  7. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Сократил ещё на байт. вообще cld imho можно и не делать...
    Код (Text):
    1.  
    2. 00401010   .  FC            cld
    3. 00401011   .  6A BF         push    -41
    4. 00401013   .  6A 1A         push    1A
    5. 00401015   .  59            pop     ecx
    6. 00401016   .  48            dec     eax
    7. 00401017   >  31D2          xor     edx, edx
    8. 00401019   .  F7F1          div     ecx
    9. 0040101B   .  52            push    edx
    10. 0040101C   .  48            dec     eax
    11. 0040101D   .^ 79 F8         jns     00401017
    12. 0040101F   >  58            pop     eax
    13. 00401020   .  04 41         add     al, 41
    14. 00401022   .  AA            stos    [byte edi]
    15. 00401023   .^ 75 FA         jnz     0040101F
    16.  
     
  8. bogrus

    bogrus Active Member

    Публикаций:
    0
    Регистрация:
    24 окт 2003
    Сообщения:
    1.338
    Адрес:
    ukraine
    S_T_A_S_




    Свиснул у тебя идею, сократилось ещё на пару тактов :)
    Код (Text):
    1. ;==========================================
    2. buffer      rb      8
    3. ;==========================================
    4.             mov     eax,0xFFFFFFFF
    5.             mov     edi,buffer+7
    6. ;==========================================
    7.             mov     ecx,26
    8. excel:      dec     eax
    9.             xor     edx,edx
    10.             div     ecx
    11.             dec     edi
    12.             add     edx,'A'
    13.             mov     byte [edi],dl
    14.             test    eax,eax
    15.             jnz     excel
    16. ;==========================================
    17. edi         =       offset 'MWLQKWU'
    В общем, из 40 тактов на PIII, 38 приходится на деление. Есть мысль повозится с делением на константу (26), у Уоррена есть соотв. глава, нужно только в ней разобратся :)
     
  9. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Похоже на тебя оказывают дурное влияние любители Фога :).

    Нужно ли это оптимизировать по скорости?

    IMHO нет:

    - в условии задачи про это не сказано;

    - как много (в %) времени будет тратить програ на этот код? ну может где-то 0.05 % =)



    По поводу деления на константу (26) - на сайте лежит MagicNumber by The Svin

    Эта прога показывает вот что:
    Код (Text):
    1. MagicNumber = 2643056798
    2. mov eax,X
    3. mov edx, MagicNumber
    4. mul edx
    5. SHR edx, 4




    Но мне почему-то кажется, что задача не в этом - может нужно какой-нибудь хитрый логарифм посчитать ???
     
  10. bogrus

    bogrus Active Member

    Публикаций:
    0
    Регистрация:
    24 окт 2003
    Сообщения:
    1.338
    Адрес:
    ukraine




    Значит надо делать с компромисом по скорости\размеру :)

    И вообще, в первую очередь, меня интересует не сделать задачу для кого-то одного, а научится для себя (и чтобы польза была всем)







    Классная вещь, но для беззнакового деления с остатком не подойдет? Интересно бы разобрать возможность деления на 26, например как это умножение
    Код (Text):
    1.         eax = x
    2. lea     ecx,[eax*8+eax] ; eax*9
    3. lea     ecx,[eax*8+ecx] ; eax*9 + eax*8
    4. lea     ecx,[eax*8+ecx] ; eax*9 + eax*8 + eax*8
    5. add     eax,ecx         ; eax*9 + eax*8 + eax*8 + eax*1
    6.         eax = x*26
     
  11. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    bogrus >




    Остаток есть - в eax и младших битах edx. Только он кратен magic number'у :)

    Быстрее вычитанием его вычислить - "быстрое" умножение на 26 уже есть :) Хотя думаю лучше будет imul



    А вот раздельть на 13 посредством кучи lea imho будет дольше чем обычный div - посмотри, сколько единичек в двоичном представлении числа 2643056798 :-(
     
  12. NANO

    NANO New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2005
    Сообщения:
    5
    Нет так:

    eax=x

    lea edx, [eax +2*eax]

    lea eax, [eax +4*edx]

    add eax, eax

    eax=26*x
     
  13. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    Вычисление остатка от деления на 13 для uint32 при помощи маленькой таблички(в 104 байта) примерно за 11-12 тактов на атлоне:


    Код (Text):
    1. ;eax = eax mod 13
    2. mod13:
    3.     mov ebx,eax ;A*2^18+B < 2^32
    4.     and eax,3FFFFh  ;B < 2^18
    5.     shr ebx,18  ;A < 2^14
    6.     add eax,40001h  ;0 mod 13 > A
    7.     sub eax,ebx ;A*2^18+B = B-A mod 13
    8.  
    9.     mov ebx,eax ;A*2^10+B < 2^19
    10.     and eax,3FFh    ;B < 2^10
    11.     shr ebx,10  ;A < 2^9
    12.     add eax,60Bh    ;0 mod 13 > 3*A
    13.     lea ebx,[ebx*3] ;3*A
    14.     sub eax,ebx ;A*2^10+B = B-3*A mod 13
    15.  
    16.     mov ebx,eax ;A*2^6+B < 2^12
    17.     and eax,3Fh ;B < 2^6
    18.     shr ebx,6   ;A <= 40 < 2^6
    19.     sub eax,ebx ;A*2^6+B = B-A mod 13,  -40<=B-A<64
    20.     movzx eax,byte [.rest+eax];0 <= eax < 13
    21.     ret
    22. times 40 db (%+11) mod 13
    23. label .rest byte
    24. times 64 db (%+12) mod 13




    У кого есть идеи как избавиться от таблички?
     
  14. NANO

    NANO New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2005
    Сообщения:
    5
    Вот деление на 26



    eax=x

    lea eax, [eax+4*eax]

    sar eax, 7

    eax =x/26



    соответственно остаток, а...
     
  15. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    NANO

    Мысль интересная, но 128/5 = 25.6, а не точно 26.

    Поэтому вблизи чисел кратных 26 (77,128,129,154,155 и т.д.) результат завышен на 1. Хотя можно дополнительно проверять знак остатка и прибавлять 26, пока он не станет > 0.
     
  16. NANO

    NANO New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2005
    Сообщения:
    5
    Мысль -заменять деления на умножения обратному числу. Подбор числителя и знаменателя дроби - решение уравнения k/2^n -1 =0, где к =1,3,5,9(ограничения наложенны на числитель и знаменатель для оптимального решения в коде) -для данного варианта. Понятно, что можно добиться большей точности если понизить эти ограничения, допустим искать решения-



    k0*k1/2^n -1 =0 ->



    lea eax, [...] ; eax=eax*k0

    lea eax, [...] ; eax=eax*k1

    sar eax, n; eax =eax/2^n



    или решения для k0*(k1+1)/2^n -1 =0 ->



    lea edx, [...]; edx=eax*k0

    lea eax, [edx+k1*eax]; eax=k0*(k1+1)

    sar eax, n; eax =eax/2^n



    или для (k0*k1+1)/2^n -1 =0 ->



    lea edx, [...]; edx=eax*k0

    lea eax, [eax+k1*edx]; eax=(k0*k1+1)

    sar eax, n; eax =eax/2^n



    ...



    проще говоря вариантов масса, можно перебрать все(програмно естественно), для поиска наиболее точного.



    т.е. можно и умножать(выше) на 26(и другие) и делить быстро потенциально -ВСЕГДА. Потери точности... лечимы(только не проверкой :)
     
  17. bogrus

    bogrus Active Member

    Публикаций:
    0
    Регистрация:
    24 окт 2003
    Сообщения:
    1.338
    Адрес:
    ukraine
    http://www.hackersdelight.org/divcMore.pdf

    Наткнулся на доку по делению, есть примеры для таких констант 3,5,6,7,10,11,12,13,100,1000 (в русской версии Уоррена этих примеров нет)
    Код (Text):
    1. unsigned divu13(unsigned n)
    2. { unsigned q, r;
    3.      q = (n>>1) + (n>>4);
    4.      q = q + (q>>4) + (q>>5);
    5.      q = q + (q>>12) + (q>>24);
    6.      q = q >> 3;
    7.      r = n - q*13;
    8. return q + ((r + 3) >> 4);
    9. // return q + (r > 12);
    10. }
    Только понял, что можно было сделать так (собрать все мысли воедино:):
    Код (Text):
    1. ;==========================================
    2. buffer      rb      8
    3. ;==========================================
    4.             mov     eax,0xFFFFFFFF
    5.             mov     edi,buffer+7
    6. ;==========================================
    7. excel:      dec     eax
    8.             mov     ecx,eax
    9.             mov     edx,2643056798
    10.             mul     edx
    11.             shr     edx,4
    12.             mov     eax,edx
    13.             lea     edx,[edx+2*edx]
    14.             lea     edx,[eax+4*edx]
    15.             add     edx,edx
    16.             sub     ecx,edx
    17.             dec     edi
    18.             add     ecx,'A'
    19.             mov     byte [edi],cl
    20.             test    edx,edx
    21.             jnz     excel
    22. ;==========================================
    23. edi         =       offset 'MWLQKWU'