Избранные места из библиотеки float

Как-то вот потребовалось кое-что сделать с использованием FLOAT, начал я рыть интернет. И тут со всей своей кристалльной ясностью вскрылся факт, что хороших руководств относительно FLOAT и ASSEMBLER для AVR нет. С вашего милостивого согласия я в меру скромных сил попробую несколько поправить положение дел.
Кругом, в том числе и на нашем сообществе, есть только обрывки несистематизированных исходников, без инструкций по применению. Иногда встречаются нестандартные форматы плавающих чисел; они-то может быть и неплохи, но не позволяют обмен данными с большими компьютерами. Самым богатым оказался улов с сайта electronix.ru, где нашлось аж несколько вариантов одной и той же библиотеки, запощенных разными людьми. Все исходники с отрезанным заголовком, в котором были бы описаны структуры данных. Есть и поздние наслоения в виде правок, привнесённых прочими авторами. Библиотека оттуда и оказалась самой полезной, особенно после того, как после поисков обнаружился изначальный вариант с заголовками и копирайтом “1998,1999 by Jack Tidwell <jackt@igalaxy.net>”.
Процесс внимательного изучения кода доставил немалое количество отборнейших лулзов. Например, автор исходного текста решил, что сменить знак 24хбитного числа можно вот так:
NEGMANT: ldi	temp,-1
	sub	mant1,temp
	sbc	mant1m,temp
	sbc	mant1h,temp
	ret

То есть не вычитая число из нуля, а наоборот, вычитая 0xff ff ff из числа. Следующий подход к снаряду произвёл программер с ником Magopa, и его версия была такой:
NEGMANT:
;-----------------------------------
	com mant1	;corrected by MAGOPA 11.11.2007
	com mant1m
	com mant1h
;-----------------------------------
	ret

И таких моментов в коде… ну, их есть тама. Вероятно, в ходе творческой обработки напильником, я внёс туда и свой вклад. Если вы найдёте откровенную ерунду в коде — не стесняйтесь, указывайте.
Пожалуй, начнём.
И начнём с хранения данных; Повторю ещё раз то, что объяснял в посте от конвертере флота в текст: Есть целочисленная математическая библиотека (о ней как-нибудь в другой раз), и она использует два аргумента — аргумент var1 хранится в регистрах r2~r9 и имеет размер до 64х бит. Аргумент var2 хранится в регистрах r10~r13 и имеет размер до 32х бит.
Эта асимметрия имеет естественную природу, поскольку умножение 32-битных чисел даёт в итоге 64 бита, и размеры аргументов позволяют провести операцию вида var1=var1*var2, для восьми-, шестнадцати-. двадцатичетырёх-, и 32-битных чисел, и обеспечить место для результата.
Два аргумента с плавающей точкой хранятся там же, только в некотором роде «наоборот». Первый аккумулятор занимает var2, а второй — var1, сейчас поясню почему именно так. Каждый из аккумуляторов имеет размер 32 бита, и соответственно занимает 4 регистра; число, хранящееся в этих регистрах имеет стандартный формат ieee754.
Когда с числом производятся какие-либо действия, оно распаковывается — младшие 24 бита занимает нормализованная мантисса, старшие 8 бит занимает экспонента в дополнительном коде, а знак временно хранится в регистре T. Псевдонимы регистров, хранящих мантиссу — mant1l, mant1m, mant1h — младший, средний и старший байты соответственно. В exp1 находится экспонента.
После математических действий число опять упаковывается в формат ieee или теряется, если больше не требуется.
Аналогично первому, второй аккумулятор состоит из регистров mant2l, mant2m, mant2h и exp_2 с таким же предназначением.
И если хранить первый аккумулятор в тех же регистрах, где хранится первый аргумент для целочисленных процедур — то станет невозможным использование при работе с float уже готовых процедур целочисленного умножения — расширяясь в ходе умножения, первый аргумент затрёт содержимое экспоненты первого float. А второй float и так типа жертвенный, после операции он не потребуется, пусть его ломает, не жалко. Вот как-то так.
Теперь — о составе библиотеки. На настоящее время обработке рашпилем подверглись три арифметические операции — умножение, деление, и сложение (оно же вычитание). И конвертеры из флота в инт и назад. Все процедуры были опробованы на тестовых данных, аномалий поведения пока не замечено.
Корни-шморни, сравнить — встепень это будет когда-нибудь завтра. Наверное. Может быть. Не знаю.
Кроме того, для публикации подготовлен монолитный файл, где все процедуры просто лежат вместе. С точки зрения компактности было бы хорошо разделить его на несколько отдельных, и подключать к проекту по необходимости. Ну это как кому удобнее.

