5

I have this column in my Oracle 11g mapped as NUMBER (21,20), which is mapped in Hibernate as:

@Column(name = "PESO", precision = 21, scale = 20, nullable = false)
public BigDecimal getWeight() {
    return weight;
}

For a particular record for which the value of the column is 0.493 I get a BigDecimal whose value is 0.49299999999. It seems that somewhere there is a loss of precision due (maybe) to a Double or Float conversion, but I couldn't track it down with a simple unit test like this:

Double d = new Double("0.493");
System.out.println((d));

Any variant of that code, using Float, BigDecimal and various constructors gives the same result: "0.493"... Any hint on how should I map the column to avoid such issues? I'm using Hibernate 3.5.6, with JPA annotations and Hibernate API (that is Session and not EntityManager)

Riccardo Cossu
  • 2,699
  • 1
  • 28
  • 47
  • it seems that this was not hibernate fault... This is entirely on SquirrelSql, which displays 0.493 for a value of 0.49299999999... The value has always been that, both in the "source" file, both in database, as SqlDeveloper reports. – Riccardo Cossu Apr 20 '11 at 17:17

2 Answers2

3

It's a result of initializing BigDecimal from double:

System.out.println(String.format("%21.20f", new BigDecimal(0.493)); 
// Prints 0,49299999999999999378  

So, when BigDecimal initialized this way is saved in the database, it produces an inaccurate value, which is correctly loaded later.

If BigDecimal is initialized by string or if the value is set directly in Java everything works fine.

Torsten
  • 1,696
  • 2
  • 21
  • 42
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • thanks for the example, probably the "%21.20" part does some trick because System.out.println(String.format("%f", new BigDecimal(0.493))); print almost fine -> "0,493000". That was not my case though :-) – Riccardo Cossu Apr 20 '11 at 17:31
0

For me, the winning solution has been the one based on How do I map a BigDecimal in Hibernate so I get back the same scale I put in?:

private final int SCALE = 2;
private final RoundingMode ROUNDING_MODE = RoundingMode.CEILING;

@Column(name = "value", precision = 8, scale = 2)
private BigDecimal value;

public BigDecimal getValue() {
    return value.setScale(SCALE, ROUNDING_MODE);
}

So, I just provided a getter that performed automatic tidying up of the big decimal.

Nestor Milyaev
  • 5,845
  • 2
  • 35
  • 51