Добавьте два 32-битных числа с плавающей точкой с помощью AVR-Ассемблера

Я пытаюсь использовать AVR Studio, чтобы сложить два 32-битных числа с плавающей точкой. Я знаю, что мне нужно будет хранить 32-битное число в 4 отдельных 8-битных регистрах. Затем мне нужно добавить регистры вместе, используя флаг переноса. Это то, что я до сих пор. Я добавляю 5.124323 и 2.2134523.

;5.124323 (01000000101000111111101001110100)
;Store hex value (40A3FA74)
ldi r21,$40
ldi r22,$A3
ldi r23,$FA
ldi r24,$74


;2.2134523 (01000000000011011010100100110100)
;Store hex value (400DA934)
ldi r25,$40
ldi r26,$0D
ldi r27,$A9
ldi r28,$34


;Add the corresponding bytes together including the carry flag
add r21,r25
adc r22,r26
adc r23,r27
adc r24,r28

Кажется, это не дает мне правильного ответа... Я почти уверен, что единственное, что здесь не так, это порядок регистров в последних 4 строках кода. Или, может быть, функции add / adc также неверны. Может кто-нибудь помочь мне с этим?

3 ответа

Спустя почти 4 года вы не получили прямого ответа на вопрос, и вы это заслужили. Итак, поехали:

Вы не можете получить биты с плавающей точкой и просто ДОБАВИТЬ их, чтобы получить плавающую точку результата, они не являются десятичными или шестнадцатеричными числами.

Числовая система с плавающей точкой пугает многих людей, это другой способ представления числовых значений, очень полезный для вычисления с гигантскими или микроскопическими числами. Мы не узнаем, что в начальной школе, а не в средней школе, даже в колледже. Нет карманного калькулятора Casio, который может обрабатывать числа FP, поэтому мы не очень знакомы с ними, и мы все одни в темноте.

Большинство программистов (если не все) предпочитают оставлять FP для работы с компиляторами C/C++, они не касаются FP побитово, для некоторых это похоже на чуму. Как бы то ни было, FP был создан людьми (настоящая проблема), но есть способ с этим справиться.

Мое намерение здесь не в том, чтобы научить вас делать это в сборке AVR, даже если это полностью возможно и легко, если вы будете следовать моим объяснениям ниже. Мое намерение здесь состоит в том, чтобы показать, что "тигр" - не монстр, это маленькая кошка с голубыми глазами, но все же, это кошачий с зубами и когтями.

Ниже вы найдете:

1. Внутренности Плавающей Точки
2. Легкое умножение с плавающей запятой
3. Дополнение с плавающей точкой (ваш вопрос)

Кишки:

FP одинарной и двойной точности, 32- и 64-битные, позволяют снимать 32.

Номер FP имеет 3 района:

a) Знак, 1 бит, # ​​32 (первый слева), ноль для положительного, один для отрицательного.
б) Экспонент, 8 бит, здесь, где охотится тигр, люди очень запутаны.
в) Мантисса, 23 бита, доля экспоненты дополняет.

Чтобы иметь возможность представлять очень большое или очень маленькое число, центр будет равен 1, поэтому, когда бит 7 экспоненты (назовем его "7E") равен единице, число равно 1 или больше. 7E=1 число>> 1, 7E=0 число меньше 1 (например, 0,7).

Вам не нужно считать 7E как действительный бит со значением, когда думают о значении битов Экспонента, некоторые люди делают это. С этого момента здесь мы будем иметь дело только с числами, большими, чем 1, и положительными, поэтому я не буду комментировать данные о 7E или знаковых битах и ​​даже не буду учитывать их как значение экспоненты.

Экспонент и Мантисса считаются двоично. Значения битов экспоненты совпадают с обычным двоичным числом, разница состоит в том, что значение бита всегда складывается из 1, E1=2, E2=3, E2+E1=4, E3=5... Все биты От E1 до E6 = 128.

Значение представления экспоненты равно 2^n, где "n" - это значение битов или комбинации битов, как указано выше. Минимум будет 2^-126 - очень маленькое жестяное число, а максимум - 2^128 - гигантское гигантское число, считающееся бесконечным, 2^127 имеет 39 цифр.

E7.6.5.4.3.2.1.0   
 1 0 0 0 0 0 0 0 = decimal 0, but remember, it needs to add 1, so it is 1, and the value is 2^1 = 2

