Что именно происходит при умножении двойного значения на 10 - PullRequest
1 голос
/ 17 января 2020

Мне недавно стало интересно, как умножить числа с плавающей запятой.
Предположим, у меня есть число, например 3.1415 с гарантированной точностью 3-ди git.
Теперь я умножил это значение на 10, и я получаю 31.415X, где X - это ди git, который я не могу определить из-за ограниченной точности.

Теперь, могу ли я быть уверен, что пять разнесены до точных цифр? Если число окажется точным с точностью до 3 цифр, я бы не ожидал, что эти пять всегда будут появляться там, но после изучения многих случаев в c ++ я заметил, что это всегда происходит.

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

Я задаю этот вопрос, потому что я хотел создать функцию, которая вычисляет, насколько точен тип. Я придумал что-то вроде этого:

template <typename T>
unsigned accuracy(){
        unsigned acc = 0;
        T num = (T)1/(T)3;
        while((unsigned)(num *= 10) == 3){
                acc++;
                num -= 3;
        }
        return acc;
}

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

Ответы [ 3 ]

2 голосов
/ 17 января 2020

Предположим, что для одинарной точности 3.1415 составляет

0x40490E56

в формате IEEE 754, который является очень популярным, но не единственным используемым форматом.

01000000010010010000111001010110 0 10000000 10010010000111001010110

, поэтому двоичная часть равна 1.10010010000111001010110

110010010000111001010110 1100 1001 0000 1110 0101 0110 0xC90E56 * 10 = 0x7DA8F5 C

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

01111.10110101000111101011100

, чтобы перейти в формат IEEE 754, его необходимо перевести в формат 1.mantissa, так что это сдвиг 3

1.11110110101000111101011

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

1.11110110101000111101100

0111 1011 0101 0001 1110 1100

0x7BA1E C

теперь, если Я уже вычислил ответ:

0x41FB51E C

0 10000011 11110110101000111101100

мы переместили точку 3, и показатель степени отражает то, что мантисса соответствует тому, что мы вычислили. мы потеряли один из ненулевых исходных битов справа, но слишком ли много потерь?

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

2 голосов
/ 17 января 2020

Я поговорю конкретно о двойниках IEEE754, поскольку, как я думаю, вы просите.

Двойники определяются как знаковый бит, 11-битная экспонента и 52-битная мантисса , которые объединяются в 64-битное значение:

sign|exponent|mantissa

Биты экспоненты хранятся в смещенном формате, что означает, что мы сохраняем фактическую экспоненту +1023 (для двойного). Показатель «все нули» и показатель «все единицы» являются особыми, поэтому в конечном итоге мы можем представить показатель в диапазоне от 2 ^ -1022 до 2 ^ + 1023

Это распространенное заблуждение, что целочисленные значения не могут быть представлены точно в виде двойных чисел, но мы можем фактически сохранить любое целое число в [0,2 ^ 53) точно , правильно установив мантиссу и экспоненту, фактически диапазон [2 ^ 52,2 ^ 53) может только хранить целочисленные значения в этом диапазоне. Таким образом, 10 легко хранить точно в двойном числе.

Когда дело доходит до умножения двойных чисел, у нас фактически есть два числа этой формы:

A = (-1)^sA*mA*2^(eA-1023)
B = (-1)^sB*mB*2^(eB-1023)

Где sA, mA, eA - знак, мантисса и показатель степени для A (и аналогично для B).

Если мы умножим их:

A*B = (-1)^(sA+sB)*(mA*mB)*2^((eA-1023)+(eB-1023))

Мы можем видеть, что мы просто sum экспоненты, а затем умножить мантиссы. На самом деле это не плохо для точности! Мы можем переполнить биты экспоненты (и, таким образом, получить бесконечность), но в остальном нам просто нужно округлить промежуточный результат мантиссы до 52 бит, но в худшем случае это изменит только младший значащий бит в новой мантиссе.

В конечном итоге ошибка, которую вы увидите, будет пропорциональна величине результата. Но у двойников есть ошибка, пропорциональная их величине в любом случае , так что это действительно настолько безопасно, насколько мы можем получить. Способ приблизить ошибку в вашем числе как | величина | * 2 ^ -53. В вашем случае, поскольку 10 является точным , единственная ошибка возникнет в представлении числа pi. Он будет иметь ошибку ~ 2 ^ -51, и, следовательно, результат будет также.

Как правило, я считаю, что двойные числа имеют ~ 15 цифр с точностью десятичной при мышлении о проблемах точности.

1 голос
/ 17 января 2020

Теперь, могу ли я быть уверен, что пять разнесены до точных цифр?

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

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

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

, поскольку числа с плавающей запятой хранятся в двоичном формате

Хотя это очень распространено, это не всегда верно. Некоторые системы, например, используют base-10.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...