【问题标题】:Can 12.1 be represented exactly as a floating point number?12.1 可以精确地表示为浮点数吗?
【发布时间】:2010-12-26 15:14:05
【问题描述】:

这是指this question中的cmets:

Java 中的这段代码生成 12.100000000000001,这是使用 64 位双精度数,可以精确地呈现 12.1。 – 热解

这是真的吗?我觉得由于浮点数表示为 2 的幂的和,所以无论你有多少位,你都不能准确地表示 12.1。但是,当我实现了这两种算法并打印了使用 (12.1, 3) 和许多有效数字调用它们的结果时,我分别得到了他和我的结果:

12.100000000000000000000000000000000000000000000000000000000000000000000000000 12.10000000000000100000000000000000000000000000000000000000000000000000000000

我使用String.format("%76f") 打印了这个。我知道这比必要的零多,但我在 12.1 中看不到任何舍入。

【问题讨论】:

  • 能否请您发布打印这两个数字的代码,因为我不得不说我相信其他 cmet 说您不能准确表示 12.1,以及您要打印的位数应该在某个时候显示刚好超过或低于。如果双精度数有 53 位尾数,那么您可能会看到与第 14 位有效数字之间的差异。
  • 这是该问题的前两个答案 - 我不想在这里重复自己。
  • 如果你们都使用strictfp 并再次比较结果会怎样?在这种情况下,结果应该与 VM 无关。 (我没有声称没有strictfp 语言规范允许不同的结果。不知道不关心。无论如何,浮点数意味着不精确的值)
  • 我认为您的问题当前设置方式的正确答案在这里 stackoverflow.com/questions/1904321/… 。你的意思是你能在 java double 中准确地表示 12.1 吗?
  • 我看了那个问题,没有看到任何打印数字的代码,只是计算数字的代码。

标签: java math floating-point numbers floating-accuracy


【解决方案1】:

没有。正如其他人在他评论的后续文章中指出的那样,(有限数量的)2 的幂之和永远不会恰好等于 12.1。就像你不能精确地表示以十为底的 1/3,无论你在小数点后使用多少位。

【讨论】:

  • 为什么打印的数字那么准确?第一个答案肯定比第二个更准确。
  • 嘿,“其他人”跟进的是我=P。
  • @Claudio - 据我所知,您将 12.1 的 double 近似值四舍五入到一个数字......当转换回十进制时......非常接近 12.1。这并不奇怪。
  • @Claudio - 如果生成的十进制数字恰好是 12.1,这绝对是您用于将 double 转换为十进制字符串的库例程的人工制品。它四舍五入为数字的“人类友好”版本,而不是显示与您提供的 double 数学上最接近的十进制数字。
【解决方案2】:

在二进制中,12.1 是:

1100.000110011001100110011...

由于 this 不会终止,因此它不能用 double 或任何其他有限宽度二进制浮点类型的 53 个有效位精确表示。

【讨论】:

    【解决方案3】:

    尝试用二进制表示 0.1:
    0.5 太大
    0.25 太大
    0.125 太大
    0.0625 适合,余数为 0.0375
    0.03125 适合,余数为 0.00625
    0.015625 太大
    0.0078125 太大
    0.00390625 适合,余数为 0.00234375
    0.001953125 适合,余数为 0.000390625

    它将无限期地重复,创建一个以 2 为底的值 0.00011001100...

    不,它不能精确地用双精度表示。如果 Java 支持 BCD 或定点十进制,那将完全可以工作。

    【讨论】:

    • Java 确实支持小数,参见 java.math.BigDecimal。
    【解决方案4】:

    不是二进制的,不。如果您允许我幻想,您可以使用“浮点二进制编码的十进制”(据我所知,从未实现过):

    12.1 = 0000 . 0001 0010 0001 * (10^2)
    

    在二进制中,所有非零值的格式为1.xyz * m,而 IEEE 格式利用这一点省略了前导 1。我不确定 FP-BCD 的等价物是什么,所以我走了取而代之的是 0.xyz * m 形式的值。

    【讨论】:

    • '"浮点二进制编码的十进制"(据我所知,从未实现过)'——哎呀,你需要阅读你的历史记录。它是在大约 50 年前实施的。
    • P.S.也就是说,它是在大约 50 年前在硬件中实现的,尽管在此之前和之后它也在软件中实现。
    • 这很有趣,事后看来并不奇怪。不过,我所说的仍然是正确的:“据我所知”等等;-)
    • 实际上 IEEE 754 浮点标准以密集十进制(10 位 = 3 位)定义了十进制浮点格式。不过,我还没有在野外见过那些。 en.wikipedia.org/wiki/IEEE_754-2008#Basic_formats
    • @starblue -- 感谢您提供的信息。更有效地打包东西是有意义的(实际上是 2 位)。当它以 10^3 为底时,您是否可以诚实地称其为“十进制”是另一回事... ;-)
    【解决方案5】:

    我建议阅读What Every Computer Scientist Should Know About Floating Point Arithmetic。那你就肯定知道了。 :)

    【讨论】:

    • java 程序员应该知道的关于浮点数的一切:它们并不精确:) 这对我来说已经足够了。
    • @irreputable 什么?它们非常精确。它们是由一些最聪明的人制作的,尽可能精确和准确,即使在使用不当和编程不佳的情况下也是如此。它们只是不是无限精确的。
    【解决方案6】:

    查看双精度的方法是将其转换为 BigDecimal。

    // prints 12.0999999999999996447286321199499070644378662109375
    System.out.println(new BigDecimal(12.1));
    

    【讨论】:

      【解决方案7】:

      是的,您可以用浮点数精确表示 12.1。 您只需要十进制浮点表示,而不是二进制表示。

      使用 BigDecimal 类型,您将准确地表示它!

      【讨论】:

      • 浮点数专门指 IEEE 754 规范...... BigDecimal 也不是浮点数
      • 不,这是浮点的一种实现;浮点通常是指使用滑动表示的任何实现。 big decimal 既是浮点实现又是任意精度实现。此外,IEEE 754 规范确实指定了可以精确保存 12.1 的十进制浮点表示,以及名称为 Decimal32 的更知名的二进制表示。
      • 哦有趣,我从来不知道十进制格式!谢谢。是否有任何硬件支持使用该格式?
      • 我使用过的硬件十进制浮点实现的唯一硬件是 IBM 的 POWER6 和 POWER6+ 架构。
      【解决方案8】:

      不,十进制数 12.1 不能表示为有限(终止)二进制浮点数。

      记住12.1 是有理数121/10。请注意,这个分数是最低的(不能通过去除分子和分母的共同因素来减少)。

      假设(为了达到一个矛盾)121/10 也可以写成n / (2**k) 其中nk 是一些正整数,而2**k 表示k 两个的幂.我们会有一个unique factorization 的反例。特别是

      10 * n == 2**k * 121
      

      左边能被 5 整除,右边不能被 5 整除。

      【讨论】:

        【解决方案9】:

        您可以使用的一个选项是不存储 v=0.1,而是存储 v10=1。只需在需要时除以 10(除法会在结果中产生截断错误,但 v 仍然可以)

        在这种情况下,您基本上是在进行定点破解,但将数字保持在浮点数中。 但除非你真的必须这样做,否则通常不值得这样做。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-03-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-20
          • 2021-09-03
          • 1970-01-01
          • 2011-11-17
          相关资源
          最近更新 更多