Определить количество десятичных мест с помощью BigDecimal
Мне было интересно иметь следующую функцию 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".
Ответы
Ответ 1
Этот код:
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.
Ответ 2
Сочетание Turismo, Robert и user1777653's ответы, у нас есть:
int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
return Math.max(0, bigDecimal.stripTrailingZeros().scale());
}
- stripTrailingZeros() гарантирует, что конечные нули не учитываются (например,
1.0
имеет 0 знаков после запятой).
-
scale()
более эффективен, чем String.indexOf()
.
- Отрицательный
scale()
представляет нулевые десятичные разряды.
Там у вас это, лучшее из обоих миров.
Ответ 3
Это не ваш код, который неправильный, но ваши ожидания. double
основан на двоичном представлении с плавающей запятой и полностью непригоден для точного представления десятичных дробей. Десятичный 0,1, например. имеет бесконечное число цифр, если оно представлено в двоичном формате, поэтому оно усекается и при преобразовании обратно в десятичное значение вы получаете erros в наименее значимых цифрах.
Если вы используете только BigDecimal
, ваш код будет работать как ожидалось.
Ответ 4
Если вы действительно получаете парные пары, я рекомендую сначала их создать как строки перед созданием BigDecimal
. По крайней мере, это сработало для меня: Как проверить, имеет ли double не более 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()
, чтобы получить число десятичных знаков.
Ответ 5
Попробуйте следующее:
Math.floor(Math.log(x) / Math.log(10))
0.001 = -3
0.01 = -2
0.1 = -1
1 = 0
10 = 1
100 = 2
Ответ 6
Это должно сделать это
int getNumberOfDecimalPlace(BigDecimal number) {
int scale = number.stripTrailingZeros().scale();
return scale > 0 ? scale : 0;
}
Ответ 7
Как насчет просмотра javadoc BigDecimal. Я не уверен, но я бы дал getScale и getPercision попробовать.
Ответ 8
Лучший способ получить BigDecimal с указанным числом десятичных знаков - использовать метод setscale над ним. Лично мне нравится использовать версию округления метода (см. Ссылку ниже):
http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#setScale(int,%20int)
Если вы хотите получить число десятичных позиций, которое BigDecimal в настоящий момент задает при вызове связанного метода scale().
Ответ 9
Лучший вариант, который я нашел до сих пор (не нужен индекс toString +):
public static int digitsStripTrailingZero(BigDecimal value)
{
return digits(value.stripTrailingZeros());
}
public static int digits(BigDecimal value)
{
return Math.max(0, value.scale());
}
Ответ 10
Без необходимости конвертировать в String, более эффективно использовать масштаб непосредственно:
private int getNumberOfDecimalPlaces(BigDecimal bigDecimal)
{
int scale = bigDecimal.stripTrailingZeros().scale();
return scale>0?scale:0;
}
Ответ 11
Ответ Майкла Боргвардта правильный. Как только вы используете любой двойной или плавающий, ваши значения уже повреждены.
Чтобы предоставить пример кода:
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 или String:
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());
}
Ответ 12
Почему бы просто не изменить код, чтобы получить удвоение десятичных знаков?
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;
}