我有一个浮点计算,结果为-1。#IND0000,在随后的浮点计算中导致了-1。#IND0000。是什么原因造成的?
编译器为Visual Studio C ++ 2013 18.00 x86。如果我使用GCC版本7.4.0,则不会发生此问题。据我所知,不需要FPU交互即可重置FPU的状态。
代码首先执行一些计算,然后将值转换为固定点。谷歌测试框架使用被测方法。我可以减轻计算中的数字错误(限制输入值,验证输出),但随后的计算应为-1。#INF0000;编译器错误?
最小示例:我在RunCalcs()中执行计算,例如,t为零,从而导致_SW_ZERODIVIDE。然后调用CheckNextCalc()来执行两个不依赖于先前结果的后续计算。
将FPU_RESET设置为1包括在RunCalcs()之后和CheckNextCalc()之前调用_fpreset()导致val1计算为0.999981。当FPU_RESET为0时,val1为-1。#IND0000。
#define FPU_RESET 0
#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
class CatchFloatEr
{
public:
void RunCalcs(const float t, const float r, unsigned int& val)
{
const float l = 1.0F / t;
val = static_cast<unsigned int>((l * static_cast<float>(0x10000)) + 0.5f);
}
void CheckNextCalc(float& val1, float& val2)
{
const float x1 = (2.0f * static_cast<float>(M_PI)) / static_cast<float>(1023);
val1 = cosf(x1);
const float x2 = (2.0f * static_cast<float>(M_PI)) / static_cast<float>(1023);
val2 = cosf(x2);
}
};
int main(int argc, char *argv[])
{
CatchFloatEr cf;
for (unsigned int a = 0; a < 5; a++)
{
for (unsigned int b = 0; b < 5; b++)
{
unsigned int val;
cf.RunCalcs(0.0F, 0.0F, val);
//std::cout << a << b << " val = " << val << std::endl;
}
}
#if FPU_RESET
_fpreset();
#endif
float val1;
float val2;
cf.CheckNextCalc(val1, val2);
std::cout << "val1 = " << val1 << std::endl;
std::cout << "val2 = " << val2 << std::endl;
return 0;
}
GCC 7.4.0
FPU_RESET 0 and 1
g++ -Wall -pg -O0 -pedantic -o app main.cpp
val1 = 0.999981
val2 = 0.999981
FPU_RESET 0/1
g++ -Wall -pg -O2 -pedantic -o app main.cpp
val1 = 0.999981
val2 = 0.999981
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
我一直在使用_statusfp()来读取标志位:
_SW_INEXACT 0x01 inexact (precision)
_SW_UNDERFLOW 0x02 underflow
_SW_OVERFLOW 0x04 overflow
_SW_ZERODIVIDE 0x08 zero divide
_SW_INVALID 0x10 invalid
如果我取消屏蔽FPU异常,则似乎会捕获以下与读取的标志匹配的多个错误。
STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4L
STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5L
启用FP可以通过以下方式完成:
_clearfp();
unsigned int currentState = 0U;
_controlfp_s(¤tState, 0U, _MCW_EM);
答案 0 :(得分:2)
1.f / 0.f
不会产生NaN
。结果为inf
。不过,在随后的计算中使用它可能会产生NaN
,就像您的g
一样。
我将尝试演示FPU异常状态如何不影响它所做的计算,而是所使用的各个变量的状态会影响结果。我将变量重命名为L
和G
(因为l
和1
看起来很相似)。
#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