Thread: PreparedStatement rounds doubles to scale 14 during update

PreparedStatement rounds doubles to scale 14 during update

From
"Peter Kovacs"
Date:
Hi,

I have the following problem with postgresql-8.1-407.jdbc3.jar used
against a 8.1 version backend.

A table "chemterms_cols_test" has a column "logp" defined as:

logp numeric(30,17)

The double value 6.118992224252588 gets rounded to 6.11899222425259000
during updates with PreparedStatements using place-holder. (There is
no rounding with Statement and the value specified in the SQL
literally.) Is this the expected behaviour?

Below is the code that can be used to test this behaviour ("cd_id" is
the primary key):

    public static void main(String[] args) {
        try {
            Connection conn = DBTools.getConnectionHandler().getConnection();
            conn.setAutoCommit(false);

            int cdId = 1;
            double dblValue = 6.118992224252588;

            String sql =
                    "update chemterms_cols_test set logp = ? where cd_id = ?";
            try {
                PreparedStatement pstmt = conn.prepareStatement(sql);
                try {
                    pstmt.setDouble(1, dblValue);
                    pstmt.setInt(2, 1);
                    pstmt.executeUpdate();
                    conn.commit();
                } finally {
                    pstmt.close();
                }

                ResultSet rs = null;
                sql = "select logp from chemterms_cols_test where cd_id = ?";
                pstmt = conn.prepareStatement(sql);
                try {
                    pstmt.setInt(1, 1);
                    rs = pstmt.executeQuery();
                    rs.next();
                    double actual = rs.getDouble(1);
                    System.out.println("actual=" + actual + ", expected="
                            + dblValue + ", equals? : " + (actual == dblValue));

                } finally {
                    try {
                        if (rs != null) {
                            rs.close();
                        }
                    } finally {
                        pstmt.close();
                    }
                }
            } finally {
                conn.close();
            }
        } catch (Throwable tbl) {
            tbl.printStackTrace();
            System.exit(1);
        }
    }

Thanks
Peter

Re: PreparedStatement rounds doubles to scale 14 during update

From
"Heikki Linnakangas"
Date:
Peter Kovacs wrote:
> Hi,
>
> I have the following problem with postgresql-8.1-407.jdbc3.jar used
> against a 8.1 version backend.
>
> A table "chemterms_cols_test" has a column "logp" defined as:
>
> logp numeric(30,17)
>
> The double value 6.118992224252588 gets rounded to 6.11899222425259000
> during updates with PreparedStatements using place-holder. (There is
> no rounding with Statement and the value specified in the SQL
> literally.) Is this the expected behaviour?

The problem is in the conversion from double to numeric in the server.
The server-side float8 data type doesn't have enough precision for that
many decimal places. Because converting between numeric and
floating-point data types almost always leads to rounding errors at some
point, I would strongly recommend sticking to base-10 data types
everywhere if you care about the precision.

If you use BigDecimal instead of double in your java-program, it works fine.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Re: PreparedStatement rounds doubles to scale 14 during update

From
"Peter Kovacs"
Date:
Heikki,

Thank you for your reply.

What are base-10 datatypes? Are they numeric types? I cannot find them
in the backend documentation.

Is there a mapping of Java types to backend types documented somewhere?

Thanks a lot
Peter

On 9/5/07, Heikki Linnakangas <heikki@enterprisedb.com> wrote:
> Peter Kovacs wrote:
> > Hi,
> >
> > I have the following problem with postgresql-8.1-407.jdbc3.jar used
> > against a 8.1 version backend.
> >
> > A table "chemterms_cols_test" has a column "logp" defined as:
> >
> > logp numeric(30,17)
> >
> > The double value 6.118992224252588 gets rounded to 6.11899222425259000
> > during updates with PreparedStatements using place-holder. (There is
> > no rounding with Statement and the value specified in the SQL
> > literally.) Is this the expected behaviour?
>
> The problem is in the conversion from double to numeric in the server.
> The server-side float8 data type doesn't have enough precision for that
> many decimal places. Because converting between numeric and
> floating-point data types almost always leads to rounding errors at some
> point, I would strongly recommend sticking to base-10 data types
> everywhere if you care about the precision.
>
> If you use BigDecimal instead of double in your java-program, it works fine.
>
> --
>   Heikki Linnakangas
>   EnterpriseDB   http://www.enterprisedb.com
>