E7.6.5.4.3.2.1.0   
 1 0 0 0 0 0 0 1 = decimal 1, +1 = 2, 2^2 = 4   
 1 0 0 0 0 0 1 0 = decimal 2, +1 = 3, 2^3 = 8   
 1 0 0 0 0 0 1 1 = decimal 3, +1 = 4, 2^4 = 16   
 1 0 0 0 0 1 0 0 = decimal 4, +1 = 5, 2^5 = 32   
 1 0 0 0 0 1 0 1 = decimal 5, +1 = 6, 2^6 = 64   
 1 0 0 0 0 1 1 0 = decimal 6, +1 = 7, 2^7 = 128   
 1 0 0 0 0 1 1 1 = decimal 7, +1 = 8, 2^8 = 256   
 1 0 0 0 1 0 0 0 = decimal 8, +1 = 9, 2^8 = 512   
 1 0 0 1 0 0 0 0 = decimal 16, +1 = 17, 2^17 = 131072   
 1 0 0 1 0 0 0 1 = decimal 17, +1 = 18, 2^18 = 262144   

Заметьте, что добавляя 1 к экспоненте, умножьте ее значение на 2.
Обратное верно, вычитая 1, делим экспоненту на 2.

10000100 = 32, вычитая 1, становится 10000011 = 16.

Кроме того, сдвигая один бит влево и добавляя 1, умножьте экспоненту на себя, как N^2. Например, 10000010 = 8, сдвиг влево 10000100 = 32, плюс 1 10000101 = 64 = 8^2.
Итак, сдвиг влево = N * N/2.

Инверсия верна только в том случае, если бит E1 (последний бит экспоненты) равен 1, случай 10000101 (64), вычитая единицу и сдвиг вправо, даст вам квадратный корень, 10000010 (8). То же самое для 10001011 (4096) вычитания единицы и сдвига вправо становится 10000101 = 64. Если E1=0, результат недействителен, случай 10000110 (128) вычитания единицы, 10000101 и сдвига вправо становится 10000010, то есть 8, если только сдвиг без вычитания становится 10000011, то есть 16, оба недействительны.

Укрепление идеи:
0000-1110 в двоичном виде означает 2 ^ 3 + 2 ^ 2 + 2 ^ 1 = 8 + 4 + 2 = 14.
x000-1110 в FP Exponent означает 2^(2^3 + 2^2 + 2^1 + 1) = 2^(14+1) = 2^15 = 32768.

Но вы видите, что Экспонент может формировать только значения 2^n, а не промежуточные значения, такие как 5, 12, 15, 257 и т. Д. Именно здесь работает Мантисса. Он хранит доли Экспонента как 2^-n, где это "n" - это значения битов Мантиссы.

FP бит 23 является битом Мантиссы 1, поэтому его значение равно 2^-1, то есть 1/2 или 0,5.
Бит 22 FP - это бит Мантиссы 2, его значение 2^-2, 1/4 или 0,25 и т. Д.

Когда вы ставите отрицательный знак на "n" как показатель степени 2, это то же самое, что использовать это значение для деления 1. 2^3=8, 2^-3=1/8.

Но значение Mantissa не является числовым, это множитель доли экспоненты. Например, бит FP Mantissa M1 10000000... не равен 0,5, это означает, что значение экспоненты должно быть добавлено к его собственной половине. Чтобы понять это, Mantissa 23 бит все меньше 1, они равны 0.nnnn. Исходя из этого, нам нужно вставить перед ним невидимое целое число 1. Таким образом, Mantissa 1000000... то есть 0,5 становится 1,5. Это означает, что экспонента должна быть умножена на 1,5

Итак, FP 0-10000001-1000000000.... означает показатель степени 4, умноженный на Mantisse 1.5 = 6.
Если FP 0-10000001-110000000... означает 4 х (1 + 0,5 + 0,25) = 4 х 1,75 = 7

Bit 23 = 0.5  (left bit)   
Bit 22 = 0.25   
Bit 21 = 0.125    
Bit 20 = 0.0625   
Bit 19 = 0.03125   

...   
...   
Bit 1 = 0.0000001192092896 (bit FP 1)  

Теперь та же самая Мантисса с 0,75 (1100000....) всегда умножит Экспонент на 1,75, независимо от его значения. Число 6, 12, 24 делит ту же мантиссу со значением "100000....", то есть 0,5, потому что оно умножается на 4 x 1,5=6, 8x1,5=12, 16x1,5=24 и т. Д.

Какова важность этого?

2. Легкое умножение с плавающей запятой

Понимаете, если у вас два числа с ЖЕ Мантиссой, это означает, что оба числа будут иметь одинаковый множитель дроби. Если вы хотите умножить эти числа, просто умножьте экспоненту, разделите мантиссу, сдвигая вправо.

