首页 文章

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

提问于
浏览
1

我想将一个高精度整数(long或BigInteger)乘以一个小的double(考虑一些> 0和<1),并获得操作的精确算术值的算术舍入整数(long或BigInteger)作为结果 .

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

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

奖金问题:

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

4 回答

  • 0

    您想使用BigDecimal以保持精度 .

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

    使用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
    
  • 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);
    
  • 1

    Double有52位的精确度 . 怎么样:

    • 乘以你的双倍(1 << 52)

    • 将double转换为BigInteger(小数点左边的全精度没有损失)

    • 与其他BigIngeger相乘

    • 部分正确的结果二进制指数(BigInteger >> 51)

    • 如果很奇怪,可以通过添加1或BigInteger.Sign进行舍入(取决于您的舍入偏好)

    • 最后移位一位(BigInteger >> 1)

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

相关问题