0

I'm trying to understand the scale for a BigDecimal but it acts weird and I can't understand why. Here's a couple of examples:

Double d = new Double(1000000d);
int scale = new BigDecimal(d.toString()).scale();

The scale in this example will be 1 which is correct to me. The result of d.toString() is "1000000.0".

Double d = new Double(10000000d);
int scale = new BigDecimal(d.toString)).scale();

The scale in this example will be -6. Can anyone explain why? The result of d.toString() is "1.0E7".

I thought the number of digits caused this but if I go:

Double d = new Double(11111111d);
int scale = new BigDecimal(d.toString()).scale();

Expected a scale of -8 but suddenly it's 0. The result of d.toString() is "1.1111111E7".

These different scales make no sense to me after reading the Javadoc of scale():

Returns the scale of this BigDecimal. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. For example, a scale of -3 means the unscaled value is multiplied by 1000.

I'd very much appreciate an explanation how BigDecimal behaves when the numbers are large.

Thanks in advance!

2 Answers2

3

The scale you got is the number of decimals with some significance:

  • 1000000d -> 1000000.0 -> 0: the numbers at the right of the dot have no significance, the result is 0;
  • 10000000d -> 1.0E7 -> -6: the numbers at the right of the dot have significance, as if you denormalize the power by ten you get the 6 zeros;
  • 11111111d -> 1.1111111E7 -> 0: all the numbers at the right of the dot have significance, denormalizing the power by ten you get more information, so you "can't" normalize the number if you want to keep this information. This way (the number denormalized), you have 0 numbers at the right of the dot.

EDIT

As commented, the first line is wrong, it must be 1000000d -> 1000000.0 -> 1. The reason is that the numbers with exponential have a different behavior (when obtaining the scale) that the formatted numbers.

The value of 1 is due that BigDecimal counts the numbers in the right side of the dot (which in this case is one, a single 0), subtract the numbers to drop (in this case there is one, the single 0) and add the math precision (by default is one) -> result = 1.

Sergio Lema
  • 1,491
  • 1
  • 14
  • 25
  • Complicated but I think I get it :) Thanks! – Richard Silvertass Mar 06 '17 at 14:53
  • `1000000d -> 1000000.0 -> 0` should be `1` – vossad01 Mar 06 '17 at 15:23
  • No, should be `0` as 0 in the right side of the dot have no significance – Sergio Lema Mar 06 '17 at 15:58
  • @SergioLema Please reconsider. Saying it is zero makes your answer incorrect (`new BigDecimal("1000000.0").scale() == 1`), self-contradictory (`1.0E7 -> -6` should then to to `-7` because the zero to the right is not significant), and not consistent with mathematical definitions (["In a number with a decimal point, trailing zeros, those to the right of the last non-zero digit, are significant."](https://en.wikipedia.org/wiki/Significant_figures#Identifying_significant_figures)) – vossad01 Mar 06 '17 at 16:15
1

You are seeing the behavior your report because you are calling toString() on the decimal provided, which in for some of your examples represents in exponential notation, which is then preserved by BigDecimal when it chooses the scale.

If you provide the the double directly to the BigDecimal constructor you consistently get 0.

new Double(1000000d).toString() //1.0E7

Double d = new Double(1000000d);
int scale = new BigDecimal(d).scale(); //0

Double d = new Double(10000000d);
int scale = new BigDecimal(d).scale(); //0

Double d = new Double(11111111d);
int scale = new BigDecimal(d).scale(); //0

Update:

scale is is not a useful attribute on its own. It must be considered in conjunction with unscaledValue. The represented number is unscaledValue × 10 ^ -scale.

That is,

BigDecimal d = new BigDecimal(1000000d)
BigDecimal e = d.setScale(2)

int dScale = d.scale() //0
int dUnscaled = d.unscaledValue() //1000000

int eScale = e.scale() //2
int eUnscaled = e.unscaledValue() //100000000

Both d and e are a representation of 1000000. However, e preserves there are 2 trailing zeros (zeros after the decimal point).

d.toString() //1000000
e.toString() //1000000.00
vossad01
  • 11,552
  • 8
  • 56
  • 109