2015-08-25 8 views
16

Я должен проверить неравенство, содержащее квадратные корни. Чтобы избежать неверных результатов из-за плавающей точкой неточность и округление, я использую std::nextafter() получить верхний/нижний предел:C++ sqrt гарантированная точность, верхняя/нижняя граница

#include <cfloat> // DBL_MAX 
#include <cmath> // std::nextafter, std::sqrt 

double x = 42.0; //just an example number 
double y = std::nextafter(std::sqrt(x), DBL_MAX); 

а) Является ли y*y >= x гарантируемой с помощью GCC компилятор?

b) Будет ли эта работа для других операций, таких как + - * / или даже std::cos() и std::acos()?

c) Есть ли лучшие способы получить верхнюю/нижнюю границу?

Обновление: I read это не гарантируется стандартом C++, но должно работать в соответствии с IEEE-754. Будет ли это работать с компилятором GCC?

+1

Вы запрашиваете этот конкретный случай или вообще для любого числа с плавающей запятой? NaN немедленно нарушает любую логику. –

+3

Насколько я могу судить, стандарт не дает никаких гарантий на реализацию '' std :: sqrt'', поэтому я думаю, что ответ на этот вопрос будет зависимым от реализации. –

+0

@MarkB Я имею в виду любое (положительное) число с плавающей запятой, 42 - просто пример. – Lux

ответ

2

Для страницы GCC this предлагается, что она будет работать, если вы используете встроенную функцию sqrt GCC __builtin_sqrt.

Кроме того такое поведение будет зависеть от того, как вы скомпилировать код и машину, что он запускается на

  1. Если процессор поддерживает SSE2, то вы должны скомпилировать код с флагами -mfpmath=sse -msse2, чтобы гарантировать, что все операции с плавающей запятой выполняются с использованием регистров SSE.

  2. Если процессор не поддерживает SSE2, то вы должны использовать long double типа для значений с плавающей точкой и компилировать с флагом -ffloat-store, чтобы заставить GCC не использовать регистры для хранения значений с плавающей точкой (вы будете иметь производительность штраф за это)

3

В целом операции с плавающей запятой приведут к ошибке ULP. IEEE 754 требует, чтобы результаты для большинства операций были корректными с точностью до 0,5 ULP, но могут накапливаться ошибки, что означает, что результат может быть не в пределах одного ULP от точного результата. Существуют также пределы точности, поэтому в зависимости от количества цифр в результирующих значениях вы также можете не работать со значениями одинаковых величин. Трансцендентальные функции также представляют собой notorious для введения ошибки в вычисления.

Однако, если вы используете GNU glibc, SQRT будет правильным в пределах 0,5 ULP (округленно), так что вы конкретный пример будет работать (без учета NaN, +/-0, +/-Inf). Хотя, вероятно, лучше определить некоторый эпсилон в качестве допустимости ошибок и использовать его в качестве вашей привязки. Для exmaple,

bool gt(double a, double b, double eps) { 

    return (a > b - eps); 
} 

В зависимости от уровня точности вам необходимо в расчетах, вы можете также использовать long double вместо.

Таким образом, чтобы ответить на ваши вопросы ...

а) Является ли у * у> = х гарантируется с помощью GCC компилятор?

Предполагая, что вы используете встроенные GNU glibc или SSE2, да.

b) Будет ли это работать для других операций, таких как + - */или даже std :: cos() и std :: acos()?

Предполагая, что вы используете GNU glibc и одну операцию, да. Хотя некоторые трансценденталы не гарантированы правильно округлены.

c) Есть ли лучшие способы получить верхнюю/нижнюю границу?

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

0

Относительно

там с) более эффективными способами, чтобы получить верхние/нижние границы?

Другой способ заключается в использовании другой rounding mode, т.е. FE_UPWARD или FE_DOWNWARD вместо стандартного FE_TONEAREST. См. https://stackoverflow.com/a/6867722 Это может быть slower, но является лучшей верхней/нижней границей.

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