2

std::strod()将字符串转换为双精度。但在一种特殊情况下,存在一个微小的小数错误。atof()使用和也可以看到此错误sscanf()仅当满足以下所有条件时才会发生错误:

  • Visual Studio 2015(或 VS 2015 更新 1)
  • 为 64 位 (x64) 构建
  • 调用以_controlfp_s()设置“向负无穷舍入”
  • 数字有小数部分,例如 0.5 或 2.5(不是整数)

(我只在 64 位 Windows 8.1 Enterprise 上测试过)这里有一个简单的例子:

#include <string>

inline double fromString(const std::string& s) {
   size_t idx;
   return std::stod(s, &idx);
}

int main()
{
   double res1 = fromString("0.5");
   unsigned tmp;
   _controlfp_s(&tmp, RC_DOWN, MCW_RC);
   double res2 = fromString("0.5");
   _controlfp_s(&tmp, RC_NEAR, MCW_RC);
   double res3 = fromString("0.5");
}

std::stod()正在调用std::calling strtod()stdlib.h。 res1正好是 0.5,但res2会是 0.50000000000000011

_controlfp_s(&tmp, RC_DOWN, MCW_RC);控制舍入误差,在这种情况下,它设置为向负无穷大舍入。如果将其设置回默认值,_controlfp_s(&tmp, RC_NEAR, MCW_RC);则 thenstrod()再次精确,res30.5也是如此

请注意,某些十进制数无法精确表示。但有些数字可以,例如 2.5、0.5 和 0.375,但在上面的示例中它们都会出现舍入误差。

很奇怪,不是吗?

我做错了什么,还是这是 Visual Studio 标准库中的错误?

4

2 回答 2

0

感谢 Adrian McCarthy 的链接,我找到了一种解决方法:使用 fesetround()而不是_controlfp_s()

#if _MSC_VER == 1900
   //Visual Studio 2015
   fesetround(FE_DOWNWARD);
#else
   _controlfp_s(&tmp, RC_DOWN, MCW_RC);
#endif

fesetround()在 VS2015 中引入。我会向微软报告这个错误。也可以使用#if _MSC_VER == 1900 && defined(_WIN64)更精确,因为该错误不存在于 32 位目标。

于 2015-12-10T08:26:03.323 回答
0

这不是错误。这是一个特点!

https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

于 2015-12-10T09:24:48.963 回答