Реализация вычитания с плавающей запятой

все, что я пытаюсь реализовать арифметическую библиотеку с плавающей запятой, и мне трудно понять алгоритм вычитания с плавающей запятой. Я успешно реализовал сложение и думал, что вычитание — это всего лишь его частный случай, но, похоже, я где-то ошибся. Я добавляю код сюда только для справки, он имеет много понятных функций, но я не ожидаю, что кто-то поймет его на 100%. В чем я хотел бы помочь, так это в алгоритме. Мы следуем тому же методу, что и при добавлении чисел с плавающей запятой, за исключением того, что когда мы добавляем мантиссы, мы преобразуем отрицательную единицу (тот, которую мы вычитаем) в дополнение до двух, а затем добавляем их?

Это то, что я делаю, но результат не правильный. Пусть и очень близко... но не то же самое. У кого-нибудь есть идеи? Заранее спасибо!

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

_float subFloat(_float f1,_float f2)
{
unsigned char diff;
_float result;

//first see whose exponent is greater
if(f1.float_parts.exponent > f2.float_parts.exponent)
{
    diff = f1.float_parts.exponent - f2.float_parts.exponent;

    //now shift f2's mantissa by the difference of their exponent to the right
    //adding the hidden bit
    f2.float_parts.mantissa = ((f2.float_parts.mantissa)>>1) | (0x01<<22);
    f2.float_parts.mantissa >>= (int)(diff);//was (diff-1)

    //also increase its exponent by the difference shifted
    f2.float_parts.exponent = f2.float_parts.exponent + diff;
}
else if(f1.float_parts.exponent < f2.float_parts.exponent)
{
    diff = f2.float_parts.exponent - f1.float_parts.exponent;
    result = f1;
    f1 = f2;        //swap them
    f2 = result;

    //now shift f2's mantissa by the difference of their exponent to the right
    //adding the hidden bit
    f2.float_parts.mantissa = ((f2.float_parts.mantissa)>>1) | (0x01<<22);
    f2.float_parts.mantissa >>= (int)(diff);

    //also increase its exponent by the difference shifted
    f2.float_parts.exponent = f2.float_parts.exponent + diff;
}
else//if the exponents were equal
  f2.float_parts.mantissa = ((f2.float_parts.mantissa)>>1) | (0x01<<22); //bring out the hidden bit




//getting two's complement of f2 mantissa
f2.float_parts.mantissa ^= 0x7FFFFF;
f2.float_parts.mantissa += 0x01;



result.float_parts.exponent = f1.float_parts.exponent;
result.float_parts.mantissa = (f1.float_parts.mantissa +f2.float_parts.mantissa)>>1;
                                                //gotta shift right by overflow bits

//normalization
if(manBitSet(result,1))
    result.float_parts.mantissa <<= 1;  //hide the hidden bit
else
    result.float_parts.exponent +=1;

return result;

}

person Community    schedule 26.02.2009    source источник


Ответы (2)


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

Нужно ли делать два дополнения и сложения, а не вычитания?

Если это не проблема, у меня проблемы с вашим алгоритмом. Прошло некоторое время с тех пор, как я сделал что-нибудь подобное. Не могли бы вы предоставить некоторые подробности? Точнее, что такое скрытый бит?

Мне кажется возможным, что обработка скрытого бита подходит для сложения, а не для вычитания. Может быть, вы должны установить его в мантиссе f1, а не в f2? Или отрицать мантиссу f1 вместо f2?

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

Изменить: хорошо, я посмотрел ссылки в вашем комментарии. Одна вещь, которую вы не можете сделать в предоставленном коде, - это нормализация. При сложении либо переполняются скрытые биты (сдвиг мантиссы влево, приращение экспоненты), либо нет. При вычитании произвольные части мантиссы могут быть равны нулю. В десятичном формате рассмотрите возможность добавления 0,5E1 и 0,50001E1; вы получите 1,00001E1, а если вы нормализуете, вы получите 0,10001E2. Вычитая 0,5E1 из 0,50001E1, вы получаете 0,00001E1. Затем вам нужно сдвинуть мантиссу влево и уменьшить показатель степени на столько, сколько потребуется, чтобы получить 0,1E-4.

person David Thornley    schedule 26.02.2009
comment
Спасибо за ваш ответ, ну, алгоритм - это обычный способ вычитания чисел с плавающей запятой. Проверьте это, например: cs.uaf.edu/~cs301 /notes/Chapter6/ и/или здесь: pages.cs.wisc.edu/~smoler/x86text/lect.notes/arith.flpt.html - person ; 26.02.2009
comment
Да, я был полностью согласен с нормализацией. Я его поправил, а также реализовал правильно (кажется) умножение и деление. Получил их все сейчас :), большое спасибо за вашу помощь. - person ; 27.02.2009

a-b == a+(-b), а унарный минус тривиален, поэтому я бы даже не стал заморачиваться с двоичным минусом.

person MSalters    schedule 26.02.2009
comment
Спасибо за ответ, но я не понимаю, что вы имеете в виду. - person ; 26.02.2009
comment
4 - 2 == 4 + (-2). Просто переверните бит знака. - person Hans Passant; 26.02.2009
comment
MSalters предлагает вам выполнять вычитание, отрицая одно из слагаемых, а затем добавляя как обычно. Судя по вашей строке в исходном посте, что вычитание - это всего лишь частный случай, я думаю, вы уже это знали... - person rmeador; 26.02.2009
comment
Нет, я думаю, вы неправильно поняли. Я не могу просто изменить бит знака и добавить. Когда я сказал, что мое дополнение работает, я имел в виду, что работает добавление плавающих элементов с одинаковым знаком. Вычитание — это добавление различных поплавков со знаком. Для этого и предназначен приведенный выше код, но он не дает ожидаемого результата. - person ; 26.02.2009