Java двойные нули только после определенного десятичного знака

#java #precision

#java #точность

Вопрос:

Я написал программу, которая должна перебирать квадратный корень с большим количеством знаков после запятой. Это работает нормально, но только до тех пор, пока не будет достигнут 50-й десятичный знак. Например, при использовании 12 в качестве входных данных для метода, после 50-го знака после запятой появляются только нули. Я почти уверен, что это не ожидаемый результат.

Я не думаю, что это проблема точности, потому что я уже использую BigDecimal s для вычислений. Вот мой текущий код:

 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 = 12 и decimalPlaces = 80 ):

 3.46410161513775460839781317190499976277351379394531250000000000000000000000000000
  

Есть ли у вас какие-либо идеи, почему это так, и возможные решения?

Комментарии:

1.Вы используете BigDecimal but также double значения во время вычисления. Это означает, что на используемые части double также повлияет его ограниченная точность. Например Math.pow() , вызов работает со double значениями. Вместо этого вы должны использовать BigDecimal.pow() . И просто помните: как только вы вызываете doubleValue() куда угодно, скорее всего, вы потеряли большую часть преимуществ использования BigDecimal .

2. Вы хотели бы выполнить все вычисления, используя BigDecimal , но вам нужно иметь в виду, что BigDecimal требуется, чтобы вы определили (максимальную) точность и правило округления, чтобы сохранить эту точность, особенно для умножения и деления.

Ответ №1:

tl; dr

  • Исключите любое использование типов с плавающей запятой double / Double и float / Float , если вы хотите получить точные результаты с широким диапазоном / точности.
  • Используйте только BigDecimal объекты.

Использовать только BigDecimal , без double

В тот момент, когда вы используете double примитивное значение или Double объект, вы теряете точность из-за математики с плавающей запятой.

Ваш метод принимает a double в качестве своего первого аргумента с именем num . Если вам нужна точность и диапазон / точность, BigDecimal вместо этого возьмите a .

 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();
}
  

Посмотрите, как этот код выполняется в реальном времени 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
  

Комментарии:

1. Это System.out.print() потому, что я хочу, чтобы он редактировал строку ( r ). В фактическом коде есть задержка в цикле for, чтобы заставить его построить результат ;). В любом случае, ваши подсказки заставили это работать, спасибо!

Ответ №2:

Вы преобразуете свой большой десятичный знак в double in Math.pow((currentNumber.add(toAdd).doubleValue()), 2) , и здесь вы теряете точность. Просто используйте метод pow для самого BigDecimal для получения мощности.

Это исправленный код, который, кажется, работает для меня:

 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();
}
  

В качестве дополнительного преимущества этот код на самом деле несколько проще.

Комментарии:

1. Извините, я сделал это в Groovy. Через секунду я исправлю код, чтобы он был правильным кодом Java