Sza*_*lcs 3 c printf visual-c++
我注意到printf("%#g\n", 0.0)
任何 gcc/clang 版本与 Windows 7 上的 Visual Studio 2019(截至今天的最新版本)都会提供不同的输出。
gcc/clang 给出0.00000
(总共 6 位数字,在 之后 5 位.
)而 VS 给出0.000000
(总共 7 位,在 之后 6 位.
)。同样,"%#.8g"
gcc/clang 总共给出 8 位数字,VS 总共给出 9 位数字。
问题:
您在 Visual Studio 中使用的 C 实现有缺陷。以下引文来自 C 2018。相关文本在 2011 年和 1999 年标准中实际上相同(在 1999 年,不等式使用文本而不是使用“>”和“?”的数学符号来描述)。
首先,在这种情况下,#
意味着将生成小数点字符并且不会删除尾随零。它对删除尾随零之前应生成的位数没有影响。7.21.6.1 6 说“……对于a
, A
, e
, E
, f
, F
, g
, 和G
转换,转换浮点数的结果总是包含一个小数点字符,即使后面没有数字……对于g
和G
转换,尾随零不是从结果中删除……”这使g
规范中说“……除非#
使用标志,任何尾随零将从结果的小数部分中删除,如果没有小数部分剩余,小数点字符将被删除。”
其次,对于零值,g
格式规则说明使用该f
格式。这是因为g
(or G
)的规则取决于e
格式将使用的指数和要求的精度:
e
,7.21.6.1 8 表示“......如果值为零,则指数为零。”g
,它表示“……如果非零,则P等于精度,如果省略精度,则为 6,如果精度为零,则为 1……”因此,问题中的或给出的P为 6 或 8 。%#g
%#.8g
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++标准采用了C规范printf
。C 2017 草案 N4659 20.2 说“C++ 标准库还提供了 C 标准库的功能,经过适当调整以确保静态类型安全。”
这个bug很可能是在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;
}
Run Code Online (Sandbox Code Playgroud)
这假设我们在精度 6 和 8 中看到的相同错误存在于精度 1 中。如果不是,则可以轻松进行适当的调整。