使用BigDecimal来确定十进制数

时间:2021-11-03 16:44:51

I was interested to have the following getNumberOfDecimalPlace function :

我对下面的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

May I know how can I implement getNumberOfDecimalPlace, by using BigDecimal?

我可以使用BigDecimal实现getNumberOfDecimalPlace吗?

The following code doesn't work as expected :

以下代码没有按预期工作:

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

The following get printed :

以下是打印出来的:

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

However, for case 0, 1.0, it doesn't work well. I expect, "0" as result. But they turned out to be "0.0" and "1.0". This will return "1" as result.

但是,对于情形0,1.0,它不能很好地工作。结果是0。但结果是“0。0”和“1.0”。结果将返回“1”。

12 个解决方案

#1


28  

This code:

这段代码:

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

... passes these tests:

…通过这些测试:

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

... if that is indeed what you want. The other replies are correct, you have to use BigDecimal all the way through for this rather than double/float.

…如果那确实是你想要的。其他的回答是正确的,您必须一直使用BigDecimal来实现它,而不是使用double/float。

#2


22  

Combining Turismo, Robert and user1777653's answers, we've got:

结合屠夫,罗伯特和user1777653的答案,我们得到:

int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
    return Math.max(0, bigDecimal.stripTrailingZeros().scale());
}
  • stripTrailingZeros() ensures that trailing zeros are not counted (e.g. 1.0 has 0 decimal places).
  • stripTrailingZeros()确保后面的0没有被计数(例如1.0有0个小数点)。
  • scale() is more efficient than String.indexOf().
  • scale()比String.indexOf()更有效。
  • A negative scale() represents zero decimal places.
  • 负数标度()表示零小数位数。

There you have it, the best of both worlds.

这就是你所拥有的,两全其美。

#3


3  

It's not your code that's wrong, but your expectations. double is based on a binary floating point representation and completely unfit for accurately representing decimal fractions. Decimal 0.1 e.g. has an infinite number of digits when represented in binary, thus it gets truncated and when converted back to decimal, you get erros in the least significant digits.

不是你的代码错了,而是你的期望。double基于二进制浮点表示法,完全不适合精确地表示十进制分数。十进制0。1例如,当用二进制表示时,有无限个数字,因此它会被截断,当转换回十进制时,您会得到最低有效位的erros。

If you use BigDecimal exclusively, your code will work as expected.

如果您只使用BigDecimal,那么您的代码将按照预期工作。

#4


3  

If you really get doubles i recommend formating them first as strings before creating the BigDecimal. At least that has worked for me: How to check if a double has at most n decimal places?

如果您确实得到了双精度,我建议在创建BigDecimal之前先将它们格式化为字符串。至少这对我来说是可行的:如何检查双精度浮点数是否最多有n个小数?

Depending on how many digits you expect you can either use standard formating like

取决于你期望多少位数字,你可以使用标准格式。

String.valueOf(doubleValue);

or you could use specialised formatting to avoid exponential format

或者你可以使用专门的格式来避免指数格式

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

When you have a BigDecimal you can simply call scale() to get the number of decimal places.

当你有一个BigDecimal,你可以简单地调用scale()来得到小数位数。

#5


2  

Try this:

试试这个:

Math.floor(Math.log(x) / Math.log(10))
0.001 = -3
0.01  = -2
0.1   = -1  
1     = 0  
10    = 1  
100   = 2

#6


1  

That should do it

应该做它

int getNumberOfDecimalPlace(BigDecimal number) {
    int scale = number.stripTrailingZeros().scale();
    return scale > 0 ? scale : 0;
}

#7


0  

How about having a look at the javadoc of BigDecimal. I'm not sure, but I'd give getScale and getPercision a try.

我们来看看BigDecimal的javadoc。我不确定,但我想试试getScale和getPercision。

#8


0  

The best way to get a BigDecimal with a specified number of decimal places is by using the setscale method over it. Personally I like to also use the rounding version of the method (see the link below): http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#setScale(int,%20int)

用指定的小数位数来获取BigDecimal的最好方法是使用setscale方法。我个人也喜欢使用方法的舍入版本(参见下面的链接):http://java.sun.com/docse/1.5.0/api/java/math/bigdecimal.html #setScale(int,%20int)

If you're wanting to get the number of decimal positions that a BigDecimal is currently set at call the associated scale() method.

如果您想获得当前在调用关联的scale()方法时设置的BigDecimal的十进制数。

#9


0  

Best option I have found so far (not needing toString + index):

到目前为止我找到的最佳选项(不需要toString +索引):

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

public static int digits(BigDecimal value)
{
    return Math.max(0, value.scale());
}

#10


0  

Without having to convert to String, it should be more efficient to use the scale directly:

不需要转换成字符串,直接使用比例应该更有效:

  private int getNumberOfDecimalPlaces(BigDecimal bigDecimal)
  {
    int scale =  bigDecimal.stripTrailingZeros().scale();
    return scale>0?scale:0;
  }

#11


-1  

Michael Borgwardt answer is the correct one. As soon as you use any double or float, your values are already corrupted.

