【问题标题】:printing float, preserving precision打印浮点数,保持精度
【发布时间】:2012-06-09 08:22:27
【问题描述】:

我正在编写一个程序,它打印浮点文字以在另一个程序中使用。

为了保持原始浮点数的精度,我需要打印多少位?

由于浮点数具有 24 * (log(2) / log(10)) = 7.2247199 十进制数字精度,我最初的想法是打印 8 位数字就足够了。但如果我不走运,那些0.2247199 会分布在 7 个有效数字的左侧和右侧,所以我应该打印 9 个十进制数字。

我的分析正确吗?对于所有情况,9 个十进制数字是否足够?喜欢printf("%.9g", x);

在 7 或 8 就足够的情况下,是否有一个标准函数可以将浮点数转换为具有该值所需的最小小数位数的字符串,所以我不打印不必要的数字?

注意:我不能使用十六进制浮点文字,因为标准 C++ 不支持它们。

【问题讨论】:

  • 使用 1000 位数字并剪掉尾随的零! ;)
  • 因为您无法将基于二进制的浮点数转换为十进制小数而不会出错,所以我建议只转储二进制表示(或单独的尾数 + 指数)。
  • @Vlad 你不能吗?不是所有的二进制分数都可以表示为有限小数吗?
  • @Fred:不会,但至少这个表示是准确的,所以你可以在另一个程序中选择它并从中生成完全相同的浮点值。
  • @FredOverflow:你能澄清一下这里的目标吗?是要获得浮点数的精确十进制表示吗? (如果是这样,R.Martinho 就在正确的轨道上。)还是打印到足够的精度,以便可以明确地将其解析回原始浮点值?

标签: c++ c string floating-point floating-accuracy


【解决方案1】:

Java 中使用的浮点到十进制转换保证产生的小数点后的十进制位数最少,需要将数字与其邻居区分开(或多或少)。

你可以从这里复制算法:http://www.docjar.com/html/api/sun/misc/FloatingDecimal.java.html 注意FloatingDecimal(float)构造函数和toJavaFormatString()方法。

【讨论】:

  • 算法究竟从那 2800 多行代码的什么位置开始?
  • @Gabe 从构造函数开始,到我指出的方法结束。完整的字符串由表达式 new FloatingDecimal(number).toJavaFormatString() 返回。
  • @sehe 我不是说你应该复制代码,只是复制算法。
  • 这些函数都不包含任何明显的方法来确定所需的小数位数。
【解决方案2】:

您可以使用sprintf。我不确定这是否完全回答了你的问题,但无论如何,这里是示例代码

#include <stdio.h>
int main( void )
{
float d_n = 123.45;
char s_cp[13] = { '\0' };
char s_cnp[4] = { '\0' };
/*
* with sprintf you need to make sure there's enough space
* declared in the array
*/
sprintf( s_cp, "%.2f", d_n );
printf( "%s\n", s_cp );
/*
* snprinft allows to control how much is read into array.
* it might have portable issues if you are not using C99
*/
snprintf( s_cnp, sizeof s_cnp - 1 , "%f", d_n );
printf( "%s\n", s_cnp );
getchar();
return 0;
}
/* output :
* 123.45
* 123
*/

【讨论】:

  • 这是一种明智的方法,但我们发现使用 sprintf 的一件事是,不同平台的舍入可能不同。
【解决方案3】:

24 * (log(2) / log(10)) = 7.2247199

这很能代表这个问题。以 0.0000001 位的精度表示有效位数是没有意义的。您将数字转换为文本是为了人类,而不是机器。如果你写的话,人类不会关心,而且会更喜欢

24 * (log(2) / log(10)) = 7

试图显示 8 个有效数字只会产生随机噪声数字。对于非零赔率,7 已经太多了,因为浮点误差在计算中累积。最重要的是,使用合理的计量单位打印数字。人们对毫米、克、磅、英寸等感兴趣。没有建筑师会关心比 1 毫米更精确的窗户尺寸。没有任何窗户制造厂会承诺尺寸如此精确的窗户。

最后但并非最不重要的一点是,您不能忽略输入程序的数字的准确性。将空载的欧洲燕子的速度测量到 7 位数是不可能的。它大约是每秒 11 米,最多 2 位数。因此,以该速度执行计算并打印具有更多个有效数字的结果会产生荒谬的结果,这些结果承诺不存在的准确性。

【讨论】:

  • 在另一个程序中使用 =>看来你关于人类阅读输出的假设是错误的。
  • 嗯,确实没有注册。奇怪的事情。好吧,很容易解决,只要人类永远不会看到它,然后打印大量数字。
  • 我讨厌它看起来我的大脑删除了文本中的一个导入词:x 看起来 Fred 也担心空间消耗(我猜是性能)。
【解决方案4】:

