我一直在研究一些代码,它们有一个奇怪的优化错误,在这个过程中,我偶然发现了strtod()中的错误条件,它的行为与strtof()不同,恰好处于非正规值的边缘.strtof()的行为在我看来完全合理,但strtod()并非如此!具体地说,对于输入值"-0x1.fffffffffffffp-1023",它返回-0.0.

这是在其正确解码的表示"-0x1.ffffffffffffep-1023"中设置为1的额外比特.更奇怪的是,添加额外的尾随数字得到的值是2-1018,我无法解释.在我看来,从非正规到正常浮点转换的特殊边缘情况被错误地处理,导致零值.

有没有人能解释额外数字引起的另一个奇怪的数字?

MSC 2022和Intel 2023上的故障相同

Double&输出的示例代码MRE(浮点数的工作方式与预期不谋而合)

// strtod() fails to handle edge case overflow from denormals correctly
// 
// Problem manifests on both MS 2022 and Intel 2023 compilers so by design? but why???
// 
// using Windows 11 and Microsoft Visual Studio Community 2022 (64-bit) - Version 17.1.0
 
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

void Show(const char *name, int err, double arg)
{
    unsigned iarg[2];
    memcpy(&iarg, &arg, sizeof arg);
    printf("\n\"%25s\" decoded errno=%i as 0x%08x%08x %.13a %30.22g",
           name, err, iarg[1], iarg[0], arg, arg);
}

void DecodeShow(const char *value)
{
    char *stopstr;
    double arg;
    errno = 0;
    arg = strtod(value, &stopstr);
    Show(value, errno, arg);
}

int main(void)
{
    printf("Test of doubles near denorm boundary\n");
    DecodeShow("-0x1.ffffffffffffp-1023");
    DecodeShow("-0x1.ffffffffffffep-1023");
    DecodeShow("-0x1.ffffffffffffe8p-1023");
    DecodeShow("-0x1.fffffffffffff0p-1023"); // hard fail == -0.0 !
    DecodeShow("-0x1.fffffffffffff8p-1023");
    DecodeShow("-0x1.ffffffffffffffp-1023");
    DecodeShow("-0x1.fffffffffffffffp-1023");
    printf("\n");
    DecodeShow("0x1.ffffffffffffe8p-1023");
    DecodeShow("0x1.fffffffffffff0p-1023"); // hard fail == +0.0
    DecodeShow("0x1.fffffffffffff8p-1023");
    DecodeShow("0x1.ffffffffffffffp-1023");
    printf("\n");
    DecodeShow("0x1.ffffffffffffep-1022");
    DecodeShow("-0x1.fffffffffffffp-1022");
    DecodeShow("-0x1.fffffffffffff8p-1022");
    DecodeShow("-0x1.ffffffffffffffp-1022");
}

故障边界附近的选定输出:

" -0x1.ffffffffffffep-1023" decoded errno=0 as 0x800fffffffffffff -0x0.fffffffffffffp-1022  -2.225073858507200889025e-308
"-0x1.ffffffffffffe8p-1023" decoded errno=0 as 0x800fffffffffffff -0x0.fffffffffffffp-1022  -2.225073858507200889025e-308
"-0x1.fffffffffffff0p-1023" decoded errno=0 as 0x8000000000000000 -0x0.0000000000000p+0                             -0
"-0x1.fffffffffffff8p-1023" decoded errno=0 as 0x8040000000000000 -0x1.0000000000000p-1019  -1.780059086805761106472e-30

我认为,超出机器精度的额外尾随数字不应该从根本上改变strtod这样解码的浮点值.我不能通过使用十进制数字输入字符串来引发同样的失败-跨越边界的转换似乎表现良好(尽管我不能排除我还没有发现的一个Spot值不起作用).

推荐答案

这与https://developercommunity.visualstudio.com/t/strtod-parses-0x0fffffffffffff8p-1022-i/10293606报告的问题相同.2023年2月提交了一个错误(它似乎以前在编译器中得到了修复,但没有在strtod()中得到修复).

我在报告中运行了5个strtod()示例,其中4个给出了不正确的转换(包括您的0示例).每个结果应为0x1.0000000000000p-1022,但

0x0.fffffffffffff8p-1022 => 0x1.0000000000000p-1020
0x1.fffffffffffffp-1023 => 0x0.0000000000000p+0
0x7.ffffffffffffcp-1025 => 0x1.0000000000000p-1021
0xf.ffffffffffff8p-1026 => 0x1.0000000000000p-1020

注意不正确的指数.报告中没有猜测出了什么问题.

C++相关问答推荐

字符数组,字符指针,在一种情况下工作,但在另一种情况下不工作?

C:fopen是如何实现二进制模式和文本模式的?

将常量转换为指针会增加.数据大小增加1000字节

将宏值传递给ARM链接器,该链接器将变量放置在特定位置

如何在ASM中访问C struct 成员

如何使用指向 struct 数组的指针并访问数组中特定索引处的 struct

错误...的多个定义(&Q)首先在这里定义&

-Wnonnull-Compare警告不是具有误导性吗?

我在C程序的Flex/Bison中遇到语法错误

我应该在递归中使用全局变量吗

如何对现有的双向循环链表进行排序?

Valgrind正在使用一个Fexecve电话报告不可能发生的事情

C编译和运行

unions 的原子成员是个好主意吗?

程序如何解释变量中的值

在Ubuntu上使用库部署C程序的最佳实践

即使客户端不发送数据,也会发生UNIX套接字读取

函数的typedef是标准 C 语法吗?它与函数指针的typedef有何不同?

通过修改c中的合并排序对数组的偶数索引进行排序

定义 int a = 0, b = a++, c = a++;在 C 中定义了行为吗?