C++ 为什么需要FPU重置以防止NaN结果传播到下一个计算结果?
我有一个浮点计算,结果是-1.#IND0000导致在后续的浮点计算中出现-1.#IND0000。这是什么原因造成的 <>编译器是Visual Studio C++ 2013 18 x86。如果使用GCC 7.4.0版,则不会出现问题。据我所知,重置FPU的状态不需要FPU交互 代码首先执行一些计算,将值转换为定点。google测试框架使用被测试的方法。我可以减少计算中的数字错误(限制输入值,验证输出),但后续计算应为-1;编译器错误 最简单的例子:我在RunCalcs()中执行一个计算,例如,t为零,从而导致_SW_ZERODIVIDE。然后调用CheckNextCalc()以执行两个不依赖于先前结果的后续计算 将FPU_RESET设置为1时,在RunCalcs()之后和CheckNextCalc()之前包含对_fpreset()的调用,结果val1计算为0.999981。当FPU#U RESET为0时,val1为-1.#IND0000 Visual Studio 2013C++ 为什么需要FPU重置以防止NaN结果传播到下一个计算结果?,c++,exception,floating-point,fpu,msvc12,C++,Exception,Floating Point,Fpu,Msvc12,我有一个浮点计算,结果是-1.#IND0000导致在后续的浮点计算中出现-1.#IND0000。这是什么原因造成的 编译器是Visual Studio C++ 2013 18 x86。如果使用GCC 7.4.0版,则不会出现问题。据我所知,重置FPU的状态不需要FPU交互 代码首先执行一些计算,将值转换为定点。google测试框架使用被测试的方法。我可以减少计算中的数字错误(限制输入值,验证输出),但后续计算应为-1;编译器错误 最简单的例子:我在RunCalcs()中执行一个计算,例如,t为零
FPU_RESET 0
val1 = -1.#IND <--- Why would this result in -1.#INF0000
val2 = 0.999981
FPU_RESET 1
val1 = 0.999981
val2 = 0.999981
如果我取消屏蔽FPU异常,我似乎会得到以下捕获多个与读取的标志匹配的错误
STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4L
STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5L
可通过以下方式启用FP:
_clearfp();
unsigned int currentState = 0U;
_controlfp_s(¤tState, 0U, _MCW_EM);
1.f/0.f
不会导致NaN
。它会产生inf
。但是,在后续计算中使用它可能会导致NaN
,与您的g
一样
我将尝试演示FPU异常状态如何不影响它所做的计算,而是您使用的各个变量的状态对结果产生影响。我将变量重命名为
L
和G
(因为L
和1
看起来非常相似)
我们需要一个新的世界。您的问题需要包含足够的代码,以便我们可以重现错误。具体来说,我们应该能够从您的初始代码编译第一个错误的余弦值。在您的示例中,链接的新值是从以前计算的值派生出来的。我的问题是余弦查找计算不使用以前计算的值;除了使用浮点数学,它们是完全隔离的。然而,后一个值受到先前计算的影响,这是非常奇怪的。请您澄清一下十六进制地址上的.p0是做什么的(使用VS2013,所以C++11之后的版本不完全受支持)。谢谢。@electronpygmy也许您可以编辑这个,添加导致问题的余弦计算,并在这里共享到结果程序的链接?
.p0
对十六进制浮点文本(C++17)使用指数语法。对于C++17之前的编译器,可以使用static_cast(0x10000)
。我在回答中加入的static\u assert
是为了表明它们是相等的。我曾尝试在一个新项目中添加其他浮点计算,但没有看到错误。我看不出GCC有什么问题。我不能分享整个项目。我一直在删除“最小可复制示例”的代码,但这是一个复杂的项目。我可以重复这个问题;2个谷歌单元测试,一个接一个。第一个执行上述计算,第二个在循环中计算余弦查找表。[1]. cos(0.006142)=-1.#IND00[2]。cos(0.006142)=0.999981。如果我在第一个单元测试结束时使用_fpreset()[1]。cos(0.006142)=0.999981[2]。cos(0.006142)=0.999981如果您放弃google单元测试部分,尝试只编写一个小的main()
函数来重现问题,那么就更容易发现VS 2013
、google单元测试框架或其他方面是否存在问题。
_SW_INEXACT 0x01 inexact (precision)
_SW_UNDERFLOW 0x02 underflow
_SW_OVERFLOW 0x04 overflow
_SW_ZERODIVIDE 0x08 zero divide
_SW_INVALID 0x10 invalid
STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4L
STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5L
_clearfp();
unsigned int currentState = 0U;
_controlfp_s(¤tState, 0U, _MCW_EM);
#include <cfenv>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <map>
#include <string>
std::string excstr(int excepts) {
static const std::map<int, std::string> names = {
{FE_DIVBYZERO, "FE_DIVBYZERO"}, {FE_INEXACT, "FE_INEXACT"},
{FE_INVALID, "FE_INVALID"}, {FE_OVERFLOW, "FE_OVERFLOW"},
{FE_UNDERFLOW, "FE_UNDERFLOW"},
};
std::string rv;
for(const auto& [mask, txt] : names) {
if(excepts & mask) {
if(rv.size()) rv += ", ";
rv += txt;
}
}
return rv;
}
template<typename T>
void check(const char* txt, const T& value) {
// taking "value" by-value would not guarantee a bit-perfect copy
std::cout << std::right << std::setw(13) << txt << " isnan: " << std::left
<< std::setw(5) << std::isnan(value) << " isinf: " << std::setw(5)
<< std::isinf(value) << " value: " << std::right << std::setw(5)
<< value << " ex: " << std::left
<< excstr(std::fetestexcept(FE_ALL_EXCEPT)) << "\n";
}
int main() {
std::cout << std::boolalpha;
float L = 1.f / 0.f;
check("L=1/0", L);
std::feclearexcept(FE_ALL_EXCEPT); // clearing FE_DIVBYZERO
float G = L * 0.f; // L is inf regardless of the FPU state
check("G=L*0", G);
std::feclearexcept(FE_ALL_EXCEPT); // clearing FE_INVALID
check("cleared?", 0.f);
static_assert(static_cast<float>(0x10000) == 0x10000.p0);
// the two below calculations will set FE_INVALID again.
// casting inf to an unsigned int
auto lreg = static_cast<unsigned int>((L * 0x10000.p0) + 0.5f);
check("lreg", lreg);
// casting NaN to an unsigned int
auto greg = static_cast<unsigned int>((G * 0x10000.p0) + 0.5f);
check("greg", greg);
// the FPU doesn't need a reset to perform
// even though some exception bits are set:
float flreg = static_cast<float>(lreg) + 10.f;
check("flreg", flreg);
float fgreg = static_cast<float>(greg) + 2.f;
check("fgreg", fgreg);
float R = flreg / fgreg;
check("R=flreg/fgreg", R);
}
L=1/0 isnan: false isinf: true value: inf ex: FE_DIVBYZERO
G=L*0 isnan: true isinf: false value: -nan ex: FE_INVALID
cleared? isnan: false isinf: false value: 0 ex:
lreg isnan: false isinf: false value: 0 ex: FE_INVALID
greg isnan: false isinf: false value: 0 ex: FE_INVALID
flreg isnan: false isinf: false value: 10 ex: FE_INVALID
fgreg isnan: false isinf: false value: 2 ex: FE_INVALID
R=flreg/fgreg isnan: false isinf: false value: 5 ex: FE_INVALID