#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