Например, умножение 6 на 24.
2 ^ n Показатель 6 равен 4, Мантисса равна 0,5
2 ^ n Показатель 24 равен 16, Мантисса также 0,5
Затем 6 x 24 = 144, разделив на 1,5 и снова на 1,5 = 64, то есть ровно 4x16 .
Разделить дважды на 1,5, потому что есть две мантиссы, будет таким же, как деление 144 / (1,5 * 1,5) = 144 / 2,25 = 64.
Таким образом, новая мантисса результата умножения будет 0,5 * 0,5 = 0,25 или 0,5 / 2.

Любопытство, что такое ФП для 144?
0-10000110-0010000000000....
что означает экспонент 10000110? биты справа "000110" = 6 +1 = 7, 2^7=128
что значит мантисса 001000000...? 1/8 или 0,125.
Итак, 128 * 1,125 = 144... аккуратно.

Как Экспонента 6, умноженная на Экспонент 24, становится Экспонентом 128?
Экспонента 6 - 4 0-10000001-ххххххххх
Экспонента 24 составляет 16 0-10000011-хххххххх

Каждый добавленный бит в экспоненте означает умножение его на 2
Таким образом, добавление немного к экспоненте 16 превращает 1000-0011 в 1000-0100 (32).
Нам нужно умножить 16 на 4, нам нужно добавить 2 бита, он превратится из 1000-0011 в 1000-0100 (первый бит), затем в 1000-0101 (второй бит) = 64.
Поскольку два мантиссы одинаковы, просто сдвиньте вправо на то же количество бит умножения экспоненты, 2 бита.
От 100000000... до 001000000...
Тогда, результат FP умножения "6" FP 0-10000001-1000000... на "24" FP 0-10000011-1000000... составляет 144 FP 0-10000101-0010000000... Exponent=128, умноженный на Mantissa 1.125 = 144

Если мантиссы разные, техника другая.

3. Дополнение с плавающей точкой (ваш вопрос)

Давайте сначала добавим простые числа, 6 и 8 = 14

6  = FP 0-10000001-1000000000...   
8  = FP 0-10000010-0000000000....   
14 = FP 0-10000010-1100000000.... 

Показатель 14 - это его нижнее целое число 2^n, то есть 8
Мантисса 14 - это доли 8, которые пытаются составить разницу, равную 6.
6/8 = 0,75, то есть состав 0,5 + 0,25
Итак, у Мантиссы 14 будет 11000000000....
Но как это происходит в двоичном виде?

Есть правила.
Во-первых, если первый экспонент меньше, чем второй, вычтите и возьмите абсолютную разницу (AD) и сдвиньте правые биты AD мантиссы с меньшим числом.

Экспонента 6 равна 10000001, экспонента 8 равна 10000010, разница равна 1, поэтому сдвиньте вправо на один бит Мантиссу из 6. Помните, что Мантисса 10000... равна 0,5, но в действительности равна 1,5, так что есть это невидимое целое число 1 Теперь вам нужно будет вставить эту 1 перед обеими мантиссами и сдвинуть правую мантиссу на 6 бит, разница.

Мантисса 6 с 1. = 1.10000000....
Мантисса 8 с 1. = 1.00000000....

Сдвиг вправо на 1 бит мантиссы 6 = 0.110000000
Мантисса 8 стиль такая же = 1.000000000

Теперь ДОБАВЬТЕ оба

0.1100000000    
1.0000000000   
--=--------- +   
1.1100000000   

Теперь важно, чтобы целое число 1. было точно в этой позиции, оно не могло быть слева (1x.xxxxx...) или ноль на его месте. Если это произойдет, есть методы, чтобы это исправить.

Теперь первоначальное целое число 1.xxx исчезает, оно все равно невидимо, а конечная Мантисса становится 110000000....

Новый экспонент будет больше, в этом случае показатель 8, 10000010.
Это правило, в то время как существует более высокий показатель степени или добавление мантисс не заканчивается целым числом больше 1, новый показатель совпадает с большим показателем. Если целое число результата сложения больше 1, оно будет равно 2 (10.xxxx~), затем просто добавьте 1 к экспоненте и сдвиньте на один бит вправо всю полученную мантиссу, чтобы она стала 1.xxxx~. Я положил пример в конце этого поста.

Теперь нужно просто составить Экспонент + Мантисса:
0-10000010-11000000... это FP 14, добавление 6 + 8.

Можете ли вы самостоятельно добавить свои собственные номера FP ниже?

Your number A = 5.1243230 FP 0-10000001-01000111111101001110100   
your number B = 2.2134523 FP 0-10000000-00011011010100100110100   
Result A + B  = 7.3377753 FP 0-....  

Экспонент = 4, 10000001
B Exponent = 2, 10000000
Разница между ними составляет 1
Итак, включите целое число 1. перед обеими мантиссами и сдвиньте вправо на 1 бит мантиссу B.

A = 1.01000111111101001110100   
B = 1.00011011010100100110100   

