我想将一个高精度整数(long或BigInteger)乘以一个小的double(想想一些> 0和< 1)并得到算术圆整数(long或BigInteger)的精确算术值结果。
将double转换为整数不起作用,因为它的小数值会丢失。
将整数转换为double,然后乘以并将结果转换回整数也不起作用,因为double不够精确。 当然你可以争辩说,因为双操作数首先不够精确,所以结果在数量级上不精确可能并不重要,但在这种情况下,确实如此。
奖金问题:
使用BigDecimal有效,但似乎效率很低。将long转换为double然后再乘以并转换回来似乎比转换为BigDecimal要快500倍(尽管精度下降)。有更有效的可能性吗?当将几个不同的长度乘以相同的双倍时,是否有可能获得性能?
答案 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位的精度。怎么样:
最后再将结果移位一位(BigInteger>> 1)
BigInteger myBI = BigInteger(" 99999999999999999");
双d = 0.123;
BigInteger bigDouble =(BigInteger)(d *((ulong)1<< 52));
BigInteger结果=(myBI * bigDouble)>> 51;
if(!result.IsEven)
结果+ =结果.Sign;
result = result>> 1;