Разница в точности с делением с плавающей запятой против умножения - PullRequest
9 голосов
/ 26 июня 2011

Есть ли разница между этим:

average = (x1+x2)/2;
deviation1 = x1 -average;
deviation2 = x2 -average;
variance = deviation1*deviation1 + deviation2*deviation2;

и этим:

average2 = (x1+x2);
deviation1 = 2*x1 -average2;
deviation2 = 2*x2 -average2;
variance = (deviation1*deviation1 + deviation2*deviation2) / 4;

Обратите внимание, что во второй версии я пытаюсь отложить деление как можно позже.Увеличивает ли вторая версия [задержка делений] точность в целом?

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

Кстати, я спрашиваю о делении в целом, а не только на 2 или степень 2как они сводятся к простым сдвигам в представлении IEEE 754.Я взял деление на 2, чтобы проиллюстрировать проблему на очень простом примере.

Ответы [ 4 ]

3 голосов
/ 26 июня 2011

От этого нечего извлечь. Вы только изменяете масштаб, но вы не получите более значимых цифр в своих вычислениях.

В статье Википедии о дисперсии на высоком уровне подробно объясняются некоторые варианты вычисления дисперсии.

2 голосов
/ 26 июня 2011

Вы не получаете точность от этого, поскольку IEEE754 (который вероятно , который вы используете под крышками) дает вам одинаковую точность (количество бит) в любом масштабе, с которым вы работаете. Например, 3.14159 x 10 7 будет иметь такую ​​же точность, как 3.14159 x 10 10 .

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

1 голос
/ 26 июня 2011

Лучший способ ответить на ваш вопрос - запустить тесты (как случайно распределенные, так и на основе диапазона?) И посмотреть, отличаются ли результирующие числа вообще в двоичном представлении.

Обратите внимание, что одна проблема, с которой вы столкнетесь, заключается в том, что ваши функции не будут работать со значением > MAX_INT/2 из-за способа, которым вы кодируете среднее значение.

avg = (x1+x2)/2        # clobbers numbers > MAX_INT/2
avg = 0.5*x1 + 0.5*x2  # no clobbering

Это почти наверняка не проблема, если только вы не пишете библиотеку уровня языка. А если большинство ваших чисел маленькие, это может вообще не иметь значения? На самом деле, это, вероятно, не стоит рассматривать, так как значение дисперсии будет превышать MAX_INT, поскольку это по сути квадратная величина; Я бы сказал, что вы можете использовать стандартное отклонение, но никто этого не делает.

Здесь я делаю некоторые эксперименты на python (которые, я думаю, поддерживают IEEE, что бы это ни было, в силу вероятности делегирования математики библиотекам C ...):

>>> def compare(numer, denom):
...     assert ((numer/denom)*2).hex()==((2*numer)/denom).hex()

>>> [compare(a,b) for a,b in product(range(1,100),range(1,100))]

Нет проблем, я думаю, потому что деление и умножение на 2 хорошо представимо в двоичном виде. Однако попробуйте умножение и деление на 3:

>>> def compare(numer, denom):
...     assert ((numer/denom)*3).hex()==((3*numer)/denom).hex(), '...'

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
  File "<stdin>", line 2, in compare
AssertionError: 0x1.3333333333334p-1!=0x1.3333333333333p-1

Возможно, это имеет большое значение? Возможно, если вы работаете с очень маленькими числами (в этом случае вы можете использовать логарифмическая арифметика ). Однако, если вы работаете с большими числами (редко по вероятности) и задерживаете деление, вы, как я уже упоминал, будете переполнены рисками, но, что еще хуже, рискует ошибками из-за трудно читаемого кода .

1 голос
/ 26 июня 2011

Я должен согласиться с Дэвидом Хеффернаном, это не даст вам более высокой точности.

Причина в том, как хранятся значения с плавающей запятой.У вас есть несколько битов, представляющих значащие цифры, и несколько битов, представляющих показатель степени (например, 3.1714x10-12).Биты для значащих цифр всегда будут одинаковыми, независимо от того, насколько велико ваше число - это означает, что в итоге результат не будет отличаться.

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

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

...