Сдвиг вправо на 1 бит B

A = 1.01000111111101001110100   
B = 0.10001101101010010011010 

Добавить оба

A = 1.01000111111101001110100   
B = 0.10001101101010010011010   
------------------------------ +   
    1.11010101100111100001110   

Целое число продолжается 1, отлично.
Теперь, исключите Integer и используйте эту Mantissa как результат
Объединить с большим экспонентом, равным 4, становится 10000001
FP = 0-10000001-1101010110011110000111000...
Это точно FP 7.3377753.

(Следующее было добавлено позже)
Пример добавления результирующей мантиссы с битами 10.xxxx ~

Предположим, вы добавляете 7 + 2 = 9

7 FP  0-10000001-11000~
2 FP  0-10000000-00000~

Экспонента 7 больше 2, а разница равна 1. Итак, давайте вставим неявное целое число 1 перед обеими мантиссами:

7 Mantissa:  1.110000~
2 mantissa:  1.000000~

Сдвиньте меньше (2) Mantissa вправо на один бит и добавьте оба.

7 Mantissa:  1.110000~
2 mantissa:  0.100000~
----------------------- +
Resulting:  10.010000~

Видите, есть 2 (биты 10.xxx~) как неявное целое число получающейся в результате мантиссы. Как сказано выше, эта неявная единица представляет один раз экспоненту. Теперь эти 2 (биты 10.xx~) представляют ДВУХ кратный показатель. Нам нужно перевести это дополнительное ОДНО время обратно в Экспоненту, добавив 1 к Экспоненте, то есть фактически умножив экспоненту на 2. Таким образом, нам нужно разделить Мантиссу на 2, сдвинув ее вправо на один бит.

Таким образом, получившаяся мантисса 10.010000~ станет 1.001000~
Окончательный FP будет 0-10000010-0010000~ это 9.

Легко, не правда ли?

Теперь давайте сделаем это в ассемблере AVR, используя только 4 регистра (32 бита) для каждой FP и еще 4 регистра для ответа (спасибо Питеру за обнаружение опечатки "3").

Как Майкл указал в комментарии, вы выполняете обычное старое целочисленное сложение для двух чисел. Алгоритм, который вы используете, предполагает, что ваши номера являются числами без знака или со знаком (два дополнения).

Вам нужно выяснить формат чисел с плавающей запятой, прежде чем вы действительно сможете подумать о том, как сложить их вместе. Число с плавающей запятой, как правило, будет представлено с показателем степени, мантиссой и, возможно, знаковым битом. Эти вещи могут зависеть от того, какой именно компилятор вы используете, и его настроек. Возможно, числа с плавающей запятой вашего компилятора соответствуют стандарту IEEE с плавающей запятой. Или, если эти плавающие числа не исходят от какого-либо компилятора, вы должны выяснить, какой код их производит, и изучить этот код, чтобы вы могли знать, в каком формате они находятся.

Вам следует пересмотреть, действительно ли вам нужны числа с плавающей точкой. Может быть, целые числа достаточно хороши.

Если вам действительно нужны числа с плавающей запятой, попробуйте скомпилировать программу на C, которая выполняет сложение с плавающей запятой, а затем посмотрите на список разборок в качестве отправной точки.

Большинство процессоров (включая AVR) не хранят информацию о типах, поэтому вы должны использовать разные инструкции для разных типов. add а также adc для целых чисел и различные инструкции для чисел с плавающей запятой.

Если процессор не имеет инструкций с плавающей запятой, то может быть подпрограмма библиотеки, чтобы сделать это для вас.


Интересно, что вы можете использовать одно и то же дополнение для подписанного (дополнение 2s) и неподписанного. Но не то же самое, умножить или разделить.

Я не предпочитаю преобразовывать его в шестнадцатеричный формат и затем делить на байты Хороший способ:

.EQU dwi = 5124323; define the constant
 LDI r21 ,LOW(dwi) ; The lowest 8 bits to R21
 LDI r22,BYTE2(dwi) ; bits 8 .. 15 to R22
 LDI r23 ,BYTE3(dwi) ; bits 16 .. 23 to R23
 LDI r24 ,BYTE4(dwi) ; bits 24 .. 31 to R24

и следующая переменная

.EQU dwj = 22134523

 LDI r25 ,LOW(dwj) ; 
 LDI r26 ,BYTE2(dwj)  
 LDI r27 ,BYTE3(dwj) 
 LDI r28 ,BYTE4(dwj) 

И чтобы исправить сложение, вы должны начать с младших байтов (в этом случае r21 and r25), но вы начали с наивысшего (порядок справа налево)

add r21, r25
adc r22, r26
adc r23, r27
adc r24, r28

мой рекомендует читать

Другие вопросы по тегам