C++ atof和stringstream产生不同的结果

C++ atof和stringstream产生不同的结果,c++,floating-point,precision,stringstream,atof,C++,Floating Point,Precision,Stringstream,Atof,我一直在研究一个问题,在这个问题上,我将浮点转换为人类可读的格式,然后再转换回来。也就是一根弦。我在使用stringstream时遇到了一些问题,发现atof可以产生“更好”的结果 请注意,我不打印数据。在本例中,我使用调试器检索值: const char *val = "73.31"; std::stringstream ss; ss << val << '\0'; float floatVal = 0.0f; ss >&g

我一直在研究一个问题,在这个问题上,我将浮点转换为人类可读的格式,然后再转换回来。也就是一根弦。我在使用stringstream时遇到了一些问题,发现atof可以产生“更好”的结果

请注意,我不打印数据。在本例中,我使用调试器检索值:

    const char *val = "73.31";
    std::stringstream ss;
    ss << val << '\0';
    float floatVal = 0.0f;
    ss >> floatVal; //VALUE IS 73.3100052

    floatVal = atof(val); //VALUE IS 73.3099976
const char*val=“73.31”;
std::stringstream-ss;
ss-floatVal//值为73.3100052
floatVal=atof(val)//值为73.3099976

对此可能有一个合理的解释。如果有人能启发我,我将不胜感激:)。

答案基于OP使用MSVC的假设

atof
在读取浮点值方面确实比
istream
更好

请参见此示例:

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>

int main()
{
    const char *val = "73.31";
    std::stringstream ss;
    ss << val;
    float floatVal = 0.0f;
    ss >> floatVal;
    std::cout << "istream>>(float&)                       :" << std::setw(18) << std::setprecision(15) << floatVal << std::endl;

    double doubleVal = atof(val);
    std::cout << "double atof(const char*)                :" << std::setw(18) << std::setprecision(15) << doubleVal << std::endl;

    floatVal = doubleVal;
    std::cout << "(float)double atof(const char*)         :" << std::setw(18) << std::setprecision(15) << floatVal << std::endl;

    doubleVal = floatVal;
    std::cout << "(double)(float)double atof(const char*) :" << std::setw(18) << std::setprecision(15) << floatVal << std::endl;
}
编译器甚至警告从
double
float
的转换:

warning C4244: '=': conversion from 'double' to 'float', possible loss of data
我还发现了这个页面:


更新:

73.30999975585938
似乎是
float
double
73.31
的正确解释


更新:
istream>>(双重&)
也能正常工作:

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>

int main()
{
    const char *val = "73.31";
    std::stringstream ss;
    ss << val;
    double doubleVal = 0.0f;
    ss >> doubleVal;
    std::cout << "istream>>(double&) :" << std::setw(18) << std::setprecision(15) << doubleVal << std::endl;
}

对于算术类型
istream::operator>
使用
num\u get::get
num\u get::get
应该使用类似于
scanf(“%g”)
for
float

但是:


对我来说,这看起来像是Microsoft
num\u get

中有一个bug,取决于是否在C++11模式下编译,流输入操作符
使用
strotd
scanf
来解析数字。这可能与使用
atof
有所不同。(参见示例)和@n.m.可使用MSV2015复制:
73.3100051879883
73.30999975585938
,如果从双精度到浮动精度不高。这就是重点。我在gcc、clang和vc上测试了这段代码。我看到的唯一解释是istream>>float的实现。看起来VC是正确的(使用float),而其他所有VC都使用double,然后将结果转换为float。@Alexander不清楚为什么您认为VC实现“正确”。它不会将十进制数转换为最接近的二进制表示形式,而其他所有的都会。为什么你会说“真正的罪魁祸首是从双精度转换为浮点”?坏的情况是istream
>
提取
atof
后跟cast可以提供正确的结果,
>
不能。那么,您对stream>>float有什么期望呢?我们有几个选择:1。读入double,然后转换为float。2.读入长双精度并转换为浮点3。直接读入浮点数。什么是正确的方法?@n.m.“它不会将十进制数转换为最接近的二进制表示形式,而所有其他的都是这样”是这样吗?当您查看测试时,您将看到gcc的计算结果为
73.309995585938
。因此,在
atof
之后的强制转换是正确的。
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>

int main()
{
    const char *val = "73.31";
    std::stringstream ss;
    ss << val;
    double doubleVal = 0.0f;
    ss >> doubleVal;
    std::cout << "istream>>(double&) :" << std::setw(18) << std::setprecision(15) << doubleVal << std::endl;
}
istream>>(double&) :             73.31
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <iomanip>
#include <cstdlib>


int main()
{
    std::string s = "73.31";
    float f = 0.f;
    sscanf(s.c_str(), "%g", &f);
    std::cout << std::setw(18) << std::setprecision(15) << f << std::endl;
}
73.3099975585938