是否有一个printf
宽度说明符可以应用于浮点说明符,它会自动将输出格式化为必要的 * 有效位数 *,以便在扫描字符串时获取原始浮点值?
例如,假设我打印一个float
,精度为2
个小数位:
float foobar = 0.9375;
printf("%.2f", foobar); // prints out 0.94
当我扫描输出0.94
时,我没有标准兼容的保证,我会得到原始的0.9375
浮点值(在这个例子中,我可能不会)。
我希望有一种方法告诉printf
自动将浮点值打印到必要的 * 有效位数 *,以确保它可以被扫描回传递给printf
的原始值。
我可以使用float.h
到derive the maximum width中的一些宏来传递给printf
,但是是否已经有一个指定符来自动打印到必要的 * 有效位数 * -或者至少打印到最大宽度?
9条答案
按热度按时间f3temu5u1#
我推荐@Jens Gustedt十六进制解决方案:使用%a。
OP希望“以最大精度(或至少到最高有效小数)打印”。
一个简单的例子是打印七分之一,如下所示:
但让我们深入挖掘。。
从数学上讲,答案是“0.142857 142857 142857...”,但我们使用的是有限精度浮点数。假设IEEE 754 double-precision binary。所以
OneSeventh = 1.0/7.0
的结果如下。还显示了前面和后面可表示的double
浮点数。打印
double
的 * 精确 * 十进制表示的用途有限。C在
<float.h>
中有两个宏家族可以帮助我们。第一组是要打印的十进制字符串中的 * 有效 * 位数,因此当扫描字符串时,我们得到原始浮点数。这里显示了C规范的 minimum 值和 sample C11编译器。
第二组是字符串可以被扫描到浮点中的 * 有效 * 位数,然后打印FP,仍然保留相同的字符串表示。这里显示了C规范的 minimum 值和 sample C11编译器。我相信可以用C99前。
第一组宏似乎满足OP的 * 有效 * 数字的目标。但这个宏并不总是可用的。
“+ 3”是我之前回答的症结所在。它的中心是如果知道往返转换字符串-FP-字符串(C89后可用的第2组宏),如何确定FP-字符串-FP(C89后可用的第1组宏)的数字?总的来说,加3是结果。
现在,要打印的 * 有效 * 位数是已知的,并通过
<float.h>
驱动。为了打印N * 有效 * 十进制数字,可以使用各种格式。
对于
"%e"
,precision 字段是前导位和小数点后的位数。所以- 1
是有序的。注意:此-1
不在初始int Digs = DECIMAL_DIG;
中对于
"%f"
,precision 字段是小数点后的位数。对于像OneSeventh/1000000.0
这样的数字,需要OP_DBL_Digs + 6
才能看到所有的 * 有效 * 位。注意:很多都使用
"%f"
。显示小数点后6位数字的; 6是显示的默认值,而不是数字的精度。lpwwtiir2#
无损打印浮点数的简短答案(这样它们就可以被读回完全相同的数字,除了NaN和Infinity):
printf("%.9g", number)
。printf("%.17g", number)
。不要使用
%f
,因为它只指定小数点后的有效位数,并且会截断小数。作为参考,幻数9和17可以在float.h
中找到,它定义了FLT_DECIMAL_DIG
和DBL_DECIMAL_DIG
。z9smfwbn3#
如果您只对位(resp十六进制模式)感兴趣,则可以使用
%a
。这保证您:如果存在以2为底的精确表示形式,则默认精度足以用于值的精确表示形式,否则,默认精度足以区分double类型的值。
我不得不补充说,这是只有自C99。
pxq42qpu4#
不,没有这样的printf宽度说明符来以最大精度打印浮点数。让我解释一下原因。
float
和double
的最大精度为变量,取决于float
或double
的实际值。回想一下,
float
和double
以sign.exponent.mantissa存储。这意味着小数比大数**的小数部分所用的位数要多得多。例如,
float
可以很容易地区分0.0和0.1。但是
float
不知道1e27
和1e27 + 0.1
之间的区别。这是因为所有精度(受尾数位数限制)都用于小数点左边的大部分数字。
%.f
修饰符只是说明你想从浮点数中打印多少个十进制值。事实上,* 可用的准确性取决于数字的大小 *,这取决于 * 作为程序员 * 来处理。printf
不能/不能为您处理。kninwzqo5#
只需使用来自
<float.h>
的宏和可变宽度转换说明符(".*"
):dgiusagp6#
在我对一个答案的评论中,我感叹道,我一直希望有某种方法可以以十进制形式打印浮点值中的所有有效数字,就像问题所问的那样。我终于坐下来写了。它并不十分完美,这是一段演示代码,可以打印附加信息,但在我的测试中基本上是有效的。请让我知道如果你(即)任何人)想要驱动它的整个 Package 器程序的副本以用于测试。
ffdz8vbo7#
我运行了一个小实验来验证使用
DBL_DECIMAL_DIG
打印确实可以精确地保留数字的二进制表示。事实证明,对于我尝试的编译器和C库,DBL_DECIMAL_DIG
确实是所需的位数,即使少一位打印也会产生严重的问题。我用微软的C编译器19.00.24215.1和gcc版本7.4.0 20170516(Debian 6.3.0-18+ deb 9 u1)运行这个。使用少一个十进制数位可以将比较完全相等的数字的数量减半。(我还验证了所使用的
rand()
确实产生了大约一百万个不同的数字。)下面是详细的结果。微软C
GCC
0s7z1bwu8#
据我所知,有一个很好的算法,允许 * 输出到必要的有效位数,这样当扫描字符串时,原始浮点值在大卫Gay编写的
dtoa.c
中获得 *,这在Netlib上是可用的here(另请参阅相关的paper)。该代码用于例如在Python、MySQL、Scilab和许多其他语言中。368yc8dk9#
为了补充Stéphane的答案,最先进的算法被称为“Dragon4”和“Grisu”。
在为自己研究这个问题时,我发现了这篇文章的背景信息,相关论文的链接和实现:
https://www.ryanjuckett.com/printing-floating-point-numbers/