Re: PreparedStatement rounds doubles to scale 14 during update

From
"Heikki Linnakangas"
Date:
Peter Kovacs wrote:
> Heikki,
>
> Thank you for your reply.
>
> What are base-10 datatypes? Are they numeric types? I cannot find them
> in the backend documentation.

I meant any data type that can accurately represent a base-10 decimal
number. In PostgreSQL, that's numeric. In Java, it's BigDecimal.

If you're not familiar with the differences between floating point and
decimal numbers, I'd suggest you to read on it. This python tutorial for
example has a pretty good chapter on the issues:
http://docs.python.org/tut/node16.html

> Is there a mapping of Java types to backend types documented somewhere?

There is for mapping between Java types and SQL types, see "Getting
Started with with the JDBC API" by Sun:

http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/mapping.html

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Re: PreparedStatement rounds doubles to scale 14 during update

From
"Peter Kovacs"
Date:
Thank you for the python reference, it is very instructive indeed.

I find it strange that the type mapping specification is in a tutorial
by Sun and not in a more formal document.

Thanks
Peter

On 9/5/07, Heikki Linnakangas <heikki@enterprisedb.com> wrote:
> Peter Kovacs wrote:
> > Heikki,
> >
> > Thank you for your reply.
> >
> > What are base-10 datatypes? Are they numeric types? I cannot find them
> > in the backend documentation.
>
> I meant any data type that can accurately represent a base-10 decimal
> number. In PostgreSQL, that's numeric. In Java, it's BigDecimal.
>
> If you're not familiar with the differences between floating point and
> decimal numbers, I'd suggest you to read on it. This python tutorial for
> example has a pretty good chapter on the issues:
> http://docs.python.org/tut/node16.html
>
> > Is there a mapping of Java types to backend types documented somewhere?
>
> There is for mapping between Java types and SQL types, see "Getting
> Started with with the JDBC API" by Sun:
>
> http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/mapping.html
>
> --
>   Heikki Linnakangas
>   EnterpriseDB   http://www.enterprisedb.com
>

Re: PreparedStatement rounds doubles to scale 14 during update

From
Dave Cramer
Date:
On 5-Sep-07, at 9:13 AM, Peter Kovacs wrote:

> Thank you for the python reference, it is very instructive indeed.
>
> I find it strange that the type mapping specification is in a tutorial
> by Sun and not in a more formal document.
>
Welcome to SUN API documentation ....

Dave
> Thanks
> Peter
>
> On 9/5/07, Heikki Linnakangas <heikki@enterprisedb.com> wrote:
>> Peter Kovacs wrote:
>>> Heikki,
>>>
>>> Thank you for your reply.
>>>
>>> What are base-10 datatypes? Are they numeric types? I cannot find
>>> them
>>> in the backend documentation.
>>
>> I meant any data type that can accurately represent a base-10 decimal
>> number. In PostgreSQL, that's numeric. In Java, it's BigDecimal.
>>
>> If you're not familiar with the differences between floating point
>> and
>> decimal numbers, I'd suggest you to read on it. This python
>> tutorial for
>> example has a pretty good chapter on the issues:
>> http://docs.python.org/tut/node16.html
>>
>>> Is there a mapping of Java types to backend types documented
>>> somewhere?
>>
>> There is for mapping between Java types and SQL types, see "Getting
>> Started with with the JDBC API" by Sun:
>>
>> http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/mapping.html
>>
>> --
>>   Heikki Linnakangas
>>   EnterpriseDB   http://www.enterprisedb.com
>>
>
> ---------------------------(end of
> broadcast)---------------------------
> TIP 6: explain analyze is your friend


Re: PreparedStatement rounds doubles to scale 14 during update

From
Oliver Jowett
Date:
Peter Kovacs wrote:

> I find it strange that the type mapping specification is in a tutorial
> by Sun and not in a more formal document.

It's in the JDBC spec too.

-O

Re: PreparedStatement rounds doubles to scale 14 during update

From
"Peter Kovacs"
Date:
One last question:

How come that Java double, which is an 8 byte size floating point
number, cannot fit in PostgreSQL's double precision type, which is
equally a floating point type and 8 bytes in size?

Thanks
Peter

