这是一个错误
您在 Visual Studio 中使用的 C 实现存在缺陷。以下引用来自 C 2018。相关文本在 2011 年和 1999 年标准中实际上是相同的(在 1999 年,不等式使用文本而不是使用“>”和“≥”的数学符号来描述)。
首先,在这种情况下,# 表示将生成一个小数点字符,并且不会删除尾随零。它对删除尾随零之前应该产生的位数没有影响。 7.21.6.1 6 表示“……对于a、A、e、E、f、F、g 和 G 转换,浮点转换的结果number 总是包含一个小数点字符,即使它后面没有数字……对于g 和G 转换,不会从结果中删除尾随零……”这会使g 规范中的“……除非使用 # 标志,否则将从结果的小数部分中删除任何尾随零,如果没有剩余小数部分,则删除小数点字符。”
其次,对于零值,g 格式的规则说使用f 格式。这是因为g(或G)的规则取决于e 格式使用的指数和要求的精度:
- 对于
e,7.21.6.1 8 表示“……如果值为零,则指数为零。”
- 对于
g,它说“……如果非零,让 P 等于精度,如果省略精度,则等于 6,或者如果精度为零,则等于 1……”所以 P在问题中给出的%#g 或%#.8g 中,em> 是 6 或 8。
- 文本继续“...如果 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 表示“代表浮点数的double 参数被转换为样式[-]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。如果不存在,则可以轻松进行适当的调整。