Как обнаружить переполнение и переполнение двойной точки с двойной точностью?
У меня есть следующие переменные:
double dblVar1;
double dblVar2;
Они могут иметь большие значения, но меньше double
max.
У меня есть различная арифметика над такими переменными, как сложение, умножение и мощность:
double dblVar3 = dblVar1 * dblVar2;
double dblVar4 = dblVar1 + dblVar2;
double dblVar5 = pow(dblVar1, 2);
Во всех вышеперечисленных я должен проверить переполнение и недополнение. Как я могу достичь этого в С++?
Ответы
Ответ 1
Многое зависит от контекста. Чтобы быть совершенно портативным, вы должны
перед выполнением операции, например. (для добавления):
if ( (a < 0.0) == (b < 0.0)
&& std::abs( b ) > std::numeric_limits<double>::max() - std::abs( a ) ) {
// Addition would overflow...
}
Аналогичная логика может использоваться для четырех основных операторов.
Если все машины, на которые вы нацелены, поддерживают IEEE (который
вероятно, случай, если вам не нужно рассматривать мэйнфреймы), вы
может просто выполнять операции, затем использовать isfinite
или isinf
on
результаты.
Для нижнего потока первый вопрос заключается в том, происходит ли постепенное переполнение
считается недостаточным или нет. Если нет, просто проверьте,
результаты равны нулю, а a != -b
- трюк. Если ты хочешь
для обнаружения постепенного перетока (который, вероятно, присутствует только в том случае, если
у вас есть IEEE), то вы можете использовать isnormal
— это будет
return false, если результаты соответствуют постепенному потоку.
(В отличие от переполнения, вы проверите для underflow после операции.)
Ответ 2
POSIX, C99, С++ 11 имеют <fenv.h>
(и <cfenv>
для С++ 11), которые имеют функции для проверки флажков исключений IEEE754 (которые не имеют ничего общего с исключениями С++, было бы слишком легко ):
int feclearexcept(int);
int fegetexceptflag(fexcept_t *, int);
int feraiseexcept(int);
int fesetexceptflag(const fexcept_t *, int);
int fetestexcept(int);
Флаг является битовым полем со следующими битами:
FE_DIVBYZERO
FE_INEXACT
FE_INVALID
FE_OVERFLOW
FE_UNDERFLOW
Итак, вы можете очистить их перед операциями, а затем проверить их после. Вам нужно будет проверить документацию о влиянии функций библиотеки на них.
Ответ 3
С достойным компилятором (который поддерживает новейший стандарт С++) вы можете использовать эти функции:
#include <cfenv>
#include <iostream>
int main() {
std::feclearexcept(FE_OVERFLOW);
std::feclearexcept(FE_UNDERFLOW);
double overflowing_var = 1000;
double underflowing_var = 0.01;
std::cout << "Overflow flag before: " << (bool)std::fetestexcept(FE_OVERFLOW) << std::endl;
std::cout << "Underflow flag before: " << (bool)std::fetestexcept(FE_UNDERFLOW) << std::endl;
for(int i = 0; i < 20; ++i) {
overflowing_var *= overflowing_var;
underflowing_var *= underflowing_var;
}
std::cout << "Overflow flag after: " << (bool)std::fetestexcept(FE_OVERFLOW) << std::endl;
std::cout << "Underflow flag after: " << (bool)std::fetestexcept(FE_UNDERFLOW) << std::endl;
}
/** Output:
Overflow flag before: 0
Underflow flag before: 0
Overflow flag after: 1
Underflow flag after: 1
*/
Ответ 4
ISO C99 определяет функции для запроса и управления словом состояния с плавающей запятой. Вы можете использовать эти функции для проверки исключенных исключений, когда это удобно, а не беспокоиться о них в середине расчета.
Он обеспечивает
FE_INEXACT
FE_DIVBYZERO
FE_UNDERFLOW
FE_OVERFLOW
FE_INVALID
Например
{
double f;
int raised;
feclearexcept (FE_ALL_EXCEPT);
f = compute ();
raised = fetestexcept (FE_OVERFLOW | FE_INVALID);
if (raised & FE_OVERFLOW) { /* ... */ }
if (raised & FE_INVALID) { /* ... */ }
/* ... */
}
http://www.gnu.org/software/libc/manual/html_node/Status-bit-operations.html