On 9/5/07, Heikki Linnakangas <heikki@enterprisedb.com> wrote:
> Peter Kovacs wrote:
> > Heikki,
> >
> > Thank you for your reply.
> >
> > What are base-10 datatypes? Are they numeric types? I cannot find them
> > in the backend documentation.
>
> I meant any data type that can accurately represent a base-10 decimal
> number. In PostgreSQL, that's numeric. In Java, it's BigDecimal.
>
> If you're not familiar with the differences between floating point and
> decimal numbers, I'd suggest you to read on it. This python tutorial for
> example has a pretty good chapter on the issues:
> http://docs.python.org/tut/node16.html
>
> > Is there a mapping of Java types to backend types documented somewhere?
>
> There is for mapping between Java types and SQL types, see "Getting
> Started with with the JDBC API" by Sun:
>
> http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/mapping.html
>
> --
>   Heikki Linnakangas
>   EnterpriseDB   http://www.enterprisedb.com
>

Re: PreparedStatement rounds doubles to scale 14 during update

From
Richard Broersma Jr
Date:
--- Dave Cramer <pg@fastcrypt.com> wrote:

>
> On 5-Sep-07, at 9:13 AM, Peter Kovacs wrote:
>
> > Thank you for the python reference, it is very instructive indeed.
> >
> > I find it strange that the type mapping specification is in a tutorial
> > by Sun and not in a more formal document.
> >
> Welcome to SUN API documentation ....

I found this link, maybe it is a little better:
http://java.sun.com/javase/6/docs/technotes/guides/jdbc/getstart/mapping.html#1004845
Regards,
Richard Broersma Jr.

Re: PreparedStatement rounds doubles to scale 14 during update

From
"Heikki Linnakangas"
Date:
Peter Kovacs wrote:
> One last question:
>
> How come that Java double, which is an 8 byte size floating point
> number, cannot fit in PostgreSQL's double precision type, which is
> equally a floating point type and 8 bytes in size?

Hmm. I'm not sure when the truncation happens. The value might actually
still be intact when it's converted from text representation to double,
and loses precision in the conversion from float8 to decimal. It's
possible to have a floating point value in the database that you can't
get out in text format without losing precision. Consider this for example:

postgres=#  SELECT
'6.118992224252588'::float8,
'6.11899222425259'::float8,
'6.11899222425259'::float8 = '6.118992224252588'::float8;
      float8      |      float8      | ?column?
------------------+------------------+----------
 6.11899222425259 | 6.11899222425259 | f
(1 row)

Both values are rounded to the same value on output, but if you compare
them, they're actually not equal.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com

Re: PreparedStatement rounds doubles to scale 14 during update

From
Kris Jurka
Date:

On Wed, 5 Sep 2007, Heikki Linnakangas wrote:

> Peter Kovacs wrote:
>> One last question:
>>
>> How come that Java double, which is an 8 byte size floating point
>> number, cannot fit in PostgreSQL's double precision type, which is
>> equally a floating point type and 8 bytes in size?
>
> Hmm. I'm not sure when the truncation happens. The value might actually
> still be intact when it's converted from text representation to double,
> and loses precision in the conversion from float8 to decimal. It's
> possible to have a floating point value in the database that you can't
> get out in text format without losing precision. Consider this for example:
>
> postgres=#  SELECT
> '6.118992224252588'::float8,
> '6.11899222425259'::float8,
> '6.11899222425259'::float8 = '6.118992224252588'::float8;
>      float8      |      float8      | ?column?
> ------------------+------------------+----------
> 6.11899222425259 | 6.11899222425259 | f
> (1 row)
>
> Both values are rounded to the same value on output, but if you compare
> them, they're actually not equal.
>

Actually you'll see different behavior with "SET extra_float_digits=2".
Is there a reason we shouldn't set this when we create a connection?

Kris Jurka

Re: PreparedStatement rounds doubles to scale 14 during update

From
Tom Lane
Date:
Kris Jurka <books@ejurka.com> writes:
> Actually you'll see different behavior with "SET extra_float_digits=2".
> Is there a reason we shouldn't set this when we create a connection?

Makes sense to me.

            regards, tom lane

Re: PreparedStatement rounds doubles to scale 14 during update

From
Kris Jurka
Date:

On Sun, 7 Oct 2007, Tom Lane wrote:

> Kris Jurka <books@ejurka.com> writes:
>> Actually you'll see different behavior with "SET extra_float_digits=2".
>> Is there a reason we shouldn't set this when we create a connection?
>
> Makes sense to me.
>

OK, done for 8.3dev.

Kris Jurka