Java double - 特定小数位后只有零

Java double - Only zeros after certain decimal place

我写了一个程序,它可以暴力破解带有很多小数位的平方根。这工作正常,但只能达到小数点后第 50 位。例如,当使用 12 作为该方法的输入时,小数点后第 50 位后,仅出现零。我很确定这不是预期的输出。

我不认为这是一个精度问题,因为我已经在使用 BigDecimals 进行计算了。这是我当前的代码:

static String solve(double num, int decimalPlaces) {
    BigDecimal toAdd = new BigDecimal("1.0");
    BigDecimal currentNumber = new BigDecimal("0.0");

    for(int i = 0; i <= decimalPlaces; i++) {
        currentNumber = currentNumber.setScale(i);

        System.out.print(currentNumber.toPlainString() + "          >> " + i + "\r");
        while(Math.pow((currentNumber.add(toAdd).doubleValue()), 2) <= num) {
            currentNumber = currentNumber.add(toAdd);
        }

        toAdd = toAdd.divide(BigDecimal.TEN);
    }

    return currentNumber.toPlainString();
}

当前输出(当num = 12decimalPlaces = 80):

3.46410161513775460839781317190499976277351379394531250000000000000000000000000000

您知道为什么会这样以及可能的解决方案吗?

tl;博士

  • 如果您想要宽 range/precision 的准确结果,请消除对浮点类型 double/Doublefloat/Float 的任何使用。
  • 仅使用 BigDecimal 个对象。

仅使用 BigDecimal,不使用 double

当你涉及一个 double 原始值或一个 Double 对象时,你 loose accuracy due to floating-point 数学。

您的方法将 double 作为其名为 num 的第一个参数。如果您想要准确性和 range/precision,请改用 BigDecimal

static String solve( BigDecimal num, int decimalPlaces ) { … }

以这种方式调用该方法。请注意,我们将十二作为字符串文字而不是数字文字传递。

String result = Whatever.solve ( new BigDecimal( "12" ) , 80 ) ;

您不能对对象使用比较器语法,例如 <=。您的行 while(Math.pow((currentNumber.add(toAdd).doubleValue()), 2) <= num) 应该改为调用方法 BigDecimal::pow.

