首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >打印什么(“%.17g\n”,.1)?

打印什么(“%.17g\n”,.1)?
EN

Stack Overflow用户
提问于 2016-09-23 03:31:48
回答 1查看 1K关注 0票数 1

我经常使用命令行程序"printf“来测试/预测C库函数将做什么。

但我最近发现(不再起作用了)--至少在最近的linux/ubuntu发行版上,命令行printf ( bash内置版和/usr/bin/printf)有时会给出与C函数不同的结果。

这意味着,如果我想了解C函数将做什么,我必须运行一些其他程序,这些程序实际上调用了C函数,例如(显然) python、perl、gnuplot或一个实际的C程序。

下面是一个在linux上的演示:

代码语言:javascript
运行
复制
bash$ uname -srvmpio
    Linux 3.13.0-95-generic #142-Ubuntu SMP Fri Aug 12 17:00:09 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
bash$ bash --version
    GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)
    Copyright (C) 2013 Free Software Foundation, Inc.
bash$ type printf
    printf is a shell builtin
bash$ printf "%.17g\n" .1
    0.1
bash$ /usr/bin/printf "%.17g\n" .1
    0.1
bash$ python -c 'print "%.17g" % .1'
    0.10000000000000001
bash$ perl -e 'printf("%.17g\n", .1);'
    0.10000000000000001
bash$ gnuplot -e 'print sprintf("%.17g", .1)'
    0.10000000000000001
bash$ echo -e '#include <stdio.h>\nint main(){printf("%.17g\\n",.1);}' | gcc -xc - -o /tmp/printf_.1 && /tmp/printf_.1
    0.10000000000000001

在其他一些平台上,例如最新的macbook,它们都给出了"0.10000000000000001“(我一直认为这是正确的答案):

代码语言:javascript
运行
复制
bash$ uname -srvmp
    Darwin 15.6.0 Darwin Kernel Version 15.6.0: Thu Jun 23 18:25:34 PDT 2016; root:xnu-3248.60.10~1/RELEASE_X86_64 x86_64 i386
bash$ bash --version
    GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
bash$ type printf
    printf is a shell builtin
bash$ printf "%.17g\n" .1
    0.10000000000000001
bash$ /usr/bin/printf "%.17g\n" .1
    0.10000000000000001

当然,我感到惊讶和失望的是,上面的命令并没有在任何特定的平台上提供完全相同的输出。我的问题是:根据某些官方规范,是否有一个正确的答案,在任何情况下,是否应该提交错误报告?

一些背景事实:

.1在C中表示IEEE双精度浮点数,其值在数学上准确:

代码语言:javascript
运行
复制
x  = 0.1000000000000000055511151231257827021181583404541015625

相邻的可表示值是:

代码语言:javascript
运行
复制
x- = 0.09999999999999999167332731531132594682276248931884765625
x+ = 0.10000000000000001942890293094023945741355419158935546875

因此,两个字符串表示"0.10000000000000001“和"0.1”都是无损的,因为它们都更接近x,而不是任何其他双精度值;也就是说,任何合理的字符串到双精度转换函数在给定任一字符串时都会产生x。所以这两个答案在这个意义上都是同样好的。

然而,0.10000000000000001比0.1is更接近x。我一直认为这意味着"0.10000000000000001“是正确的答案,但现在我不确定;也许这是一个依赖于实现的细节?

要了解为什么17是在任何情况下要使用的正确数字的原因,请参阅numeric_limits::max_digits10的文档:

digits10

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2005.pdf

EN

回答 1

Stack Overflow用户

发布于 2016-09-23 07:20:47

以下是关于C的一般情况,可能不适用于bashposix规范。

预计printf(" %.17f\n", .1)将至少打印DBL_DECIMAL_DIG正确的有效数字。DBL_DECIMAL_DIG是保证double到十进制文本表示返回到double并获得相同结果的精度。要做到这一点,至少有10位,通常是17位有效数字是正确的。这是OP的情况,OP当然很清楚。

代码语言:javascript
运行
复制
//0.09999999999999999167332731531132594682276248931884765625
//0.1000000000000000055511151231257827021181583404541015625
//0.10000000000000001942890293094023945741355419158935546875
//  12345678901234567
  0.10000000000000001  // DBL_DECIMAL_DIG is commonly 17

问题是,printf()不需要非常正确地进行任何操作。因此,printf()的质量将被认为是轻微可接受的,如果它打印了以下两种文字之一,因为0.1可以通过任何一种文本成功往返。

代码语言:javascript
运行
复制
//  12345678901234567
  0.10000000000000001 
  0.10000000000000000

浮点操作(+,-,*,/)和返回浮点结果的<math.h><complex.h>中的库函数的准确性是实现定义的,以及由库函数在<stdio.h><stdlib.h><wchar.h>中执行的浮点内部表示和字符串表示()之间转换的准确性。实现可能会说,准确性是未知的。C11dr§5.2.4.2.2 6

编辑

double可以具有不同的精度、基础、范围细节。为了简化,本文讨论的是一个样本值,而不是它的0.1,而是0.10000000000000000555.

@user694733 694733的评论是有用的,但它是在“推荐的实践”下使用的“应该是一个确切的.”。虽然它没有为printf("%.17f", .1)指定所希望的"0.10000000000000001“。它和上面的内容一起帮助回答了OP的问题:“应该打印什么?”,"0.10000000000000001“应该打印。但这种准确性是由C.

对于声称符合EEE浮点要求的C实现,我确信需要"0.10000000000000001“或"0.10000000000000000”的结果,但这超出了C问题的范围,进入了IEEE754-xxxx。我对IEEE浮点字符表示的理解允许一个符合要求的实现,即只需要往返double (实际上是binary64) --> text -> double。这至少需要15位有效位数,并且可能需要17位数。除了将文本转换为double以外,不指定超过15位数的数字将返回起始double

更深一点。对于IEEE754,0.100000000000000005551115123...值可以“错误地”打印为“0.1000000000001249000902”。但还是正确的往返。

代码语言:javascript
运行
复制
//  12345678901234567
//0.1000000000000000055511151231257827021181583404541015625
//0.10000000000000001249000902...  half way
//0.10000000000000001942890293094023945741355419158935546875

注意: IEEE允许将文本转换为double,忽略超过一定数量的重要数字(double为20)。

简短的IEEE回答:当将[C I]打印到各种精度时,输出应该在低于X的范围内。

代码语言:javascript
运行
复制
    12345678901234567
A 0.099999999999999991673327315... preceding X
B 0.099999999999999998612221219... half way
C 0.099999999999999998613          half way rounded to 20 sig digits toward x
D 0.099999999999999999             "%.17e" candidate
E 0.10000000000000000              "%.17e", "%.17f" candidate 
X 0.100000000000000005551115123... 
G 0.10000000000000001              "%.17e", "%.17f" candidate
H 0.10000000000000001249           half way rounded to 20 sig digits toward x 
I 0.100000000000000012490009027... half way
J 0.100000000000000019428902930... following X
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39652130

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档