Определите число десятичных разрядов, используя BigDecimal - PullRequest
11 голосов
/ 19 февраля 2010

Мне было интересно иметь следующую getNumberOfDecimalPlace функцию:

System.out.println("0 = " + Utils.getNumberOfDecimalPlace(0));          // 0
System.out.println("1.0 = " + Utils.getNumberOfDecimalPlace(1.0));      // 0
System.out.println("1.01 = " + Utils.getNumberOfDecimalPlace(1.01));    // 2
System.out.println("1.012 = " + Utils.getNumberOfDecimalPlace(1.012));  // 3
System.out.println("0.01 = " + Utils.getNumberOfDecimalPlace(0.01));    // 2
System.out.println("0.012 = " + Utils.getNumberOfDecimalPlace(0.012));  // 3

Могу ли я узнать, как я могу реализовать getNumberOfDecimalPlace, используя BigDecimal?

Следующий код не работает должным образом:

public static int getNumberOfDecimalPlace(double value) {
    final BigDecimal bigDecimal = new BigDecimal("" + value);
    final String s = bigDecimal.toPlainString();
    System.out.println(s);
    final int index = s.indexOf('.');
    if (index < 0) {
        return 0;
    }
    return s.length() - 1 - index;
}

Будет напечатано следующее:

0.0
0 = 1
1.0
1.0 = 1
1.01
1.01 = 2
1.012
1.012 = 3
0.01
0.01 = 2
0.012
0.012 = 3

Однако, для случая 0, 1.0, это не работает хорошо. Я ожидаю, «0» как результат. Но они оказались «0.0» и «1.0». В результате вы получите «1».

Ответы [ 12 ]

32 голосов
/ 04 января 2011

Этот код:

int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
    String string = bigDecimal.stripTrailingZeros().toPlainString();
    int index = string.indexOf(".");
    return index < 0 ? 0 : string.length() - index - 1;
}

... проходит эти испытания:

assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.001")), equalTo(3));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.01")), equalTo(2));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.1")), equalTo(1));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.000")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.00")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.0")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.1")), equalTo(1));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.01")), equalTo(2));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.001")), equalTo(3));

... если это действительно то, что вы хотите.Другие ответы верны, вы должны использовать BigDecimal для этого, а не double / float.

31 голосов
/ 27 мая 2015

Объединяя Turismo , Robert и user1777653's ответов, мы получаем:

int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
    return Math.max(0, bigDecimal.stripTrailingZeros().scale());
}
  • stripTrailingZeros () обеспечиваетчто завершающие нули не учитываются (например, 1.0 имеет 0 десятичных знаков).
  • scale() более эффективен, чем String.indexOf().
  • Отрицательный scale() представляет ноль десятичных знаков.

Вот оно, лучшее из обоих миров.

3 голосов
/ 19 февраля 2010

Если вы действительно получаете двойные значения, я рекомендую сначала отформатировать их в виде строк перед созданием BigDecimal.По крайней мере, это сработало для меня: Как проверить, имеет ли двойное число не более n десятичных знаков?

В зависимости от того, сколько цифр вы ожидаете, вы можете использовать стандартное форматирование, например

String.valueOf(doubleValue);

или вы можете использовать специализированное форматирование, чтобы избежать экспоненциального формата

DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE);
// don't use grouping for numeric-type cells
decimalFormat.setGroupingUsed(false);
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
value = decimalFormat.format(numericValue);

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

3 голосов
/ 19 февраля 2010

Это не твой код неправильный, а твои ожидания. double основан на двоичном представлении с плавающей запятой и совершенно не подходит для точного представления десятичных дробей. Десятичный 0,1, например имеет бесконечное количество цифр при представлении в двоичном виде, поэтому он усекается, а при преобразовании обратно в десятичную форму вы получаете ошибки в наименее значащих цифрах.

Если вы используете исключительно BigDecimal, ваш код будет работать как положено.

2 голосов
/ 31 октября 2016

Без необходимости преобразования в строку, более эффективно использовать шкалу напрямую:

  private int getNumberOfDecimalPlaces(BigDecimal bigDecimal)
  {
    int scale =  bigDecimal.stripTrailingZeros().scale();
    return scale>0?scale:0;
  }
2 голосов
/ 15 ноября 2014

Это должно сделать

int getNumberOfDecimalPlace(BigDecimal number) {
    int scale = number.stripTrailingZeros().scale();
    return scale > 0 ? scale : 0;
}
2 голосов
/ 23 мая 2012

Попробуйте это:

Math.floor(Math.log(x) / Math.log(10))
0.001 = -3
0.01  = -2
0.1   = -1  
1     = 0  
10    = 1  
100   = 2
0 голосов
/ 11 июня 2015

Лучший вариант, который я нашел (без необходимости toString + index):

public static int digitsStripTrailingZero(BigDecimal value)
{
    return digits(value.stripTrailingZeros());
}

public static int digits(BigDecimal value)
{
    return Math.max(0, value.scale());
}
0 голосов
/ 19 февраля 2010

Ответ Михаэля Боргвардта правильный.Как только вы используете любое значение типа double или float, ваши значения уже повреждены.

Чтобы предоставить пример кода:

System.out.println("0 = " + BigDecimalUtil.getNumberOfDecimalPlace("0")); // 0
System.out.println("1.0 = " + BigDecimalUtil.getNumberOfDecimalPlace("1.0")); // 0
System.out.println("1.01 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.01"))); // 2
System.out.println("1.012 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.012"))); // 3
System.out.println("0.01 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.01")); // 2
System.out.println("0.012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.012")); // 3
System.out.println("0.00000000000000000012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.00000000000000000012")); // 20

И перегруженную версию getNumberOfDecimalPlace, чтобы вы могли использовать ее с BigDecimalили строка:

public static int getNumberOfDecimalPlace(String value) {
    final int index = value.indexOf('.');
    if (index < 0) {
        return 0;
    }
    return value.length() - 1 - index;
}

public static int getNumberOfDecimalPlace(BigDecimal value) {
    return getNumberOfDecimalPlace(value.toPlainString());
}
0 голосов
/ 19 февраля 2010

Почему бы просто не изменить свой код, чтобы получить двойные десятичные знаки?

public static int getNumberOfDecimalPlace(double value) {
    //For whole numbers like 0
    if (Math.round(value) == value) return 0;
    final String s = Double.toString(value);
    System.out.println(s);
    final int index = s.indexOf('.');
    if (index < 0) {
       return 0;
    }
    return s.length() - 1 - index;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...