Делаем 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 свернуть / развернуть
(О рапололожении
лоло это не айлюлю…
Если в строке наткнёмся на букву e — значит прекращаем конверсию числа, и переходим к конверсии экспоненты. Тут всё тоже просто и предсказуемо.
Значит выводим «Hello world» и получаем его формате экспоненты?
0
Да, если если Hello world начинается с последовательности цифр.
+1
вспоминается две картинки про сову :)
0
Уащще никаких проблем!
Разве я запрещаю вам преподать всем мастер-класс по рисованию сов? Как вы могли такое подумать!
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.