3

given the following line

BigDecimal step = 10.0G**-1.0G

groovy 1.7.5 returns the wrong

0.1000000000000000055511151231257827021181583404541015625

and groovy 1.8 returns the correct

0.1

Unfortunately, I want to solve my Problem in Grails. 1.4 with groovy 1.8 isn't stable enough yet (controllers are not refreshed in dev mode) and grails 1.3.7 comes with groovy 1.7.x

two questions:

  • am I doing something wrong or is it a bug in 1.7.5?

  • how can I avoid this behaviour? I though BigDecimals where perfect for this kind of rounding problem?

second update: (forget the first update)-;

I now get a bit confused. seems that I get different result every time I try....:

BigDecimal step = 10.0G**-1.0G
println step

returns 0.1000000000000000055511151231257827021181583404541015625

and

println 10.0G**-1.0G

returns

0.1

in both groovy versions.

But if you just put BigDec step = 10.0G**-1.0G in the groovyConsole and let the console print the last response, you get the different results for different groovy versions. So one problem seems to be the groovyConsole.

Another Problem seems to be the toString conversion which is performed.

And it seems that some autoboxing is involved... when I do a

def step = 10.0G**-1.0G

the result is a Double...

I guess this boils the problem down to two questions:

  • a) which math operations are BigDecimal operations?

and

  • b) how can I easily round a BigDecimal so that I can correct the above problem?

Thanx for your patience

rdmueller
  • 10,742
  • 10
  • 69
  • 126
  • 1
    If it is just in groovy console, it's still a problem? – RonK Jun 13 '11 at 15:45
  • yep. the main problem is that my calculations run into those rounding problems. I though I had tracked it down with the example above. I will try to track it down again. Please stay tuned... :-) – rdmueller Jun 13 '11 at 17:19

4 Answers4

4

I guess I've got it. Here is the clue from the java doc:

BigDecimal(double val) Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value.

  • So, the Math.-Functions I use in my code seem not to support BigDecimal.
  • the result is converted behind the scenes from Double to BigDecimal
  • exact representation of the double's binary is 0.1000000000000000055511151231257827021181583404541015625 when the double is 0.1
  • println 10.0G**-1.0G prints the double value (0.1)
  • BigDecimal step = 10.0G**-1.0G; println step prints the BigDecimal representation of the double 0.1which is the ugly number above

It seems that there is no difference in the behaviour of the groovy versions (with regard to the BigDecimals), but there is a difference in the behaviour of how the result is output in the groovyConsole.

rdmueller
  • 10,742
  • 10
  • 69
  • 126
2

I'm using Groovy 1.7.5 and for me 10.0G**-1.0G returns 0.1

Which JDK are you running on?

RonK
  • 9,472
  • 8
  • 51
  • 87
  • hm. both groovyConsoles read as JAVA_HOME jdk1.6.0_21 from the environment variables... but there seems to be a difference between `BigDecimal step = 10.0G**-1.0G; println step` and `println 10.0G**-1.0G`... – rdmueller Jun 13 '11 at 15:01
1

If you want to convert from a Double to BigDecimal and not get all the extra digits, one way to do it is to put the Double into a String and then convert that to BigDecimal, which will prevent all the extra digits coming over from the exact binary representation of the double.

Double someFraction = 1.23456789
BigDecimal sameFraction = "${someFraction}".toBigDecimal()
Todd
  • 1,822
  • 15
  • 18
1

I'd suspect that that 10.0G**-1.0G turns into Math.pow(10, -1), math.pow returns a double, which then gives a rounding error. To avoid it, if you're only raising to -1 you can implement it as a division (1.0G/10.0G).

That said, I don't know Groovy that well, so my guess could be wrong, but that's certainly what it looks like, and it's backed up by what the docs for Groovy say.

Sysyphus
  • 1,061
  • 5
  • 10
  • Good idea to replace the math function with simpler code in this example. Unfortunatly, my code makes use of more math function (log for example). Are there maybe special BigMath functions? – rdmueller Jun 13 '11 at 07:20
  • 1
    Not in standard java that I'm aware of. How much precision do you need? If it's less than 16 digits you can get away with using doubles. If more, you'd need to look at using an external library or writing your own. There are past questions on stack overflow on writing your own BigDecimal implementations of things like [log](http://stackoverflow.com/questions/739532/logarithm-of-a-bigdecimal) – Sysyphus Jun 13 '11 at 07:40
  • It is not so much about the precision. It is more that I want to avoid those rounding errors...-not easy to explain. It's code for drawing the axis of a chart. – rdmueller Jun 13 '11 at 08:47
  • 1
    `BigDecimal` supports the `pow` method, but it only excepts `int` - so I don't know how it will behave if you pass it a `BigDecimal`. You should try to look it up in `Groovy`'s specs - I found nothing about it – RonK Jun 13 '11 at 13:05
  • `BigDecimal step = 10**-1; println step` still looks ugly... :-( – rdmueller Jun 13 '11 at 17:33