编译器使用的双精度表示的显式规范

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 个显式存储的有效十进制位,见下文。

由于gccclang 是合规的,并且非常需要一致的跨平台行为,我想知道是否可以强制visual-c++ 使用正常表示。或者让gccclang 使用次正规表示当然也可以解决问题。

可以使用以下代码在visual-c++gccclang 中重现不同双重表示的问题:

#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
    

【讨论】:

以上是关于编译器使用的双精度表示的显式规范的主要内容,如果未能解决你的问题,请参考以下文章

为啥接口的显式实现不能公开?

智能转换与 KOTLIN 中的显式转换有何不同

代码指定的双精度和编译器选项双精度之间的差异

如何避免包含`a(i)= b(i,c(i))`的显式循环?

仅标头模板的显式实例化声明(外部模板)

显式实例化模板类的显式实例化模板方法