gpt4 book ai didi

c - 从编译器之间的 printf() 获得一致的输出

转载 作者:行者123 更新时间:2023-12-03 23:33:32 25 4
gpt4 key购买 nike

我注意到 printf("%#g\n", 0.0) 在任何 gcc/clang 版本与 Windows 7 上的 Visual Studio 2019 (截至今天的最新版本)上提供不同的输出。

gcc/clang 给出 0.00000(总共 6 个数字,. 之后的 5 个)而 VS 给出 0.000000(总共 7 个,在)。类似地,"%#.8g" 使用 gcc/clang 总共给出 8 位数字,而 VS 总共给出 9 位数字。

问题:

  • 标准对此有何规定?编译器/标准库之一有问题吗?
  • 我只在本地(Windows 7)的 VS 中看到这种行为,但在 Azure Pipelines(最近的 Windows Server)上看不到这种行为。哪些特定的编译器版本/标准库版本/操作系统会受到影响?
  • 有没有办法在编译器之间获得一致的输出?

最佳答案

这是一个错误

您在 Visual Studio 中使用的 C 实现存在缺陷。以下引用来自 C 2018。相关文本在 2011 年和 1999 年标准中实际上是相同的(在 1999 年,不等式使用文本而不是使用“>”和“≥”的数学符号来描述)。

首先,在这种情况下,#表示将生成小数点字符,并且不会删除尾随零。它对删除尾随零之前应该产生的位数没有影响。 7.21.6.1 6 表示“……对于 a , A , e , E , f , F , g , 和 G转换, float 的转换结果总是包含一个小数点字符,即使它后面没有数字……对于 gG转换,尾随零不会从结果中删除……”这会使 g 的部分无效。说明“……除非 #如果使用标志,则从结果的小数部分中删除任何尾随零,如果没有剩余小数部分,则删除小数点字符。”

其次,对于零值,g 的规则格式说 f使用格式。这是因为 g 的规则(或 G )取决于 e 的指数格式将使用和要求的精度:

  • 对于 e , 7.21.6.1 8 表示“……如果值为零,则指数为零。”
  • 对于 g ,它说“......如果非零,让P等于精度,如果省略精度,则为6,如果精度为零,则为1......”所以P是6或8 %#g%#.8g问题中给出。
  • 文本继续“…如果P> X ≥ -4,则转换为样式f (或 F )和精度 P - (X + 1)。”

所以 %#g 的转换或 %#.8g完成样式f分别使用精度 6−(0+1) = 5 或 8−(0+1) = 7。

第三,对于f , 7.21.6.1 8 说“A double表示 float 的参数以 [-]ddd.ddd 样式转换为十进制表示法,其中小数点字符后的位数等于精度规范……”因此,小数点后应分别打印 5 位或 7 位。

所以,对于 %#g , “0.00000”符合C标准,“0.000000”不符合。对于,%#.8g ,总共八位(小数点后七位)符合,九位(八位后)不符合。

既然你用visual-c++标记了这个,我会注意到C++标准采用了printf的C规范。 . C 2017 草案 N4659 20.2 说“C++ 标准库还提供了 C 标准库的功能,经过适当调整以确保静态类型安全。”

补偿错误

错误可能在C/C++库,而不是编译器,所以调整源代码使用#if测试微软宏_MSC_VER的值,例如,可能不是一个好的解决方案。 (特别是在编译之后,编译器可能会与以后的库一起运行。)

可以在程序启动期间测试库。定义后int PrecisionAdjustment;对于外部作用域,这段代码可以用来初始化它:

{
/* The following tests for a Microsoft bug in which a "%#g" conversion
produces one more digit than the C standard specifies. According to
the standard, formatting zero with "%#.1g" should produce "0.", but
Microsoft software has been observed to produce "0.0". If the bug
appears to be present, PrecisionAdjustment is set -1. Otherwise,
it is 0. This can then be used to select which format string to
use or to adjust a dynamic precision given with "*" such as:

printf("%#.*g", 6+PrecisionAdjustment, value);
*/
char buffer[4];
snprintf(buffer, sizeof buffer, "%#.1g", 0.);
PrecisionAdjustment = buffer[2] == '0' ? -1 : 0;
}

这假设我们在精度 6 和 8 中看到的相同错误存在于精度 1。如果不存在,则可以轻松进行适当的调整。

关于c - 从编译器之间的 printf() 获得一致的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65900619/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com