浮点数计算常见错误1.#INF, 1.#IND和#QNAN

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

以下是您作为程序员在执行浮点算术(任何加法、减法、乘法和除法)时应注意的一些浮点条件,即(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++表示"无穷大"。由于浮点数的有限性(对于双精度浮点数),无限以有限的值表示。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;

浮点数计算常见错误1.#INF, 1.#IND和#QNAN

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

浮点数计算常见错误1.#INF, 1.#IND和#QNAN

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

 

上一篇:MyBatis-动态SQL


下一篇:力扣1473. 粉刷房子 III