2012-05-29 2 views
10

Предположим, что t, a, b все двойные (IEEE Std 754) переменные, и оба значения a, b НЕ NaN (но может быть Inf). После t = a - b, у меня обязательно есть a == b + t?Плавающая точка IEEE Std 754: пусть t: = a - b, гарантирует ли стандарт, что == b + t?

+0

Я считаю, что результат нижнего потока был бы неопределенным, и так будет переполнение во втором выражении, так что нет. Если кто-то сможет это подтвердить, было бы хорошо. – chris

+0

Ах, я думаю, этот тип подтверждает, что переполнение не определено для с плавающей запятой: 'As с любым другим арифметическим переполнением, если результат не установлен в указанном пространстве, поведение не определено.' – chris

+3

В реализации C в соответствии с IEEE 754, нет UB для любой арифметики с плавающей точкой. Все результаты строго определены. –

ответ

25

Абсолютно нет. Один очевидный случай - a=DBL_MAX, b=-DBL_MAX. Затем t=INFINITY, поэтому b+t также является INFINITY.

Что может быть более неожиданным, так это случаи, когда это происходит без переполнения. В принципе, все они имеют форму, в которой нет a-b. Например, если a является DBL_EPSILON/4 и b является -1, a-b является 1 (предполагается, что режим округления по умолчанию), а затем a-b+b 0.

Причина я упоминаю этот второй пример в том, что это канонический способ заставить округление до определенной точности в арифметике IEEE. Например, если у вас есть число в диапазоне [0,1] и вы хотите округлить его до 4 бит точности, вы должны добавить, а затем вычесть 0x1p49.

+1

Второй пример замечательный, поскольку он не несет Inf или NaN. Большое спасибо. – updogliu

+1

Возможно, вы захотите уточнить константу '0x1p49', последний раз, когда я посмотрел шестнадцатеричные цифры, пробег от 0 до F;) – MSalters

+8

@MSalters:« 0x1p49 »- это шестнадцатеричная плавающая запятая, как определено в стандарте C. Формат «0x» «p» , где представляет собой шестнадцатеричную цифру, необязательно включающую период, и представляет собой десятичную цифру, необязательно включающую знак. База для экспоненты равна двум, поэтому 0x1p49 равно 2 ** 49. 0x1p-4 будет 1/16, а 0x1.23p8 будет (1 + 2/16 + 3/256) * 2 ** 8 = 291. Шестнадцатеричная плавающая точка обеспечивает формат, который легко людям и компиляторам конвертировать до и из двоичных кодов с плавающей запятой без проблем округления. –

1

В процессе выполнения первой операции бит мог быть потерян с нижнего конца результата. Итак, один вопрос: будет ли вторая операция точно воспроизвести эти потери? Я не совсем так думал.

Но, конечно, первая операция могла бы переполняться до +/- бесконечности, делая второй сравнивать неравномерно.

(И, конечно же, в общем случае, используя == для значений с плавающей точкой почти всегда является ошибкой.)

+1

Только с помощью аргумента подсчета вторая операция не может вернуть то, что было потеряно. Если это возможно, вы будете хранить больше бит информации в 't', чем количество бит в' t' ... –

+0

@R - Да. Интуитивно известно, что это не сработает, из-за того, что вы говорите, но найти примеры лучше «доказательство», чем обращение к эзотерическому правилу, независимо от того, насколько он действителен. –

-3

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

Рассмотрим этот код:

float a = 0.003f; 
float b = 10000000.0f; 
float t = a - b; 
float x = b + t; 

Запуск на Visual Studio 2010, вы получаете t==-10000000.0f, и поэтому x==0.

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

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

+5

Мне никогда не нравился совет «сравните абсолютную ценность разницы». Можно получить ограничения на ошибки ([Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) является хорошим начало), и нужно подумать о том, чего пытаешься добиться при сравнении, прежде чем вслепую переключиться на какую-то произвольную границу. –

+8

Есть много вещей, которые гарантируются при использовании поплавков IEEE-754. Это не один из них. –

+0

Есть много гарантий при использовании поплавков IEEE, и бывают случаи, когда сравнение для равенства не только разумно, но и существенно. Математика с плавающей запятой определенно сложна, но она не случайна и не злонамерена. Вот пример из моего блога, когда критическое значение имеет равенство с плавающей точкой: https://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/ –

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