Michael Borgwardt的答案是正确的。只要使用任何双精度或浮点数,您的值就已经被损坏。

To provide a code example:

提供一个代码示例:

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

And an overloaded version of getNumberOfDecimalPlace so you could use it with BigDecimal or String:

还有一个重载的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


-6  

Why not just change your code to get a doubles decimal places?

为什么不直接修改代码,得到一个双精度小数呢?

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

#1


28  

This code:

这段代码:

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

... passes these tests:

…通过这些测试:

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

... if that is indeed what you want. The other replies are correct, you have to use BigDecimal all the way through for this rather than double/float.

…如果那确实是你想要的。其他的回答是正确的,您必须一直使用BigDecimal来实现它,而不是使用double/float。

#2


22  

Combining Turismo, Robert and user1777653's answers, we've got:

结合屠夫,罗伯特和user1777653的答案,我们得到:

int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
    return Math.max(0, bigDecimal.stripTrailingZeros().scale());
}
  • stripTrailingZeros() ensures that trailing zeros are not counted (e.g. 1.0 has 0 decimal places).
  • stripTrailingZeros()确保后面的0没有被计数(例如1.0有0个小数点)。
  • scale() is more efficient than String.indexOf().
  • scale()比String.indexOf()更有效。
  • A negative scale() represents zero decimal places.
  • 负数标度()表示零小数位数。

There you have it, the best of both worlds.

这就是你所拥有的,两全其美。

#3


3  

It's not your code that's wrong, but your expectations. double is based on a binary floating point representation and completely unfit for accurately representing decimal fractions. Decimal 0.1 e.g. has an infinite number of digits when represented in binary, thus it gets truncated and when converted back to decimal, you get erros in the least significant digits.

不是你的代码错了,而是你的期望。double基于二进制浮点表示法,完全不适合精确地表示十进制分数。十进制0。1例如,当用二进制表示时,有无限个数字,因此它会被截断,当转换回十进制时,您会得到最低有效位的erros。

If you use BigDecimal exclusively, your code will work as expected.

如果您只使用BigDecimal,那么您的代码将按照预期工作。

#4


3  

If you really get doubles i recommend formating them first as strings before creating the BigDecimal. At least that has worked for me: How to check if a double has at most n decimal places?

如果您确实得到了双精度,我建议在创建BigDecimal之前先将它们格式化为字符串。至少这对我来说是可行的:如何检查双精度浮点数是否最多有n个小数?

Depending on how many digits you expect you can either use standard formating like

取决于你期望多少位数字,你可以使用标准格式。

String.valueOf(doubleValue);

or you could use specialised formatting to avoid exponential format

或者你可以使用专门的格式来避免指数格式

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

When you have a BigDecimal you can simply call scale() to get the number of decimal places.

当你有一个BigDecimal,你可以简单地调用scale()来得到小数位数。

#5


2  

Try this:

试试这个:

Math.floor(Math.log(x) / Math.log(10))
0.001 = -3
0.01  = -2
0.1   = -1  
1     = 0  
10    = 1  
100   = 2

#6


1  

That should do it

应该做它

int getNumberOfDecimalPlace(BigDecimal number) {
    int scale = number.stripTrailingZeros().scale();
    return scale > 0 ? scale : 0;
}

#7


0  

How about having a look at the javadoc of BigDecimal. I'm not sure, but I'd give getScale and getPercision a try.

我们来看看BigDecimal的javadoc。我不确定,但我想试试getScale和getPercision。

#8


0  

The best way to get a BigDecimal with a specified number of decimal places is by using the setscale method over it. Personally I like to also use the rounding version of the method (see the link below): http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#setScale(int,%20int)

用指定的小数位数来获取BigDecimal的最好方法是使用setscale方法。我个人也喜欢使用方法的舍入版本(参见下面的链接):http://java.sun.com/docse/1.5.0/api/java/math/bigdecimal.html #setScale(int,%20int)

If you're wanting to get the number of decimal positions that a BigDecimal is currently set at call the associated scale() method.

如果您想获得当前在调用关联的scale()方法时设置的BigDecimal的十进制数。

#9


0  

Best option I have found so far (not needing toString + index):

到目前为止我找到的最佳选项(不需要toString +索引):

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

public static int digits(BigDecimal value)
{
    return Math.max(0, value.scale());
}

#10


0  

Without having to convert to String, it should be more efficient to use the scale directly:

不需要转换成字符串,直接使用比例应该更有效:

  private int getNumberOfDecimalPlaces(BigDecimal bigDecimal)
  {
    int scale =  bigDecimal.stripTrailingZeros().scale();
    return scale>0?scale:0;
  }

#11


-1  

Michael Borgwardt answer is the correct one. As soon as you use any double or float, your values are already corrupted.

Michael Borgwardt的答案是正确的。只要使用任何双精度或浮点数,您的值就已经被损坏。

To provide a code example:

提供一个代码示例:

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

And an overloaded version of getNumberOfDecimalPlace so you could use it with BigDecimal or String:

还有一个重载的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


-6  

Why not just change your code to get a doubles decimal places?

为什么不直接修改代码,得到一个双精度小数呢?

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