Fadd /Fsub — складывает содержимое первого и второго аккумуляторов. Результат в первом аккумуляторе, содержимое второго разрушено. Вычитание сначала меняет знак второго слагаемого на противоположный, затем складывает.

Fdiv делит содержимое первого аккумулятора на содержимое второго. Результат в первом аккумуляторе, содержимое второго разрушено.

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

FINT делает из числа в первом аккумуляторе целое со знаком 24бита. Результат остаётся в мантиссе первого аккумулятора, то есть var20:21:22.

FILD24u — делает из целого беззнакового 24бит числа float. Число на входе должно быть в мантиссе первого аккумулятора, то есть var20:21:22. Результат в первом аккумуляторе.

FILD16u — 16бит версия Int to Float. Число на входе в var20:21. Результат в первом аккумуляторе.

FILD16s — то же, но число со знаком.

Ещё в комплекте присутствует процедура целочисленного умножения 24 * 24 бита, без знака. Более подробные описания в комментариях-заголовках каждой из процедур

В файле float.zip — сокращённая и комментированная версия после обработки напильником.
В файле fp_routines.zip находится самый первый найденный мной оригинал исходника, без моих или чьих-либо ещё правок.

UPD 2021_01_14. Обновил Float.zip. В процедуре деления после нормализации ошибочно приращивалась экспонента (вместо того, чтобы уменьшаться). В результате некоторые числа получались в 4 раза больше.
  • +5
  • 24 ноября 2020, 14:17
  • Gornist
  • 2
Файлы в топике: fp_routines.zip, float.zip

Комментарии (4)

RSS свернуть / развернуть
Если вы найдёте откровенную ерунду в коде — не стесняйтесь, указывайте.
Сколько архитектур МК столько и разновидностей ассемблера. Если уж указали тег Assembler, не помешал бы и тег AVR. Имхо.
0
Согласен.
0
лет цать назад на форуме автоматчиков был вопрос: при сравнение результата операции 0,1 + 0,2 и заданного значения 0,3 не срабатывает оператор Равенство = глюк оператора языка? А речь была о производственной установке… с возможно тяжёлыми последствиями.
С этим форматом не всё так однозначно: Сложение двух чисел 0,1 + 0,2 с плавающей запятой… с потерей точности # 0,3
habr.com/ru/post/523654/
Начнём с того, что чисел 0,1 и 0,2 в двоичной арифметике с плавающей запятой быть не может, а наиболее близкие к ним значения для типа данных double (число удвоенной точности binary64, так его называют в Стандарте IEEE-754) следующие:
0,1000000000000000055511151231257827021181583404541015625 +
0,2000000000000000111022302462515654042363166809082031250 =
0,3000000000000000166533453693773481063544750213623046875.
аналогичные проблемы и с одинарной точностью
так и в этом случае никогда не сработает равенство…
Решение = сравнивать результат на попадание в диапазон с учётом неточности конвертации текстовой «плавающей точки» в его двоичное представление.
0
Решение = сравнивать результат на попадание в диапазон с учётом неточности конвертации
Это вполне себе стандартный подход, описанный в учебниках по программированию. Задается окрестность точки и проверяется попадание в нее. А так же правило семи знаков никто не отменял.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.