类似的东西

def f(a):
    b=0
    while a != int(a): a*=2; b+=1
    return a, b

(即 Python)您应该能够以无损失的方式获取尾数和指数。

在 C 中,这可能是

struct float_decomp {
    float mantissa;
    int exponent;
}

struct float_decomp decomp(float x)
{
    struct float_decomp ret = { .mantissa = x, .exponent = 0};
    while x != floor(x) {
        ret.mantissa *= 2;
        ret.exponent += 1;
    }
    return ret;
}

但请注意,并非所有值都可以用这种方式表示,这只是一个快速的想法,应该可以给出想法,但可能需要改进。

【讨论】:

  • 不加任何注释的-1非常有帮助。
【解决方案5】:

如果程序要被计算机读取,我会使用char* 别名的简单技巧。

  • 别名float*char*
  • 通过char* 别名复制到unsigned(或任何足够大的无符号类型)
  • 打印unsigned

解码只是颠倒过程(在大多数平台上,可以直接使用reinterpret_cast)。

【讨论】:

  • 这会以何种方式给出浮点字面量?当然,您总是可以使用二进制表示来获得精确的精度,而且空间很小,但是...
  • @ChristianRau:我的想法是挑战最初的要求,即两个程序交换浮点数需要文字表示。由于格式是规范化的(并且由标准强制),使用二进制表示是可移植的(在潜在的字节序问题上)。
  • @dbaupp:实际上是的(因此reinterpret_cast 位)。理论上,别名为 char* 以外的其他类型是未定义的行为。
  • @MatthieuM。所以这只是一个“做不同的事情”评论,伪装成一个答案?
  • @ChristianRau:YMMV。大多数 SO 问题都受到XY problem 的影响。最初的问题是在两个程序之间交换浮点文字,并且 OP 似乎假设需要人类可读的格式,但除了对标准的引用之外,没有提供任何其他理由,可能需要明确定义的行为。因此,我提出了一个解决方案,该解决方案可以在指定的原因范围内完成交换数据的目标,但在其他方面忽略了 OP 似乎强加给自己的自我限制。
【解决方案6】:

为了保证二进制->十进制->二进制往返恢复原始二进制值,IEEE 754要求


The original binary value will be preserved by converting to decimal and back again using:[10]

    5 decimal digits for binary16
    9 decimal digits for binary32
    17 decimal digits for binary64
    36 decimal digits for binary128

For other binary formats the required number of decimal digits is

    1 + ceiling(p*log10(2)) 

where p is the number of significant bits in the binary format, e.g. 24 bits for binary32.

在 C 中,可用于这些转换的函数是 snprintf() 和 strtof/strtod/strtold()。

当然,在某些情况下,甚至更多的数字可能是有用的(不,它们并不总是“噪音”,这取决于十进制转换例程的实现,例如 snprintf() )。考虑例如printing dyadic fractions.

【讨论】:

  • +1 对于经过标准验证的答案(尽管假设是符合 IEEE 的实现,但无论如何谁不使用 IEEE 浮点数)。
  • @ChristianRau:现在每个人或多或少都在使用 IEEE 浮点数,是的。但是,需要注意的是您的十进制转换函数是否对所有输入都正确舍入,这可能不太确定。但是,如果您需要十进制表示,则无能为力(除了实现自己的无错误十进制转换,祝您好运!)。
  • @ChristianRau, janneb:这不是通用的。我仍然偶尔需要处理以 IBM 浮点格式生成的地震数据!
  • 它们是噪音,因为它们没有提供任何额外的精度;也就是说,3.1415927f3.1415927410125732421875f 表示完全相同的 float 值。 927后面的数字完全由前面的数字决定;他们不提供任何新信息。那么为什么要打印它们,如果目的是产生最短的 float 文字可能呢?
  • @FredOverflow:关于噪音的观点并不是要直接回答您的问题,而是更多的一般观点,有时比二进制->十进制->二进制往返所需的数字更多有用。
【解决方案7】:

如果您有一个符合 C99 的 C 库(并且如果您的浮点类型的基数是 2 的幂:)printf 格式字符 %a 可以打印浮点值而不会缺少精度十六进制格式,scanfstrod 等实用程序将能够读取它们。

【讨论】:

    【解决方案8】:

    如果您阅读这些论文(见下文),您会发现有一些算法可以打印最少的十进制数字,以便可以不加改变地重新解释该数字(即通过 scanf)。

    由于可能有几个这样的数字,算法也会选择最接近原始二进制分数的十进制分数(我命名为浮点值)。

    可惜C中没有这样的标准库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-25
      • 2020-08-04
      • 2015-02-02
      • 1970-01-01
      • 2017-01-19
      • 1970-01-01
      • 2019-02-27
      相关资源
      最近更新 更多