将long或BigInteger乘以double而不会损失精度

时间:2013-05-14 12:42:31

标签: java double precision long-integer biginteger

我想将一个高精度整数(long或BigInteger)乘以一个小的double(想想一些> 0和< 1)并得到算术圆整数(long或BigInteger)的精确算术值结果。

将double转换为整数不起作用,因为它的小数值会丢失。

将整数转换为double,然后乘以并将结果转换回整数也不起作用,因为double不够精确。 当然你可以争辩说,因为双操作数首先不够精确,所以结果在数量级上不精确可能并不重要,但在这种情况下,确实如此。

奖金问题:

使用BigDecimal有效,但似乎效率很低。将long转换为double然后再乘以并转换回来似乎比转换为BigDecimal要快500倍(尽管精度下降)。有更有效的可能性吗?当将几个不同的长度乘以相同的双倍时,是否有可能获得性能?

4 个答案:

答案 0 :(得分:3)

您希望使用BigDecimal以保持精确度。

BigInteger myBI = new BigInteger("99999999999999999");
Double d = 0.123;
BigDecimal bd = new BigDecimal(myBI);
BigDecimal result = bd.multiply(BigDecimal.valueOf(d));

答案 1 :(得分:1)

使用BigDecimal确实有效。你仍然要小心使用double表示的精确值并进行算术舍入。

    BigInteger myBI = new BigInteger("1000000000000000000000000000000000000000000000000000000");
    double d = 0.1;
    BigDecimal bd = new BigDecimal(myBI);

    BigInteger doubleWithStringValue = bd.multiply(BigDecimal.valueOf(d)).toBigInteger();

    BigDecimal bdresult = bd.multiply(new BigDecimal(d));
    BigInteger unrounded = bdresult.toBigInteger();
    BigInteger correct = bdresult.add(new BigDecimal("0.5")).toBigInteger(); // this way of rounding assumes positive numbers
    BigInteger lostprecision = new BigDecimal(myBI.doubleValue() * d).toBigInteger();

    System.out.println("DoubleString:   " + doubleWithStringValue);

    System.out.println("Unrounded:      " + unrounded);
    System.out.println("Correct:        " + correct);
    System.out.println("Lost precision: " + lostprecision);

输出:

DoubleString:   100000000000000000000000000000000000000000000000000000
Unrounded:      100000000000000005551115123125782702118158340454101562
Correct:        100000000000000005551115123125782702118158340454101563
Lost precision: 100000000000000020589742799994816764107083808679919616

答案 2 :(得分:0)

我能看到的最佳解决方案是使用Math.round函数。用这样的代码。

long l; //your long value
double d;//your fraction
long answer;

answer = Math.round((double)(l * d));

这将为您提供答案而不会丢失预防错误。 另一种选择是截断它。

与上述代码相同。

String s;

s = "" + (l*d);
StringTokenizer token = new StringTokenizer(s);
s = token.nextToken();
answer = Long(s);

答案 3 :(得分:0)

Double具有52位的精度。怎么样:

  1. 将您的双倍乘以(1 <&lt; <52)
  2. 将double转换为BigInteger(小数点左边没有丢失,因为全精度)
  3. 与其他BigIngeger相乘
  4. 部分正确的结果二进制指数(BigInteger&gt;&gt; 51)
  5. 如果有奇怪的话,可以通过添加1或BigInteger.Sign进行舍入(取决于您对舍入的偏好)
  6. 最后再将结果移位一位(BigInteger&gt;&gt; 1)

    BigInteger myBI = BigInteger(&#34; 99999999999999999&#34;);
    双d = 0.123;
    BigInteger bigDouble =(BigInteger)(d *((ulong)1&lt;&lt; 52));
    BigInteger结果=(myBI * bigDouble)&gt;&gt; 51; if(!result.IsEven)
        结果+ =结果.Sign; result = result&gt;&gt; 1;