浮点算法不能产生精确的结果

31 java floating-point

我需要在Java中进行一些浮点运算,如下面的代码所示:

public class TestMain {
    private static Map<Integer, Double> ccc = new HashMap<Integer, Double>() {
      { put(1, 0.01); put(2, 0.02); put(3, 0.05); put(4, 0.1); put(6, 0.2);
        put(10, 0.5); put(20, 1.0); put(30, 2.0); put(50, 5.0); put(100, 10.0);
      }
    };

    Double increment(Double i, boolean up) {
        Double inc = null;

        while (inc == null) {
            inc = ccc.get(i.intValue());

            if (up)
                --i;
            else
                ++i;
        }
        return inc;
    }

    public static void main(String[] args) {
        TestMain tt = new TestMain();

        for (double i = 1; i < 1000; i += tt.increment(i, true)) {
            System.out.print(i + ",");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是为了模拟Betfair微调器小部件作为输出给出的值的范围.

Java中的浮点运算似乎会引入一些意外错误.例如,我得到2.180000000000001而不是2.18.浮点数的用途是什么,你不能相信对它们执行的算术结果?我该如何解决这个问题?

Jon*_*eet 33

如果您需要精确的十进制值,则应使用java.math.BigDecimal.然后阅读"每个计算机科学家应该知道什么关于浮点运算"的背景知识为什么你得到这些结果.

(我有一篇以.NET为中心的文章,你可能会发现它更容易阅读 - 当然也更短.为了理解这个问题,Java和.NET之间的差异大多不相关.)

  • @Anthony:是的,但如果你实际上*需要*精确的十进制值,那么慢和右比快速和错误更好. (22认同)
  • 请记住,`BigDecimal`将比普通浮点算法慢很多. (6认同)
  • 在这种情况下,您也可以使用整数作为美分.你必须记住,218意味着2.18,你需要做一些额外的打印工作. (4认同)
  • 我喜欢数学,甚至我也无法完成那篇文章.我希望人们不要发布它 - 它不是一个很好的参考,除非你想读一本小书只是为了理解浮点数是如何工作的. (3认同)

Jay*_*Jay 11

浮点数使用二进制分数而不是小数分数.也就是说,你习惯于由十分位数,百分位数,千分位数等组成的小数分数.d1/10 + d2/100 + d3/1000 ......但是浮点数是二进制的,所以他们有半位数,四分之一位数,第八位数等.d1/2 + d2/4 + d3/8 ......

许多小数部分不能精确地表示为任何有限数量的二进制数字.例如,1/2是没有问题的:在十进制中它是.5,在二进制中它是.1.3/4是十进制.75,二进制.11.但是1/10是一个干净的.1(十进制),但是二进制它是.0001100110011 ......"0011"永远重复.由于计算机只能存储有限数量的数字,因此必须将其切断,因此答案并不准确.当我们在输出时转换回十进制时,我们得到一个奇怪的数字.

正如Jon Skeet所说,如果你需要精确的小数,请使用BigDecimal.如果性能是一个问题,您可以滚动自己的小数部分.就像,如果你知道你总是想要3个小数位并且数字不会超过一百万左右,你可以简单地使用带有假设3位小数的int,在你算术和写输出时根据需要进行调整format函数在正确的位置插入小数点.但99%的时间表现不是一个值得麻烦的大问题.