(1 + SQRT (2))^2 = 3 + 2*SQRT (2) удовлетворен в арифметике с плавающей темой?

StackOverflow https://stackoverflow.com/questions/1995963

Вопрос

В математике идентичность (1 + sqrt(2))^2 = 3 + 2*sqrt(2) Справедливо. Но в плавающей запятой (IEEE 754, с использованием единой точки, т. 32 бита), это не так, как sqrt(2) не имеет точного представления в двоичном языке.

Так же использует аппроксимированное значение sqrt(2) Предоставить разные результаты для слева и правой стороны? Если да, то почему? Существует ли квадрат аппроксимированного значения значительно снижает точность?

Какое из эквивалентных выражений тогда дает наиболее точный результат?

Это было полезно?

Решение

Эта идентичность сохраняется, когда вычисляется, как написано в двойной точке IEEE-754. Вот почему:

Квадратный корень двух правильно округлена до двойной точности:

sqrt(2) = 0x1.6a09e667f3bcd * 2^0

(Я использую Hexadecimal здесь, потому что представления аккуратные, а перевод в формат IEEE754 намного проще). Умножение на два точнее в двоичной точке с плавающей точкой, если переполнение не происходит, как в этом случае здесь, так что:

2*sqrt(2) = 0x1.6a09e667f3bcd * 2^1

Когда мы добавим три, мы получаем:

3 + 2*sqrt(2) = 0x1.7504f333f9de68 * 2^2

Это, однако, не является представительным номером с двойной задачей (оно слишком широко), поэтому результат округлен до ближайшего представительного числа. Получается, что это значение точно на полпути между двумя представительными числами, поэтому мы выбираем тот, который с ноль ноль:

3 + 2*sqrt(2) = 0x1.7504f333f9de6 * 2^2

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

1 + sqrt(2) = 0x1.3504f333f9de68 * 2^1

Это также точный случай на полпути между репрезентативными числами с двойной задачей, и опять же он округлен до ближайшего «даже» представительного числа:

1 + sqrt(2) = 0x1.3504f333f9de6 * 2^1

Когда это значение квадрат, результатом является:

(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de599cacbc97eaa4 * 2^2

Который также не является представительным номером с двойной задачей. Этот нет Точный на полпути, так что он просто переходит к ближайшему представительному числу, то есть:

(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de6 * 2^2

Резюме: Вычисление этого значения двумя разными способами вызывает две разные последовательности округления, но конечный результат одинаков. Однако мы смотрели только на вычисление с двойной точностью; Это может быть не так, когда вычисление выполняется с использованием различных типов арифметических средств.

В целом, однако, выражение 3 + 2*sqrt(2) Следует ожидать, что будет более точным (в тех случаях, когда они различаются), потому что он несет только два округления (квадратный корень и добавление) для любого бинарного типа IEEE-754, тогда как (1 + sqrt(2))*(1 + sqrt(2)) входит три округа (квадратный корень, добавление и умножение). Следует также отметить, что разница между ними будет не более одной или двумя битами и, вероятно, неравномерна для ваших целей.

Другие советы

С тех пор 0.1 + 0.2 != 0.3 Вы не должны рассчитывать на такие сложные равенства для удержания для ограниченных чисел с плавающей запятой.

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

Так что использование аппроксимированного значения SQRT (2) дает различные результаты для слева и правой стороны? Если да, то почему?

Математически это равенство работает только из -за точной взаимосвязи между этими числами (оно связано с длиной сторон треугольника). Если вы добавляете нечеткость в форме неточного представления, равенство больше не верно. Равенство - это двоичное предложение, поэтому вопрос больше не «какая сторона правильная», а скорее «это отношение вообще прав?». И ответ: «Нет, это больше не так».

Существует ли квадрат аппроксимированного значения значительно снижает точность?

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

Обычно я использую [(1 + SQRT (2))^2] - [3 + 2*SQRT (2)] <0,00001 для проверки равенства в таких условиях (конечно, для некоторых случаев я игнорирую это использование)

Есть ли способ лучше?

Комментарии благодарны :)

Остерегайтесь, ребята, полагаясь только на абсолютную разницу, может вызвать проблемы. Он работает для небольших чисел около 1, у которых достаточно десятичных точек, чтобы иметь возможность отличаться на 1E-5 или то, что вы используете. Но подумайте о больших числах. Их цифры должны храниться в ограниченном пространстве (мантисса). И хранятся только самые значимые цифры. Что это значит? Что нет места для хранения цифр, которые позволят измерять различия, такие как 1E-5!

Завершая, лучше использовать абсолютное и относительное сравнение одновременно.

bool equal(float a, float b)
{
    if (abs(a - b) < eps)
        return true;
    if (abs(a - b) / max(abs(a), abs(b)) < eps)
        return true;
    return false;
} 

Посмотрите на светлую сторону: если вы повторно проработаете это уравнение, чтобы удалить sqrtS, тогда, поскольку вы будете иметь дело с целыми числами разумного размера, уравнение будет точным в плавающей запятой;)

