编程时,可能会从您的代码中产生某些错误条件。如果编译器捕获任何编译错误,它将停止编译并告诉您错误所在位置。编译器还可能会对某些构造发出警告,这些构造可能会或可能不会在运行期间给您带来问题。

以下是您作为程序员在执行浮点算术(任何加法、减法、乘法和除法)时应注意的一些浮点条件,即(QNAN 和 SNAN)条件和错误。

 

正数负数意义
1.#INF-1.#INFInfinity无穷大
1.#SNAN-1.#SNANSignalling NaN
1.#QNAN-1.#QNANQuiet NaN
1.#IND-1.#IND

Indefinite / Indeterminite NaN

不是一个数,未定值

#NaN

C++中代表不是数字。它是一种用浮点描绘未定义或未指定值的价值。NAN 的概念于 1985 年由 IEEE 754 浮点标准引入,其中还给出了无穷大的概念。NaN

#QNaN和#SNaN

有两种,安静和信号。两者非常相似,在大多数情况下都是相同的。与MSVC,信号导航(sNaN)默认情况下是安静的导航。他们不会提出任何硬件例外(因为所有的浮点例外),你不会注意到任何错误,直到打印出结果,但模拟惨败。

#INF

C++表示"无穷大"。由于浮点数的有限性(对于双精度浮点数),无限以有限的值表示。INF32-bit64-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

当结果无法确定时出现此类错误。对于某些输入未定义的数学方法来说,情况尤其如此。#INDNaNacossqrt

 

例如,零除以零()在数学领域或。浮点也一样,产生了一个不确定的数字。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;

838

否则,我们可以用#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;

392

如果你想打开两个或两个以上的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

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