1

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

Если я отлиты двойников из междунар так:

int x = random(); 
double dx = (double) x; 

И скажем переменные у, г, ду и дг в том же формате.

Тогда бы такие операции, как:

(dx + dy) + dz == dx + (dy + dz) 
(dx * dy) * dz == dx * (dy * dz) 

ассоциативными? Я знаю, что если у нас есть дробные представления, то это не будет ассоциативно, потому что некоторая точность будет потеряна из-за округления в зависимости от того, какие операнды добавляют/умножают друг друга. Однако, поскольку они выбрасываются из ints, я чувствую, что точность не будет проблемой и что они могут быть ассоциативными?

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

dx/dx == dz/dz 

I посмотрел это онлайн, и я читал в некоторых областях, таких как операция, как 3/3, может дать 0,999 ... 9, но не было достаточной информации, чтобы объяснить, как это произошло, или если это будет отличаться от других операций деления.

+0

Хороший компилятор должен распознать dx/dx и фактически не выдавать инструкции разделения. –

+0

Вы можете точно представить любое значение до 2^53 + 1 как двойное. Кроме того, вы сталкиваетесь с ошибками округления, даже для целочисленных типов. http://stackoverflow.com/a/1848762/141172 –

+0

, вы, возможно, помните, что из ваших школьных дней число, разделенное само по себе, равно 1, поэтому сравнение «может» работать. Однако, как правило, число с плавающей запятой никогда не должно сравниваться с использованием «==» вместо этого, получить абсолютные значения, получить разницу, проверить, чтобы разница была меньше, чем какая-то трехосадка – user3629249

ответ

1

Предполагая, что int не более 32-бит, а double следует за IEEE-754. double может хранить целое значение не более 2 точно.


В случае добавления:

(dx + dy) + dz == dx + (dy + dz) 

Обе стороны == будут иметь свои точные значения, так что ассоциативная.


В то время как в случае умножения:

(dx * dy) * dz == dx * (dy * dz) 

Вполне возможно, что значение более 2 , поэтому они не гарантированно равны.

+0

Просто уточняю, поэтому причина, по которой максимум может хранить двойной, равна 2^53 из-за 52-битной мантиссы + подразумеваемой ведущей 1? –

+0

Чтобы быть ясным: 'double' может хранить все целые числа точно' -pow (2,53) ... + pow (2,53) '--inclusive - точно так же, как 54-битное целое число со знаком или' int54_t'. – chux

1

Вы должны понимать, что числа с плавающей запятой обычно представляются в виде знакового бита, мантиссы с фиксированной точкой (из 52 бит с подразумеваемым ведущим для IEEE 64-bit doubles) и двоичного показателя (11 бит для удваивания IEEE). Вы можете представить экспонента как «квант» математических единиц для заданного значения.

Добавка должна быть ассоциативным, если суммы все вписываться в мантиссы без показателя будет выше 2 == 1. Если random() производит 32-битовые целые числа, сумма таких как (dx + dy) + dz будет соответствовать, и добавление будет ассоциативным.

В случае умножения легко видеть, что произведение двух 32-битных чисел может превышать 53 бит, поэтому экспоненте может потребоваться превысить 1 для мантиссы, чтобы содержать величину результата, поэтому ассоциативность терпит неудачу.

Для деления в частном случае dx/dx компилятор может заменить выражение константой 1.0 (возможно, после нулевой проверки).

+0

экспонент также имеет встроенное смещение (я думаю, 256), чтобы иметь как положительные, так и отрицательные показатели без необходимости потреблять бит для знака – user3629249

+0

Смещение экспоненты (обычно называемое смещением) составляет половину диапазона экспонентов. Для IEEE double с 11 бит экспоненты смещение равно 1023. Для одиночного IEEE это 127. –

Смежные вопросы