Преобразование 64-битного числа в строку
Автор: xrnd | Рубрика: Исходники | 27-12-2010 | Распечатать запись
В части 22 учебного курса рассматривались способы преобразования чисел в строку. Однако, вывести в десятичном виде число больше 32 бит не так просто. Сложность в том, что требуется делить число на 10, а число слишком большое и в регистры DX:AX не помещается.
К счастью, в программе на ассемблере можно делить числа любой разрядности 🙂 Правда, одной командой DIV тут не обойтись. Придётся реализовать специальный алгоритм.
Вот код примера:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | use16 ;Генерировать 16-битный код org 100h ;Программа начинается с адреса 100h jmp start ;Перепрыгнуть данные ;------------------------------------------------------------------------------- ; Данные a dq 1600000000000000 b dq 33000000 c dq 12345678901234567890 s_a db 'a = $' s_b db 'b = $' s_c db 'c = $' endline db 13,10,'$' s_pak db 'Press any key...$' ;------------------------------------------------------------------------------- ; Код start: mov di,s_a call print_str ;Вывод строки 'a = ' mov bx,a call print_qword_udec ;Вывод числа a call print_endline ;CR+LF mov di,s_b call print_str ;Вывод строки 'b = ' mov bx,b call print_qword_udec ;Вывод числа b call print_endline ;CR+LF mov di,s_c call print_str ;Вывод строки 'c = ' mov bx,c call print_qword_udec ;Вывод числа c call print_endline ;CR+LF mov di,s_pak call print_str ;Вывод строки 'Press any key...' mov ah,8 ;\ int 21h ;/ Ввод символа без эха mov ax,4C00h ;\ int 21h ;/ Завершение программы ;------------------------------------------------------------------------------- ;Процедура вывода учетверённого слова на консоль в десятичном виде (без знака) ; BX - адрес qword print_qword_udec: push di sub sp,22 ;Выделение места в стеке для буфера mov di,sp ;DI = адрес буфера push word[bx+6] ;\ push word[bx+4] ; \ push word[bx+2] ; / Помещение qword в стек push word[bx] ;/ call qword_to_udec_str ;Преобразование qword в строку mov byte[di],'$' ;Добавление символа конца строки mov di,sp ;DI = адрес буфера со строкой call print_str ;Вывод строки на консоль add sp,22 ;Восстановление указателя стека pop di ret ;------------------------------------------------------------------------------- ;Процедура преобразования учетверённого слова ;в строку в десятичном виде (без знака) ;Вход: qword в стеке (младший байт по младшему адресу!) ; DI - буфер для строки (20 символов). Значение регистра не сохраняется. qword_to_udec_str: push bp ;Сохранение BP mov bp,sp ;BP=SP push ax ;Сохранение используемых регистров push cx push bx push si xor cx,cx ;Обнуление CX mov bx,10 ;В BX делитель (10 для десятичной системы) .lp1: ;Цикл получения остатков от деления xor dx,dx mov si,8 ;SI = смещение слова для деления .div64: ;Цикл 64-битного деления mov ax,[bp+si+2] ;Загрузка слова в AX div bx ;AX = (DX:AX)/10; DX = остаток mov [bp+si+2],ax ;Сохранение результата деления слова sub si,2 ;Уменьшение смещения на 2 jnz .div64 ;Переход к началу цикла, если SI != 0 add dl,'0' ;Преобразование остатка в код символа push dx ;Сохранение в стеке inc cx ;Увеличение счетчика символов or ax,[bp+6] ;\ or ax,[bp+8] ; > Проверка частного на равенство 0 ;) or ax,[bp+10] ;/ jnz .lp1 ;Переход к началу цикла, если частное не 0. .lp2: ;Цикл извлечения символов из стека pop dx ;Восстановление символа из стека mov [di],dl ;Сохранение символа в буфере inc di ;Инкремент адреса буфера loop .lp2 ;Команда цикла pop si ;Восстановление сохранённых регистров pop bx pop cx pop ax leave ;Восстановление SP и BP ret 8 ;Возврат с выталкиванием 8 байт из стека ;------------------------------------------------------------------------------- ;Процедура вывода строки на консоль ; DI - адрес строки print_str: push ax mov ah,9 ;Функция DOS 09h - вывод строки xchg dx,di ;Обмен значениями DX и DI int 21h ;Обращение к функции DOS xchg dx,di ;Обмен значениями DX и DI pop ax ret ;------------------------------------------------------------------------------- ;Процедура вывода конца строки (CR+LF) print_endline: push di mov di,endline ;DI = адрес строки с символами CR,LF call print_str ;Вывод строки на консоль pop di ret |
Я сделал деление в виде цикла. Суть алгоритма в следующем. Деление qword выполняется в 4 этапа. Сначала обнуляем DX, загружаем старшее слово делимого и делим. Результат оказывается в AX, а в DX — остаток. Сохраняем результат на место старшего слова. Затем загружаем в AX следующее слово и делим. Обратите внимание, что в DX остаток от предыдущего деления! Снова сохраняем результат и загружаем следующее слово. На четвёртый раз частное оказывается на месте делимого, а остаток в DX 🙂
Буфер для строки выделяется в стеке. Максимум может быть 20 цифр + конец строки. 64-битное значение тоже помещается в стек и используется для хранения промежуточных результатов деления.
Скриншот программы:
27-07-2011 10:18
ек макарек, а алгоритм можно поподробнее описать, а то асм для меня темный лес. Мне необходимо подобный алгоритм реализовать на С.
17-09-2011 16:35
На С ещё сложнее, чем на ассемблере получится 🙂 Могу написать, если ещё актуально.
20-12-2011 17:42
а можете помочь с описанием подпрограммы умножения 2х чисел с плавающей точкой, реализовать надо на С++, через мантиссу, чтобы вычисления все в ассемблере, а вывод на экран средствами С++ в двоичном коде…с мантиссами вообще косяк, есть реализация в С++, но это не подходит так как точность слишком низкая, не такая как в асме….если не трудно нид хелп =)
16-09-2011 20:55
1. Здравствуй автор и все читатели! Прочитываю классные уроки не первый раз — очень они притягивают. Но иногда встречаются и ошибочки — первую нашёл в 13-м уроке, но там ты уже её исправил. Здесь кажется нашёл ещё одну — в твоей программе перевода 64-разрядного числа в строку. Попробуй перевести число 1600000000000000 (14 нулей) — программа печатает «a = 0» (или другая переменная — куда впишешь 1600000000000000). Ошибку я нашёл — она в подпрограмме «qword_to_udec_str». В строках 91-92 проверяется на «0» регистр ax как частное от деления, но в нём-то хранится не полное частное, а только младшие цифры частного! То есть надо сравнивать с нулём все цифры частного, которые записываются на место делимого в цикле на строке 84. Это можно сделать разными способами.
2. Чтоб никто не думал, что я только ищу ошибки, у меня и вопрос есть: вот как умножать число любой разрядности на число тоже любой разрядности понятно. Как делить число любой разрядности на 16-разрядное число, понятно (в этом уроке). А вот как делить число любой разрядности на число разрядности больше 16, используя 16-разрядные команды? Для начала делить хотя бы на 32-разрядные числа???
18-09-2011 13:57
Действительно, ошибка! Сейчас буду исправлять. Спасибо.
На второе отвечу позже.
18-09-2011 17:29
Для деления на длинные числа можно реализовать похожий алгоритм по деления частям. Пример такого алгоритма есть в книге Юрова «Assembler практикум». Но мне он кажется слишком сложным. Там какие-то частичные остатки, компенсирующие сложения…
Мне приходит на ум другой способ. Сделать такое деление без команды DIV и IDIV — комбинацией вычитаний и сдвигов. Эти операции просто выполняются для многобайтных чисел.
Одно можно сказать точно: если делитель больше разрядности процессора, то быстро и просто разделить не получится 🙂
18-09-2011 19:38
В Инете часто встречается метод деления столбиком любых чисел с любым основанием. В принципе этот способ деления аналогичен одинарному вычитанию и сдвигу в двоичной системе. А вообще надо попробовать будет … Спасибо за ответ! 🙂
09-01-2012 15:13
здравствуйте! как установить четное число бит числа С
29-07-2016 20:09
Довольно частыми операциями являются получение строки из числового значения во внутреннем представлении и обратно — число из строки. При преобразовании в строку обычно доступны средства задания форматирования в зависимости от языка пользователя.