C++ 为什么需要FPU重置以防止NaN结果传播到下一个计算结果?

C++ 为什么需要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为零

我有一个浮点计算,结果是-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 2013

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(&currentState, 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(&currentState, 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