繁体   English   中英

带有说明符“%g”的 printf 的精度

[英]the precision of printf with specifier "%g"

谁能解释一下 printf 中的[.precision]如何与说明符“%g”一起使用? 我对以下 output 感到很困惑:

double value = 3122.55;
printf("%.16g\n", value); //output: 3122.55
printf("%.17g\n", value); //output: 3122.5500000000002

我了解到%g使用最短的表示形式。

但是以下输出仍然让我感到困惑

printf("%.16e\n", value); //output: 3.1225500000000002e+03
printf("%.16f\n", value); //output: 3122.5500000000001819
printf("%.17e\n", value); //output: 3.12255000000000018e+03
printf("%.17f\n", value); //output: 3122.55000000000018190

我的问题是:为什么%.16g给出了准确的数字而%.17g不能?

好像16位有效数字就可以了。 谁能告诉我原因?

%g使用最短的表示。

浮点数 通常不存储为以10为基数的数字,而是2 (性能、大小、实用性原因)。 但是,无论您的表示的基础是什么,总会有有理数无法在变量存储它们的任意大小限制中表达。

当您指定%.16g ,您是说您想要以最多16位有效数字给出的数字的最短表示。

如果最短的表示超过16位, printf将通过在最后的2位进行切割来缩短数字串,为您留下3122.550000000000 ,实际上是最短形式的3122.55 ,解释您获得的结果。

通常, %g将始终为您提供最短的结果,这意味着如果可以缩短表示您的数字的数字序列而不会损失任何精度,那么它就会完成。

为了进一步举例,当您使用%.17g并且第17个小数位包含一个不同于0的值(特别是2 )时,您最终会得到完整的数字3122.5500000000002

我的问题是:为什么%.16g给出确切数字而%.17g不能?

它实际上是%.17g为您提供确切的结果,而%.16g仅提供一个带有错误的舍入近似值(与内存中的值相比)。

如果您想要更固定的精度,请改用%f%F

十进制值 3122.55 不能用二进制浮点精确表示。 当你写

double value = 3122.55;

您最终会得到可以精确表示的最接近的可能值。 碰巧,该值正好是3122.5500000000001818989403545856475830078125

16 位有效数字的值是3122.550000000000 对于 17 位有效数字,它是3122.5500000000002 所以这些就是%.16g%.17g给你的表示。

请注意,十进制数的最接近的double精度表示保证至少精确到 15 位十进制有效数字。 这就是为什么在这种情况下您需要打印到 16 或 17 位数字才能开始在输出中看到这些明显的不准确之处 - 对于任何较少数量的有效数字, double表示保证与您输入的原始十进制数相匹配。

最后一点:你说

我了解到%g使用最短的表示。

虽然这是%g行为的流行总结,但它也是错误的。 请参阅%g printf 说明符的确切含义是什么? 我在这里详细讨论了这个问题,并展示了一个使用科学记数法的%g示例,即使它比不使用科学记数法长 4 个字符。

十进制表示 3122.55 不能用二进制浮点表示精确表示。

双精度二进制浮点值可以正确表示十进制值的大约 15 位有效数字(注意不是小数位); 此后数字可能不相同,在极端情况下甚至没有任何实际意义,并且将是从浮点表示转换为十进制数字字符串的人工制品。

我了解到 %g 使用最短的表示。

规则是:

其中P是精度(如果没有指定精度则为 6,如果精度为零则为 1), X是 E/e 样式表示法所需的十进制指数,则:

  • 如果P > X ≥ −4 ,则转换的样式为 f 或 F,精度为P − 1 − X
  • 否则,转换采用样式 e 或 E 和精度P − 1

%g精度的修改导致不同的输出:

printf("%.16g\n", value); //output: 3122.55
printf("%.16e\n", value); //output: 3.1225500000000002e+03
printf("%.16f\n", value); //output: 3122.5500000000001819

尽管在格式说明符中具有相同的精度。

十进制 3122.55 在 memory 中的精确表示是带有 53 位尾数的二进制小数。

printf("%a\n", value);     // output 0x1.865199999999ap+11

精确转换回小数是:

printf("%.45f\n", value);  // output 3122.550000000000181898940354585647583007812500000

如果将该数字削减为 17 位数字,您将得到:

printf("%.17g\n", value);  // output 3122.5500000000002

在 16 位数字处,所有尾随数字都是0并且可以安全地删除(默认情况下g格式会自动删除)以获得:

printf("%.16g\n", value);  // output 3122.55

这就是为什么您要取回原始十进制数的原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM