编译器使用的双精度表示的显式规范
Posted
技术标签:
【中文标题】编译器使用的双精度表示的显式规范【英文标题】:Explicit specification of the double precision representation used by compiler 【发布时间】:2018-11-19 13:21:22 【问题描述】:我最近遇到了visual-c++
似乎不符合IEEE 754 的问题,而是使用subnormal representation。也就是说,其中的双精度浮点数没有通常表示的 1 个符号位、11 个指数位和 52 个显式存储的有效十进制位,见下文。
由于gcc
和clang
是合规的,并且非常需要一致的跨平台行为,我想知道是否可以强制visual-c++
使用正常表示。或者让gcc
和clang
使用次正规表示当然也可以解决问题。
可以使用以下代码在visual-c++
、gcc
和clang
中重现不同双重表示的问题:
#include <iostream>
#include <string>
int main()
try
std::stod("8.0975711886543594e-324");
std::cout << "Subnormal representation.";
catch (std::exception& e)
std::cout << "Normal representation.";
return 0;
表示规范是否可能在所有三种情况下产生一致的行为?
编辑:正如geza 所指出的,这在std::stod
的不同实现中出现了问题,如果有任何方法可以使std::stod
行为一致,那么就会产生问题无需为其实现单独的包装器。
【问题讨论】:
这里,区别在于std::stod
,不是吗? GCC 和 clang 将 out_of_range 用于次正常的数字(如果你问我,这很奇怪),而 msvc 不会。我什至不明白,为什么他们会为一个被截断为零的小数抛出 out_of_range 。这绝对不是超出范围。这种情况应该有一个单独的例外。
相关:***.com/questions/48086830/…
std::stod
设计不当。 strtod
可以报告 ERANGE 是上溢还是下溢。 std::stod
不能。
@geza 谢谢,编辑了帖子以反映它的std::stod
,也许有一些标准的设计方式
是的,使用strtod
,它没有这个限制。它的行为不像记录的次正规数:它将返回次正规值(因此,与文档相反,它返回一个非零值,但设置 errno=ERANGE)。这对我们有好处,因为你可以得到你想要的号码。
【参考方案1】:
很遗憾,std::stod
的设计很糟糕,因为无法确定导致 std::out_of_range
异常的原因。
我建议您改用strtod
。虽然标准中没有指定这个函数应该对次正规数做什么,但它通常对次正规数表现良好(这意味着它返回次正规数)。这个函数的好处是它对超出范围的情况返回有意义的结果,因此可以确定超出范围的原因。
如果您想处理超出范围的情况,您需要检查errno
以获得ERANGE
。请注意,如果结果是次正规数/零数,那么 errno
可能会设置为 ERANGE
,您应该忽略它(您可以使用 fpclassify
进行检查)。
所以逻辑是这样的:
double r = strtod(string, &end);
// here, check for end to know about invalid strings
if (errno==ERANGE) // out-of-range (overflow, underflow)
int c = fpclassify(r);
if (c!=FP_SUBNORMAL&&c!=FP_ZERO) // let's filter out underflow cases
// "real" out of range handling here, just overflow
【讨论】:
以上是关于编译器使用的双精度表示的显式规范的主要内容,如果未能解决你的问题,请参考以下文章