编程时,可能会从您的代码中产生某些错误条件。如果编译器捕获任何编译错误,它将停止编译并告诉您错误所在位置。编译器还可能会对某些构造发出警告,这些构造可能会或可能不会在运行期间给您带来问题。
以下是您作为程序员在执行浮点算术(任何加法、减法、乘法和除法)时应注意的一些浮点条件,即(QNAN 和 SNAN)条件和错误。
正数 | 负数 | 意义 |
1.#INF | -1.#INF | Infinity无穷大 |
1.#SNAN | -1.#SNAN | Signalling NaN |
1.#QNAN | -1.#QNAN | Quiet NaN |
1.#IND | -1.#IND | Indefinite / Indeterminite NaN 不是一个数,未定值 |
#NaN
C++中代表不是数字。它是一种用浮点描绘未定义或未指定值的价值。NAN 的概念于 1985 年由 IEEE 754 浮点标准引入,其中还给出了无穷大的概念。NaN
#QNaN和#SNaN
有两种,安静和信号。两者非常相似,在大多数情况下都是相同的。与MSVC,信号导航(sNaN)默认情况下是安静的导航。他们不会提出任何硬件例外(因为所有的浮点例外),你不会注意到任何错误,直到打印出结果,但模拟惨败。
#INF
C++表示"无穷大"。由于浮点数的有限性(对于双精度浮点数),无限以有限的值表示。INF
32-bit
64-bit
当由此产生的数字溢出或溢出浮点数的容量时,就会出现此类错误条件/值。换句话说,值太大或太小,不能表示为浮点值。
因此,该值显示为。#INF
- 1.#INF-如果结果是一个太大的正数。
- -1.#INF-如果结果是一个太大的负数。
最简单的使用方式得到一个无限的数字是要求它。
#include <limits>
// Infinite
auto positive_inf = std::numeric_limits< double >::infinity();
auto negative_inf = positive_inf * -1;
std::cout << "Positive infinity: " << positive_inf << std::endl;
std::cout << "Negative infinity: " << negative_inf << std::endl;
// Output
// Positive infinity: 1.#INF
// Negative infinity: -1.#INF
// Or division by zero
double zero = 0.0;
double divbyzero = 1.0 / zero;
// Output
// Division by zero: 1.#INF
|
任何具有无穷大的操作都会产生另一个无限值,因此它的行为就像瘟疫一样,在迭代模拟中传播,杀死模拟。
std::cout << "Add: " << add << " Sub: " << sub << " Mul: " << mul << " Div: " << div << std::endl;
// Output:
// Add: 1.#INF Sub: 1.#INF Mul: 1.#INF Div: 1.#INF
|
在执行流格式化时,无穷大数字可能会混乱,并可能显示一些不预期的东西。
#include <iomanip>
// Formatting (scientific)
for ( size_t i=0; i<10; i++)
{
std::cout << "Precision: " << std::scientific << i << std::setprecision(i) << " inf: " << positive_inf << std::endl;
}
// Output
// Precision: 0 inf: 1.#INF00e+000
// Precision: 1 inf: 1.$e+000
// Precision: 2 inf: 1.#Je+000
// Precision: 3 inf: 1.#IOe+000
// Precision: 4 inf: 1.#INFe+000
// Precision: 5 inf: 1.#INF0e+000
// Precision: 6 inf: 1.#INF00e+000
// Precision: 7 inf: 1.#INF000e+000
// Precision: 8 inf: 1.#INF0000e+000
// Precision: 9 inf: 1.#INF00000e+000
// Formatting (fixed)
for ( size_t i=0; i<10; i++)
{
std::cout << "Precision: " << std::fixed << i << std::setprecision(i) << " inf: " << positive_inf << std::endl;
}
// Output
// Precision: 0 inf: 1
// Precision: 1 inf: 1.$
// Precision: 2 inf: 1.#J
// Precision: 3 inf: 1.#IO
// Precision: 4 inf: 1.#INF
// Precision: 5 inf: 1.#INF0
// Precision: 6 inf: 1.#INF00
// Precision: 7 inf: 1.#INF000
// Precision: 8 inf: 1.#INF0000
// Precision: 9 inf: 1.#INF00000
|
固定精度0是非常危险的!没有迹象表明这是一个无限的数字!
#IND
当结果无法确定时出现此类错误。对于某些输入未定义的数学方法来说,情况尤其如此。#IND
NaN
acos
sqrt
例如,零除以零()在数学领域或。浮点也一样,产生了一个不确定的数字。0.0 / 0.0
#IND
double a = 0.0;
double b = 0.0 / a;
double negative_sqrt = sqrt (-1);
std::cout << "0.0/0.0 = " << b << std::endl;
std::cout << "sqrt(-1) = " << negative_sqrt << std::endl;
// Output
// 0.0/0.0 = -1.#IND
// sqrt(-1) = -1.#IND
|
捕获错误
有几个方法可以捕获 NAN,有些方法需要附加调试器才能获得最佳结果。
方法1:比较。
当将变量与自身进行比较时,它会产生与正常数字相反的结果。
double inf = std::numeric_limits< double >::infinity();
double nan = std::numeric_limits< double >::quiet_NaN();
double ind = sqrt (-1);
if (nan != nan)
std::cout << "nan != nan" << std::endl;
if (ind != ind)
std::cout << "ind != ind" << std::endl;
if (inf != inf)
std::cout << "inf != inf" << std::endl;
// Output
// nan != nan
// ind != ind
// std::isnan(nan) is true
// std::isnan(ind) is true
|
方法2(C++11):使用标准。
C++11
引入了几种方法来确定一个值是否是一个或不是,这些值居住在名称空间中std::
// Method 2, std::isnan
if (std::isnan(nan))
std::cout << "std::isnan(nan) is true" << std::endl;
if (std::isnan(ind))
std::cout << "std::isnan(ind) is true" << std::endl;
if (std::isnan(inf))
std::cout << "std::isnan(inf) is true" << std::endl;
// Output
// std::isnan(nan) is true
// std::isnan(ind) is true
|
方法3(MSVC):控制浮点控制词。
使用_controlfp
和_control87
将改变浮点控制词,基本上使计算机在浮点异常发生时发出硬件例外。MSVC 的默认值是静音浮点异常,并有可能默默销毁模拟。
我个人建议打开所有浮点例外,以便所有(或大多数)案件可以审查和代码更加强大。
这个用法有点违反直觉,但过了一会儿你会习惯的。以下是对我有效的东西。
// According to the docs, always clear fp control word
auto state = _clearfp();
state = _control87(0,0);
|
这将重置,并获得当前的浮点控制字状态。
有了这个,我们可以根据自己的喜好修改浮点控制词。
// This will turn ON FPE for zerodiv
state = _control87(state & ~_EM_ZERODIVIDE, _MCW_EM);
// This WILL NOT fail
const double sqrtneg = sqrt (-1);
// This WILL fail due to zerodiv
const double zero = 0.0;
const double zerodivresult = 1.0 / zero;
|
否则,我们可以用#IND
_EM_INVALID与
indeterminate ()退出。
// This will turn on FPE for #IND
state = _control87(state & ~_EM_INVALID, _MCW_EM);
// This WILL fail
const double sqrtneg = sqrt (-1);
// This WILL NOT fail and it'll produce #inf
const double zero = 0.0;
const double zerodivresult = 1.0 / zero;
|
如果你想打开两个或两个以上的FPEs,你必须做一些算术来设置正确的开关。
// This will turn on FPE for #IND and zerodiv
state = _control87(state & ~(_EM_ZERODIVIDE|_EM_INVALID), _MCW_EM);
|
如果你想把所有的FPEs,这将做到这一点。
// All FPEs
state = _control87(state & ~(_EM_INEXACT|_EM_UNDERFLOW
|_EM_OVERFLOW|_EM_ZERODIVIDE|
_EM_INVALID|_EM_DENORMAL), _MCW_EM);
|
要找出哪些浮点异常是活跃的,此片段将有所帮助。
bool inexact = 0 == (state & _EM_INEXACT);
bool underflow = 0 == (state & _EM_UNDERFLOW);
bool overflow = 0 == (state & _EM_OVERFLOW);
bool zerodiv = 0 == (state & _EM_ZERODIVIDE);
bool invalid = 0 == (state & _EM_INVALID);
bool denorm = 0 == (state & _EM_DENORMAL);
std::cout << std::boolalpha << "Will break on: n"
<< "Inexact: " << inexact << "n"
<< "Underflow: " << underflow << "n"
<< "Overflow: " << overflow << "n"
<< "Zerodiv: " << zerodiv << "n"
<< "Invalid: " << invalid << "n"
<< "Denormal: " << denorm << "n" ;
// Output
// Will break on:
// Inexact: false
// Underflow: false
// Overflow: false
// Zerodiv: true
// Invalid: true
// Denormal: false
|