1

我想将一个高精度整数(long 或 BigInteger)乘以一个小的 double(想想 > 0 和 < 1 的东西),并得到运算的精确算术值的算术舍入整数(long 或 BigInteger)作为结果。

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

将整数转换为双精度,然后乘以并将结果转换回整数也不起作用,因为双精度不够精确。当然,您可能会争辩说,由于双操作数一开始就不够精确,因此结果在相同数量级上不精确可能无关紧要,但在这种情况下,它确实如此。

奖金问题:

使用 BigDecimal 有效,但似乎效率很低。将 long 转换为 double,然后乘以并转换回来似乎比转换为 BigDecimal 快 500 倍(尽管会丢失精度)。有没有更有效的可能性?将几个不同的 long 与相同的 double 相乘是否可以获得性能?

4

4 回答 4

2

您想使用BigDecimal以保持精度。

BigInteger myBI = new BigInteger("99999999999999999");
Double d = 0.123;
BigDecimal bd = new BigDecimal(myBI);
BigDecimal result = bd.multiply(BigDecimal.valueOf(d));
于 2013-05-14T13:05:58.107 回答
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
于 2013-05-15T07:54:57.233 回答
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);
于 2013-05-14T12:57:30.257 回答
0

Double 的精度为 52 位。怎么样:

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

    BigInteger myBI = BigInteger("99999999999999999");
    双 d = 0.123;
    BigInteger bigDouble = (BigInteger)(d * ((ulong)1 << 52));
    BigInteger 结果 = (myBI * bigDouble) >> 51; if (!result.IsEven)
    结果 += result.Sign; 结果=结果>>1;

于 2015-03-18T23:33:14.537 回答