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

Кругом, в том числе и на нашем сообществе, есть только обрывки несистематизированных исходников, без инструкций по применению. Иногда встречаются нестандартные форматы плавающих чисел; они-то может быть и неплохи, но не позволяют обмен данными с большими компьютерами. Самым богатым оказался улов с сайта 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
лет цать назад на форуме автоматчиков был вопрос: при сравнение результата операции 0,1 + 0,2 и заданного значения 0,3 не срабатывает оператор Равенство = глюк оператора языка? А речь была о производственной установке… с возможно тяжёлыми последствиями.
С этим форматом не всё так однозначно: Сложение двух чисел 0,1 + 0,2 с плавающей запятой… с потерей точности # 0,3
habr.com/ru/post/523654/
так и в этом случае никогда не сработает равенство…
Решение = сравнивать результат на попадание в диапазон с учётом неточности конвертации текстовой «плавающей точки» в его двоичное представление.
С этим форматом не всё так однозначно: Сложение двух чисел 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.
так и в этом случае никогда не сработает равенство…
Решение = сравнивать результат на попадание в диапазон с учётом неточности конвертации текстовой «плавающей точки» в его двоичное представление.
Комментарии (4)
RSS свернуть / развернуть