Делаем float из строки

(О расположении данных и общей идеологии можно прочесть здесь.)
Сперва убедимся, что строка не начинается с минуса. Запомним этот факт на будущее.
Затем, будем извлекать из строки символы по одному, и делать из них целое. Надеюсь, это распространённое действие не вызовет у вас затруднений, и его не надо подробно комментировать. Если в ходе конверсии строки в ней обнаружится десятичная точка — точку игнорируем, а количество знаков после точки подсчитываем.
Если в строке наткнёмся на букву e — значит прекращаем конверсию числа, и переходим к конверсии экспоненты. Тут всё тоже просто и предсказуемо.
По завершению у нас есть три числа — само число, количество знаков в нём после точки и экспонента, оно же десятичный показатель степени. Из экспоненты вычитаем количество знаков после точки, насчитанных нами на предыдущем этапе, таким образом приводим строку например “0.025” к виду “25E-3”.
Дальше получаем значение десятичной экспоненты. Делаем это брутфорсом, простым перемножением на десятку в цикле.
И наконец множим наше число, или же делим его в зависимости от знака показателя степени, на значение экспоненты. Не забываем применить знак мантиссы. Вкратце как-то так.
Ниже листинг самого конвертера, на посмотреть, а в аттаче — монолитный файл, вместе со всеми зависимостями и приблудами, описанными мною ранее. Его можно загрузить в отладчик и погонять, посмотреть как работает с разными строками на входе. Пример сначала перемещает тестовую строку из флеша в ОЗУ, затем конвертирует её в iee754, и назад в текстовый вид. Просмотрев содержимое ОЗУ можно увидеть, как это всё работает.

;*****************************************************************************
; универсальный конвертер строка-число
; На входе - указатель в X на строку в RAM
; На выходе - число в первом аккумуляторе.
; Портит оба аккумулятора, var14 var15 r16
;*****************************************************************************
Str2flt: clr var10
clr var11
clr var12
clr var21 ; это будет количество знаков после запятой
clt
ld r16, x+ ; первый символ
cpi r16, '-' ; это минус?
brne stoiloop+1
set ; знак числа = отрицательный
stoiloop: ld r16, x+
cpi r16, 0x2e ; '.' десятичный разделитель
breq _stoidot
subi r16, 0x30
brcs _stoieos
cpi r16, 0x0a
brcc _stoieos
mov var20, r16
rcall Mul24Var1x0A ; Множим накопленное число на 10
add var10, var20 ; и добавляем извлечённый из строки разряд
adc var11, var14
adc var12, var14
adc var13, var14 ; проверяем на переполнение в ходе конверсии
breq pc+2
rjmp MaxFloat1
tst var21 ; Проверяем - была ли точка
breq stoiloop ; нет, не было, продолжаем
_stoidot: inc var21 ; да, точка была - приращиваем "счётчик знаков после точки"
rjmp stoiloop ; и продолжаем
; Конец основной строки. Сюда мы попали потому что встречен символ, отличный от цифры
; к этому моменту у нас есть число, накопленное в var1 и количество знаков после точки
; в var21. Ещё в r16 сидит тот символ, что воспринялся как терминатор строки
_stoieos: mov var13, r16
mov var14, var21 ; это посчитанное количество знаков посте точки
tst var14 ; Оно или ноль - запятой нет
breq pc+2 ; или на единицу больше реального. Приводим его к реальному
dec var14
movw var20, var10 ; перемещаем число в первый акк
mov var22, var12
rcall Fild24t ; Конвертируем его в ieee754
; Теперь разбираемся с порядком
clr var10 ; это будет счётчик экспоненты
ldi r16, 0x0a
mov var12, r16
mov r16, var13 ; восстанавливаем символ, вызвавший прекращение накопления числа
cbr r16, 0b00100000
cpi r16, 0x15 ; Это E или e?
brne _stoinoe ; Нет, что-то другое, воспринимаем как терминатор
; буква e была, извлекаем экспоненту
clt ; Знак экспоненты = положительный.
ld r16, x+
cpi r16, '-' ; Это мнус?
brne pc+3 ; нет, проверим, может это число
set ; да, ставим флаг что экспонента отрицательна
; Экстракция экспоненты в цикле
_s2fexexl: ld r16, x+ ; достаём следующий символ
subi r16, 0x30 ; проверяем - это число?
brcs _s2fchkexp
cpi r16, 0x0a
brcc _s2fchkexp
mul var10, var12 ; экспоненту множим на 10
tst r1 ; Переполнение?
brne _s2fexerr
add r0, r16 ; складываем накопленную экспоненту с новым числом
brcs _s2fexerr ; переполнение?
mov var10, r0
rjmp _s2fexexl
_s2fexerr: rjmp NaNFloat1 ; при переполнении порядка отдаём NaN
_s2fchkexp: mov r16, var10
cpi r16, 127 ; проверем экспоненту на допустимость
brcc _s2fexerr
brtc pc+2 ; Если экспонента начиналась с минуса
neg var10 ; меняем знак
; разбираемся со знаком экспоненты
_stoinoe: clt
sub var10, var14 ; От считанной экспоненты отнимаем количество знаков после точки
breq _stofdone ; если ноль, то дальнейших действий не требуется, число готово
brpl pc+3
set ; флаг - знак экспоненты отрицателен
neg var10 ; получаем модуль экспоненты
; Брутфорсное получение степени десятки
mov var15, var10 ; Помещаем экспоненту в счётчик
clr var10 ; делаем единицу
clr var11 ; длиной 24 бита
clr var12
inc var10
rcall Mul24Var1x0A ; получаем степень 10ки
dec var15 ; в цикле от модуля экспоненты до нуля
brne pc-2
; И тут - умножение либо деление числа на степень десятки
rcall Fswap ; Загоняем целую степень десятки в первый акк
brtc pc+4 ; проверяем знак экспоненты
rcall Fild24u ; Конвертируем степень десятки в ieee754
rcall Fswap ; меняем операнды местами
rjmp Fdiv ; Делим число на степень десятки
; Это - действия для чисел с положительной экспонентой
rcall Fild24u ; Конвертируем степень десятки в ieee754
rjmp F_mul ; множим число на степень десятки
_stofdone: ret
; Это всё, парни.
- +6
- 19 января 2021, 11:53
- Gornist
- 1
Файлы в топике:
str2flt.zip
Комментарии (4)
RSS свернуть / развернуть