将double转换为string to double throws exception

时间:2015-07-27 12:21:59

标签: c++ visual-studio gcc type-conversion

以下代码在Visual Studio 2013中抛出std :: out_of_range异常,我认为它不应该:

#include <string>
#include <limits>

int main(int argc, char ** argv)
{
    double maxDbl = std::stod(std::to_string(std::numeric_limits<double>::max()));

    return 0;
}

我还使用gcc 4.9.2测试了代码,并且它没有抛出异常。该问题似乎是由转换为字符串后的字符串表示不准确引起的。在Visual Studio std::to_string(std::numeric_limits<double>::max())中产生

  

179769313486231610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000

确实看起来确实太大了。但是,在gcc中,它会产生

  

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

似乎小于传递的值。

但是,std::numeric_limits<double>::max()不应该返回

  

最大有限可表示浮点数?

那么为什么字符串表示会出现?我在这里缺少什么?

1 个答案:

答案 0 :(得分:1)

直接回答

Gcc(和Clang和VS2105)正确返回(2 1024 - 1) - (2 1024-53 - 1)的整数值52个一位有效数字和一个无偏指数1023(2 1024 - 1将是一个整数值1023一位,我只是减去IEE754格式52下面的所有位)< / p>

我可以确认一个大整数库给出了179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368L

之前的精确浮点数为2 971 较小(971 = 1023 - 52),即:179769313486231550856124328384506240234343437157459335924404872448581845754556114388470639943126220321960804027157371570809852884964511743044087662767600909594331927728237078876188760579532563768698654064825262115771015791463983014857704008123419459386245141723703148097529108423358883457665451722744025579520L

下一个不可表示的值将是2 971 更大,即: 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216L

但MSVC2013和之前使用的值接近2 1024 + 2 971 ,即:179769313486231610731333614426100589925524828262616317947942685512308090830973387504827396012048193870699768806228404251083258210739369062217227314575410731769485876273179688476358949112102859294830297395714877595371718127781702814782017661749531126051903195165027873311156314696040132728420308633064323416064L 。因为它大于IEEE754双精度中可表示的任何值,所以它不能被解码为double。

因为最多可以说2 1024 - 2 971 std::numeric_limits<double>::max())和2 1024 之间的任何值可以四舍五入为std::numeric_limits<double>::max(),但大于2 1024 的值显然是溢出。

关于准确性的讨论

在double中只有16个十进制数字是准确的,所有其他数字可以看作是垃圾或随机值,因为它们不依赖于值本身,而只取决于您选择计算它们的方式。试着将1e + 288(已经是值)减去maxDbl并查看会发生什么:

maxLess = max Dbl - 1.e+288;
if (maxLess == maxDbl) {
   std::cout << "Unchanged" << std::endl;
}
else std::cout << "Changed" << std::endl;

你应该看到......不变。

看起来VS 2013在浮点值的方式上有点不连贯:它将maxDbl 过多比实际可表示的最大值高一位,并且无法对其进行解码后面。

问题在于标准选择使用%f格式,这给出了错误的准确性。如果你想在gcc中看到一个等价的问题,只需使用:

#include <iostream>
#include <string>
#include <limits>
#include <iomanip>
#include <sstream>

int main() {
    double max = std::numeric_limits<double>::max();
    std::ostringstream ostr;
    ostr << std::setprecision(16) << max;
    std::string smax = ostr.str();
    std::cout << smax << std::endl;
    double m2 = std::stod(smax);
    std::cout << m2 << std::endl;

    return 0;
}

舍入为16位mxDbl写入(正确):1.797693134862316e + 308,但无法再解码

这一个:

#include <iostream>
#include <string>
#include <limits>

int main() {
    double maxDbl = std::numeric_limits<double>::max();
    std::string smax = std::to_string(maxDbl);
    std::cout << smax << std::endl;

    std::string smax2 = "179769313486231570800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000";

    double max2 = std::stod(smax2);
    if (max2 == maxDbl) {
       std::cout << smax2 << " is same double as " << smax << std::endl;
    }

    return 0;
}

显示:

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
179769313486231570800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000 is same double as 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

TL / DR:我的意思是,一个大的enoudh double值当然可以用一个完整的整数表示(根据IEEE754)。但它确实代表了从一半到前一半到下一半的所有整数。因此,该范围内的任何整数都可以是double的可接受表示,并且16个十进制数字的一个值舍入应该是可接受的,但是当前标准库只允许截断最大浮点值,十六位数。但VS2013给出的数字超出了范围的最大值,无论如何都是错误。

参考

IEEE floating point on wikipedia