Неточности обычно связаны с числами, которые требуют десятичных фракций (кроме полномочий .5 и .2) для представления.


Чтобы ответить на другую часть вашего вопроса: нет, представление sqrt(2) действительно одинаково с обеих сторон. Ошибки (и различия) не введены до тех пор, пока вы не начнете применять (разные) операции к тому же числу с обеих сторон: добавление 1 против умножения на 2 и т. Д.

Человек, который определил компаратора равенства для плавания в C ++, должен быть снят:>. Многие разумные языки (например, SML) не имеют оператора сравнения для поплавок. Обычно я использую следующий код:

template < typename T >
inline bool equals( T x, T y, T precision = std::numeric_limits<T>::epsilon() ) 
{
    return abs( x - y ) <= precision;
}

Примечание: ABS также является шаблонной функцией здесь, Epsilon по умолчанию хранится снаружи. Равенство в сравнении предназначено для моих целей.

В двойной точности, (1 + sqrt(2))^2 = 3 + 2*sqrt(2) Кажется, держится. Видеть C код.

Я собираюсь выбросить еще одну идею -

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

Но также верно, что точное равенство реальных чисел является бессмысленной концепцией в нашей физической реальности.

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

Когда вы измеряете длину карандаша с линейкой, вы получаете длину до ближайшего 16 -го дюйма. Когда вы измеряете его с помощью суппортов, вы получаете длину до 1000 -го дюйма. Измерения в реальном мире всегда включают в себя такого рода округление. Когда вы имитируете измерения реального мира в компьютерной программе, вам нужно сделать то же самое.

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

SQRT (2) не имеет точного представления в двоичном языке.

SQRT (2) не имеет точного представления в десятичной, шестигранке или в любой другой системе Base-N; Это иррациональное число.

Единственным точным представлением SQRT (2) является SQRT (2). Или, как решение уравнения x2 = 2.

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

Так что использование аппроксимированного значения SQRT (2) дает различные результаты для слева и правой стороны? Если да, то почему? Существует ли квадрат аппроксимированного значения значительно снижает точность?

Добавление и умножение имеют оба приближения ошибок. Умножение эмпирическое, особенно когда оно вложено.

Следующее не является точным представлением, но оно помогает понять мою точку зрения:

example of addition:
(float1 * float2 + float3)
float1 * float2 + float3 + mult_approximation + add_approximation

example multiplication
(float1 * (float2 + float3))
(float1 * (float2 + float3 + add_apporiximation)
float1 * (float2 + float3) + add_approximation * float1 + mult_approximation

Это потому, что представление непрерывных (бесконечных) функций, таких как SQRT (x), не может быть выполнено на дискретной (конечной) состоянии состояния. Вместо этого непрерывные функции переводятся в дискретные функции с помощью расширения серии Тейлора от 0 до N, где n - самое высокое число, которое вы можете представить (в данном случае 2^32). Поскольку вы не можете перенести сумму от 0 до бесконечности на компьютере, у вас останется остальные ошибки. Эта ошибка может быть рассчитана, чтобы вы могли определить, насколько близко к вашей дискретной функции к непрерывной функции.

Для получения дополнительной информации и симпатичных представлений об уравнениях:http://en.wikipedia.org/wiki/taylor_series

В целом две стороны дадут вам разные результаты. Математика с плавающей запятой не удовлетворяет коммутативной и связанной с ним собственности. Существует ряд факторов, включая опции компилятора и аппаратное обеспечение.

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

Квадрат не должен значительно влиять на результаты в вашем случае.

Удивительно, но если по какой-то причине вам нужно точное представление нерациональных чисел (подсказка: вы, вероятно, нет), есть что-то, что вы можете сделать: продолжение фракций арифметические. Идея идет в 1972 году и связана с супер -хакером Биллом Госпера - Google It Up. Кстати, более продвинутые аспекты этой идеи являются вопросом текущих исследований по математике; Смотрите, например Эта бумага.

В целом, операции с плавающей запятой точны до FLT_EPSILON, т.е. в пределах наименее значимого бита, что для 32-битных поплавок IEEE составляет 2−23.

Также см: Разве двойная точность 15 цифр в C#?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top