小问题…您的行 System.out.print( currentNumber.toPlainString() 应该是 System.out.println( …,调用 println 而不是 print

这可能与您寻找的代码很接近。我不是数学高手所以我不能保证逻辑。

static String solve ( BigDecimal num , int decimalPlaces )
{
    BigDecimal toAdd = new BigDecimal( "1.0" );
    BigDecimal currentNumber = new BigDecimal( "0.0" );

    for ( int i = 0 ; i <= decimalPlaces ; i++ )
    {
        currentNumber = currentNumber.setScale( i );

        System.out.println( currentNumber.toPlainString() + "          >> " + i + "\r" );
        while ( currentNumber.add( toAdd ).pow( 2 ).compareTo( num ) <= 0 )
        {
            currentNumber = currentNumber.add( toAdd );
        }

        toAdd = toAdd.divide( BigDecimal.TEN );
    }

    return currentNumber.toPlainString();
}

看到这个code run live on IdeOne.com

0          >> 0
3.0          >> 1
3.40          >> 2
3.460          >> 3
3.4640          >> 4
3.46410          >> 5
3.464100          >> 6
3.4641010          >> 7
3.46410160          >> 8
3.464101610          >> 9
3.4641016150          >> 10
3.46410161510          >> 11
3.464101615130          >> 12
3.4641016151370          >> 13
3.46410161513770          >> 14
3.464101615137750          >> 15
3.4641016151377540          >> 16
3.46410161513775450          >> 17
3.464101615137754580          >> 18
3.4641016151377545870          >> 19
3.46410161513775458700          >> 20
3.464101615137754587050          >> 21
3.4641016151377545870540          >> 22
3.46410161513775458705480          >> 23
3.464101615137754587054890          >> 24
3.4641016151377545870548920          >> 25
3.46410161513775458705489260          >> 26
3.464101615137754587054892680          >> 27
3.4641016151377545870548926830          >> 28
3.46410161513775458705489268300          >> 29
3.464101615137754587054892683010          >> 30
3.4641016151377545870548926830110          >> 31
3.46410161513775458705489268301170          >> 32
3.464101615137754587054892683011740          >> 33
3.4641016151377545870548926830117440          >> 34
3.46410161513775458705489268301174470          >> 35
3.464101615137754587054892683011744730          >> 36
3.4641016151377545870548926830117447330          >> 37
3.46410161513775458705489268301174473380          >> 38
3.464101615137754587054892683011744733880          >> 39
3.4641016151377545870548926830117447338850          >> 40
3.46410161513775458705489268301174473388560          >> 41
3.464101615137754587054892683011744733885610          >> 42
3.4641016151377545870548926830117447338856100          >> 43
3.46410161513775458705489268301174473388561050          >> 44
3.464101615137754587054892683011744733885610500          >> 45
3.4641016151377545870548926830117447338856105070          >> 46
3.46410161513775458705489268301174473388561050760          >> 47
3.464101615137754587054892683011744733885610507620          >> 48
3.4641016151377545870548926830117447338856105076200          >> 49
3.46410161513775458705489268301174473388561050762070          >> 50
3.464101615137754587054892683011744733885610507620760          >> 51
3.4641016151377545870548926830117447338856105076207610          >> 52
3.46410161513775458705489268301174473388561050762076120          >> 53
3.464101615137754587054892683011744733885610507620761250          >> 54
3.4641016151377545870548926830117447338856105076207612560          >> 55
3.46410161513775458705489268301174473388561050762076125610          >> 56
3.464101615137754587054892683011744733885610507620761256110          >> 57
3.4641016151377545870548926830117447338856105076207612561110          >> 58
3.46410161513775458705489268301174473388561050762076125611160          >> 59
3.464101615137754587054892683011744733885610507620761256111610          >> 60
3.4641016151377545870548926830117447338856105076207612561116130          >> 61
3.46410161513775458705489268301174473388561050762076125611161390          >> 62
3.464101615137754587054892683011744733885610507620761256111613950          >> 63
3.4641016151377545870548926830117447338856105076207612561116139580          >> 64
3.46410161513775458705489268301174473388561050762076125611161395890          >> 65
3.464101615137754587054892683011744733885610507620761256111613958900          >> 66
3.4641016151377545870548926830117447338856105076207612561116139589030          >> 67
3.46410161513775458705489268301174473388561050762076125611161395890380          >> 68
3.464101615137754587054892683011744733885610507620761256111613958903860          >> 69
3.4641016151377545870548926830117447338856105076207612561116139589038660          >> 70
3.46410161513775458705489268301174473388561050762076125611161395890386600          >> 71
3.464101615137754587054892683011744733885610507620761256111613958903866030          >> 72
3.4641016151377545870548926830117447338856105076207612561116139589038660330          >> 73
3.46410161513775458705489268301174473388561050762076125611161395890386603380          >> 74
3.464101615137754587054892683011744733885610507620761256111613958903866033810          >> 75
3.4641016151377545870548926830117447338856105076207612561116139589038660338170          >> 76
3.46410161513775458705489268301174473388561050762076125611161395890386603381760          >> 77
3.464101615137754587054892683011744733885610507620761256111613958903866033817600          >> 78
3.4641016151377545870548926830117447338856105076207612561116139589038660338176000          >> 79
3.46410161513775458705489268301174473388561050762076125611161395890386603381760000          >> 80
result: 3.46410161513775458705489268301174473388561050762076125611161395890386603381760007

您在 Math.pow((currentNumber.add(toAdd).doubleValue()), 2) 中将您的大小数转换为双精度,而您在这里失去了精度。只需在 BigDecimal 本身上使用方法 pow 即可产生能量。

这是更正后的代码,似乎对我有用:

static String solve(double num, int decimalPlaces) {
    BigDecimal toAdd = new BigDecimal("1.0");
    BigDecimal currentNumber = new BigDecimal("0.0");
    BigDecimal numBD = BigDecimal.valueOf(num);

    for (int i = 0; i <= decimalPlaces; i++) {
        currentNumber = currentNumber.setScale(i);

        System.out.print(currentNumber.toPlainString() + "          >> " + i + "\r");
        while (currentNumber.add(toAdd).pow(2).compareTo(numBD) <= 0) {
            currentNumber = currentNumber.add(toAdd);
        }

        toAdd = toAdd.divide(BigDecimal.TEN);
    }

    return currentNumber.toPlainString();
}

作为一个额外的好处,这段代码实际上更简单一些。