Как вычесть IEEE 754 номера? - PullRequest
8 голосов
/ 07 января 2012

Как вычесть числа IEEE 754?

Например: 0,546875 - 32,875 ...

-> 0,546875 - это 0 01111110 10001100000000000000000 в IEEE-754

-> -32,875 - это 1 10000111 01000101111000000000000 в IEEE-754

Итак, как мне вычитать?Я знаю, что должен сделать оба показателя равными, но что мне делать после этого?2'Комплект мантиссы -32.875 и добавление 0.546875 мантиссы?

1 Ответ

21 голосов
/ 07 января 2012

Действительно ничем не отличается от того, что вы делаете с карандашом и бумагой. Хорошо, немного по-другому

123400 - 5432 = 1.234*10^5 - 5.432*10^3

доминирует большее число, сдвигайте мантиссу меньшего числа в область битов, пока показатели не совпадут

1.234*10^5 - 0.05432*10^5

затем выполнить вычитание с помощью мантиссы

1.234 - 0.05432 = 1.17968
1.17968 * 10^5

Затем нормализуем (что в данном случае так и есть)

Это было с основанием 10 чисел.

В поплавке IEEE, одинарная точность

123400 = 0x1E208 = 0b11110001000001000
11110001000001000.000...

нормализовать, что мы должны сместить десятичную точку на 16 знаков влево, чтобы

1.1110001000001000 * 2^16

Показатель степени смещен, поэтому мы добавляем 127 к 16 и получаем 143 = 0x8F. Это положительное число, поэтому знаковый бит равен 0, мы начинаем строить число с плавающей точкой IEEE, ведущее 1 перед тем, как десятичная дробь подразумевается и не используется с одинарной точностью, мы избавляемся от нее и сохраняем дробь

знаковый бит, показатель степени, мантисса

0 10001111 1110001000001000...
0100011111110001000001000...
0100 0111 1111 0001 0000 0100 0...
0x47F10400

И если вы пишете программу, чтобы увидеть, что такое компьютер 123400, вы получаете то же самое:

0x47F10400 123400.000000

Итак, мы знаем показатель и мантиссу для первого операнда '

Теперь второй операнд

5432 = 0x1538 = 0b0001010100111000

Нормализация, сдвиг десятичных разрядов влево на 12 бит

1010100111000.000
1.010100111000000 * 2^12

Показатель смещения добавить 127 и получить 139 = 0x8B = 0b10001011

Соберите все вместе

0 10001011 010100111000000
010001011010100111000000
0100 0101 1010 1001 1100 0000...
0x45A9C00

И компьютерная программа / компилятор выдает то же самое

0x45A9C000 5432.000000

Теперь, чтобы ответить на ваш вопрос. Используя составные части чисел с плавающей точкой, я восстановил подразумеваемую 1 здесь, потому что она нам нужна

0 10001111 111100010000010000000000 -  0 10001011 101010011100000000000000

Мы должны выстроить наши десятичные разряды, как в начальной школе, прежде чем мы сможем вычесть, поэтому в этом контексте вам нужно сдвинуть меньший номер экспоненты вправо, отбрасывая биты мантиссы с конца, пока показатели не совпадут

0 10001111 111100010000010000000000 -  0 10001011 101010011100000000000000
0 10001111 111100010000010000000000 -  0 10001100 010101001110000000000000
0 10001111 111100010000010000000000 -  0 10001101 001010100111000000000000
0 10001111 111100010000010000000000 -  0 10001110 000101010011100000000000
0 10001111 111100010000010000000000 -  0 10001111 000010101001110000000000

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

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

                         1
  111100010000010000000000
+ 111101010110001111111111
==========================

А теперь, как с бумагой и карандашом, давайте добавим

 1111000100000111111111111
  111100010000010000000000
+ 111101010110001111111111
==========================
  111001100110100000000000 

или сделайте это с гексагоном на своем калькуляторе

111100010000010000000000 = 1111 0001 0000 0100 0000 0000 = 0xF10400
111101010110001111111111 = 1111 0101 0110 0011 1111 1111 = 0xF563FF
0xF10400 + 0xF563FF + 1 = 0x1E66800
1111001100110100000000000 =1 1110 0110 0110 1000 0000 0000 = 0x1E66800

Немного о том, как работает аппаратное обеспечение, поскольку с помощью сумматора это было действительно вычитанием, мы также инвертируем бит выполнения (или на некоторых компьютерах они оставляют его как есть). Так что выполнение 1 - это хорошо, мы в основном отказываемся от него. Если бы это был унос с нуля, нам бы потребовалось больше работы. У нас нет выполнения, поэтому наш ответ действительно 0xE66800.

Очень быстро позволяет увидеть, что другой способ, вместо того, чтобы инвертировать и добавлять один, позволяет просто использовать калькулятор

111100010000010000000000 -  000010101001110000000000 = 
0xF10400 - 0x0A9C00 = 
0xE66800

Пытаясь визуализировать это, я, возможно, ухудшил ситуацию. Результат вычитания мантиссы - 111001100110100000000000 (0xE66800), в самом старшем бите не было движения, в результате мы получили 24-битное число с мсбитом 1. Нет нормализации. Для нормализации вам нужно сместить мантиссу влево или вправо, пока 24 бита не выровняются с самой значимой 1 в этой самой левой позиции, корректируя показатель степени для каждого битового сдвига.

Теперь, удалив 1. бит из ответа, мы соединяем части

0 10001111 11001100110100000000000
01000111111001100110100000000000
0100 0111 1110 0110 0110 1000 0000 0000
0x47E66800

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

#include <stdio.h>

union
{
    float f;
    unsigned int u;
} myun;


int main ( void )
{
    float a,b,c;

    a=123400;
    b=  5432;

    c=a-b;

    myun.f=a; printf("0x%08X %f\n",myun.u,myun.f);
    myun.f=b; printf("0x%08X %f\n",myun.u,myun.f);
    myun.f=c; printf("0x%08X %f\n",myun.u,myun.f);

    return(0);
}

И наш результат совпадает с выводом вышеуказанной программы, мы получили 0x47E66800, делая это вручную

0x47F10400 123400.000000
0x45A9C000 5432.000000
0x47E66800 117968.000000

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

Итак:

1) извлечь части, знак, экспоненту, мантиссу.

2) Выровняйте десятичные разряды, пожертвовав битами мантиссы от числа с наименьшим показателем, сдвиньте эту мантиссу вправо, пока показатели не совпадут

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

4) если результат равен нулю, тогда ваш ответ равен нулю, кодируйте значение IEEE для нулякак результат, в противном случае:

5) нормализуйте число, сдвиньте ответ вправо или влево (ответ может быть 25 бит из 24-битного сложения / вычитания, сложения / вычитания может иметь драматический сдвиг внормализуйте, либо один правый бит, либо много битов влево), пока не получите 24-битное число с выравниванием старшего значащего слева.24 бита для поплавка одинарной точности.Более правильный способ определения нормализации - сдвиг влево или вправо до тех пор, пока число не станет равным 1.что-то.если бы у вас было 0,001, вы бы сместились влево на 3, если бы у вас было 11,10, вы бы сместились вправо 1. сдвиг влево увеличивает ваш показатель степени, сдвиг вправо уменьшает его.Не отличается от того, когда мы преобразовали целое число в число с плавающей запятой выше.

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

Умножение и деление отличаются, вы спрашивали о вычитании, так что это все, что я рассмотрел.

...