Правильный способ рассуждать о числовой точности некоторого выражения состоит в следующем:
- Измерьте несоответствие результата относительно правильного значения в ULP (единица на последнем месте), представленная в 1960 г. У. Х. Кахан. Вы можете найти реализации C, Python и Mathematica здесь и узнать больше о здесь.
- Различайте два или более выражений на основе наихудшего случая, который они производят, а не по средней абсолютной ошибке, как это делается в других ответах, или по какой-либо другой произвольной метрике. Вот как строятся полиномы численной аппроксимации (алгоритм Ремеза), как анализируются реализации стандартных библиотечных методов (например, Intel atan2) , так далее...
Имея это в виду, version_1: sqrt(1 - x * x) и version_2: sqrt((1 - x) * (1 + x)) дают существенно разные результаты. Как показано на графике ниже, версия_1 демонстрирует катастрофическую производительность для x, близкого к 1, с ошибкой > 1_000_000 ulps, в то время как, с другой стороны, ошибка версии_2 ведет себя хорошо.
Вот почему я всегда рекомендую использовать версию_2, то есть использовать формулу квадратной разницы.
Код Python 3.6, создающий файл square_diff_error.csv:
from fractions import Fraction
from math import exp, fabs, sqrt
from random import random
from struct import pack, unpack
def ulp(x):
"""
Computing ULP of input double precision number x exploiting
lexicographic ordering property of positive IEEE-754 numbers.
The implementation correctly handles the special cases:
- ulp(NaN) = NaN
- ulp(-Inf) = Inf
- ulp(Inf) = Inf
Author: Hrvoje Abraham
Date: 11.12.2015
Revisions: 15.08.2017
26.11.2017
MIT License https://opensource.org/licenses/MIT
:param x: (float) float ULP will be calculated for
:returns: (float) the input float number ULP value
"""
# setting sign bit to 0, e.g. -0.0 becomes 0.0
t = abs(x)
# converting IEEE-754 64-bit format bit content to unsigned integer
ll = unpack('Q', pack('d', t))[0]
# computing first smaller integer, bigger in a case of ll=0 (t=0.0)
near_ll = abs(ll - 1)
# converting back to float, its value will be float nearest to t
near_t = unpack('d', pack('Q', near_ll))[0]
# abs takes care of case t=0.0
return abs(t - near_t)
with open('e:/square_diff_error.csv', 'w') as f:
for _ in range(100_000):
# nonlinear distribution of x in [0, 1] to produce more cases close to 1
k = 10
x = (exp(k) - exp(k * random())) / (exp(k) - 1)
fx = Fraction(x)
correct = sqrt(float(Fraction(1) - fx * fx))
version1 = sqrt(1.0 - x * x)
version2 = sqrt((1.0 - x) * (1.0 + x))
err1 = fabs(version1 - correct) / ulp(correct)
err2 = fabs(version2 - correct) / ulp(correct)
f.write(f'{x},{err1},{err2}\n')
Код Mathematica, который создает окончательный график:
data = Import["e:/square_diff_error.csv"];
err1 = {1 - #[[1]], #[[2]]} & /@ data;
err2 = {1 - #[[1]], #[[3]]} & /@ data;
ListLogLogPlot[{err1, err2}, PlotRange -> All, Axes -> False, Frame -> True,
FrameLabel -> {"1-x", "error [ULPs]"}, LabelStyle -> {FontSize -> 20}]
person
ahrvoje
schedule
24.11.2017
double
обычно имеет большую точность, чемfloat
. - person Pete Becker   schedule 13.08.2013long double
имеет еще большую точность, ноfloat
для меня быстрее! - person cschwan   schedule 13.08.20131.0f - cos_theta * cos_theta
являетсяfmaf(cos_theta, -cos_theta, 1.0f)
. Но если вас так заботит скорость, что вы не хотите использоватьdouble
для промежуточных вычислений, вам не следует использоватьfmaf
на процессоре, который не предоставляет ее в виде отдельной аппаратной инструкции, потому что эмулировать ее очень дорого (моя лучшая попробуй реализоватьfmaf
для процессора у которого его нет на ideone.com/kx7MXE , можно подтянуть немного, но это 1- использует двойники 2- будет продолжать делать много операций после затяжки) - person Pascal Cuoq   schedule 13.08.2013fmaf
, вероятно, слишком дорого, но тем не менее спасибо. Мой вопрос больше направлен на то, какую точность я могу получить бесплатно. - person cschwan   schedule 13.08.2013float
иdouble
выполняются с 80-битной архитектурой x86. В большинстве случаев люди используютfloat
для оптимизации пространства, а не скорости. - person Pete Becker   schedule 13.08.2013