Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion Groups
Database Servers
DB2InformixIngresMS SQLOraclePervasive.SQLPostgreSQLProgressSybase
Desktop Databases
FileMakerFoxProMS AccessParadox
General
General DB TopicsDatabase Theory
Related Topics
Java Development.NET DevelopmentVB DevelopmentMore Topics ...

Database Forum / DB2 Topics / February 2006

Tip: Looking for answers? Try searching our database.

IBM JDBC driver behaves differently on Linux than on Solaris

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Joe Weinstein - 16 Feb 2006 17:39 GMT
Hi. Below is a simple JDBC program to insert and extract
a numerical value. When ResultSet.getDouble() is called,
the same program produces different output on solaris
than it does on Linux. I would be grateful for any
discussion of this!
thanks,
Joe Weinstein at BEA Systems

Results on Linux Box
-----------------------------------------------------------------------
-bash-2.05b$ java db2
08.02.0001
Database version is 08.02.0000
Testing value 1.001
native driver object is a class java.math.BigDecimal
 value is 1.001
as getDouble it's 1.0010000000000001                    <<<<<<<<<< SEE THIS!
native driver object is a class java.math.BigDecimal
 value is 1.001
as getDouble it's 1.0010000000000001                    <<<<<<<<<< SEE THIS!
double d = 1.001d gives 1.001
new Double("1.001").doubleValue() gives 1.001
they are equal.
new BigDecimal(1.001d) gives 1.0009999999999998898658759571844711899757385253906
25
new BigDecimal( new Double("1.001").doubleValue() ) gives 1.000999999999999889865875957184471189975738525390625

Results on Solaris Box
---------------------------------------------------------------------------

bash-3.00$java db2
08.02.0002
Database version is 08.02.0000
Testing value 1.001
native driver object is a class java.math.BigDecimal
 value is 1.001
as getDouble it's 1.001                                <<<<<<<<<< DIFFERENT!
native driver object is a class java.math.BigDecimal
 value is 1.001                                        <<<<<<<<<< DIFFERENT!
as getDouble it's 1.001
double d = 1.001d gives 1.001
new Double("1.001").doubleValue() gives 1.001
they are equal.
new BigDecimal(1.001d) gives 1.000999999999999889865875957184471189975738525390625
new BigDecimal( new Double("1.001").doubleValue() ) gives 1.000999999999999889865875957184471189975738525390625

The program:

------------------------------------------------------------------------------------------
import java.io.*;
import java.util.*;
import java.net.*;
import java.sql.*;

import weblogic.common.*;

public class db2
{
  public static void main(String argv[])
    throws Exception
  {
    Connection c  = null;
    try
    {

        java.util.Properties props = new java.util.Properties();
        Driver d = (Driver)Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
        props.put("user",     "wls");
        props.put("password", "wls");
        props.put("DatabaseName",  "wls");
        String URL = "jdbc:db2:wls1";

        c = d.connect(URL, props);

        DatabaseMetaData dm = c.getMetaData();
        System.out.println(dm.getDriverVersion());
        System.out.println("Database version is " + dm.getDatabaseProductVersion() );

        Statement s = c.createStatement();
        try{s.executeUpdate("drop table joe");}catch (Exception ignore){}
        s.executeUpdate("create table joe (bar decimal(4,3))");
        s.executeUpdate("insert into joe values(1.001)");
        PreparedStatement p = c.prepareStatement("insert into joe values(?)");
        p.setDouble(1, new Double("1.001").doubleValue());
        p.executeUpdate();

        System.out.println("Testing value " + new Double("1.001").doubleValue() );
        ResultSet r = s.executeQuery("select * from joe");
        while (r.next())
        {
          System.out.println("native driver object is a " + r.getObject(1).getClass() );
          System.out.println(" value is " + r.getObject(1) );
          System.out.println("as getDouble it's " + r.getDouble(1) );
        }

        double d = 1.001d;
        double dd = new Double("1.001").doubleValue();
        System.out.println("double d = 1.001d gives " + d );
        System.out.println("new Double(\"1.001\").doubleValue() gives " + dd );
        if (d == dd)
          System.out.println("they are equal." );
        else
          System.out.println("they are not equal." );

        System.out.println("new BigDecimal(1.001d) gives " + new BigDecimal(1.001d) );
        System.out.println("new BigDecimal( new Double(\"1.001\").doubleValue() ) gives "
         + new BigDecimal( new Double("1.001").doubleValue() ) );

    }
    catch (Exception e)
    { e.printStackTrace(); }
    finally
    { try {c.close();}catch (Exception e){} }
  }
}
Knut Stolze - 17 Feb 2006 07:28 GMT
> Results on Linux Box
> --------------------------------------------------------------
[quoted text clipped - 38 lines]
> 1.000999999999999889865875957184471189975738525390625
> ------------------------------------------------------------------

You should compare the binary representation of the double value.  I assume
that _those_ are identical and what you are seeing is due to varying
implementations in your JVMs when the double value is converted to a
string.

Signature

Knut Stolze
DB2 Information Integration Development
IBM Germany

Joe Weinstein - 17 Feb 2006 18:08 GMT
Thanks much. I think you're right. I'll do that.
thanks,
Joe

>>Results on Linux Box
>>--------------------------------------------------------------
[quoted text clipped - 43 lines]
> implementations in your JVMs when the double value is converted to a
> string.
Joe Weinstein - 17 Feb 2006 20:57 GMT
Hi Knut. I just rechecked the code I sent, and it includes
some stuff to verify whether the JVM itself is printing
stuff differently, and it's not. It's just the driver's
getDouble() that behaves differently.
   If you see anything I missed about checking the
JVM itself, let me know what to try. Thanks
Joe

        Statement s = c.createStatement();
        try{s.executeUpdate("drop table joe");}catch (Exception ignore){}
        s.executeUpdate("create table joe (bar decimal(4,3))");
        s.executeUpdate("insert into joe values(1.001)");
        PreparedStatement p = c.prepareStatement("insert into joe values(?)");
        p.setDouble(1, new Double("1.001").doubleValue());
        p.executeUpdate();

        System.out.println("Testing value " + new Double("1.001").doubleValue() );
        ResultSet r = s.executeQuery("select * from joe");
        while (r.next())
        {
          System.out.println("native driver object is a " + r.getObject(1).getClass() );
          System.out.println(" value is " + r.getObject(1) );
          System.out.println("as getDouble it's " + r.getDouble(1) );
        }

        double d = 1.001d;
        double dd = new Double("1.001").doubleValue();
        System.out.println("double d = 1.001d gives " + d );
        System.out.println("new Double(\"1.001\").doubleValue() gives " + dd );
        if (d == dd)
          System.out.println("they are equal." );
        else
          System.out.println("they are not equal." );

        System.out.println("new BigDecimal(1.001d) gives " + new BigDecimal(1.001d) );
        System.out.println("new BigDecimal( new Double(\"1.001\").doubleValue() ) gives "
         + new BigDecimal( new Double("1.001").doubleValue() ) );

>>Results on Linux Box
>>--------------------------------------------------------------
[quoted text clipped - 43 lines]
> implementations in your JVMs when the double value is converted to a
> string.
Knut Stolze - 17 Feb 2006 21:01 GMT
> Hi Knut. I just rechecked the code I sent, and it includes
> some stuff to verify whether the JVM itself is printing
[quoted text clipped - 35 lines]
>          Double(\"1.001\").doubleValue() ) gives "
>           + new BigDecimal( new Double("1.001").doubleValue() ) );

You are not looking at the binary representation of the numbers.  Everything
else are just the usual rounding issues related to the conversion between
the internal binary format and the external decimal format.

You should also read this article to understand how floating point numbers
are represented in today's computer systems:
http://cch.loria.fr/documentation/IEEE754/ACM/goldberg.pdf

Signature

Knut Stolze
DB2 Information Integration Development
IBM Germany

Joe Weinstein - 17 Feb 2006 21:28 GMT
>>Hi Knut. I just rechecked the code I sent, and it includes
>>some stuff to verify whether the JVM itself is printing
[quoted text clipped - 39 lines]
> else are just the usual rounding issues related to the conversion between
> the internal binary format and the external decimal format.

Actually, yes I am. I am banking off the spec for BigDecimal:
    "Translates a double into a BigDecimal which is the exact decimal
    representation of the double's binary floating-point value."

I'll see if I can make some more additions to make it clearer to me.
thanks,
Joe

> You should also read this article to understand how floating point numbers
> are represented in today's computer systems:
> http://cch.loria.fr/documentation/IEEE754/ACM/goldberg.pdf
Knut Stolze - 20 Feb 2006 07:39 GMT
> Actually, yes I am. I am banking off the spec for BigDecimal:
>      "Translates a double into a BigDecimal which is the exact decimal
>      representation of the double's binary floating-point value."

The issue I still have with this is that you are dealing with different
representations of floating point numbers and that you are still not
comparing the internal binary IEEE754 representation.

The Java manual (http://java.sun.com/j2se/1.4.2/docs/api/index.html) gives a
nice example, which most probably applies to your question:
-----------------------------------------------------
public BigDecimal(double val)

   Translates a double into a BigDecimal. The scale of the BigDecimal is
the smallest value such that (10scale * val) is an integer.

   Note: the results of this constructor can be somewhat unpredictable. One
might assume that new BigDecimal(.1) is exactly equal to .1, but it is
actually equal to .1000000000000000055511151231257827021181583404541015625.
This is so because .1 cannot be represented exactly as a double (or, for
that matter, as a binary fraction of any finite length). Thus, the long
value that is being passed in to the constructor is not exactly equal
to .1, appearances notwithstanding.

   The (String) constructor, on the other hand, is perfectly predictable:
new BigDecimal(".1") is exactly equal to .1, as one would expect.
Therefore, it is generally recommended that the (String) constructor be
used in preference to this one.
-----------------------------------------------------

I would recommend that you try "Double.compare" or
"Double.doubleToRawLongBits" instead.

Signature

Knut Stolze
DB2 Information Integration Development
IBM Germany

Joe Weinstein - 20 Feb 2006 16:00 GMT
>>Actually, yes I am. I am banking off the spec for BigDecimal:
>>     "Translates a double into a BigDecimal which is the exact decimal
[quoted text clipped - 28 lines]
> I would recommend that you try "Double.compare" or
> "Double.doubleToRawLongBits" instead.

I have also compared the doubles. It is only the Linux IBM getDouble()
method that fails. The getBigDecimal obviously gets the correct input
from the DBMS and produces an exact 1.001 in both cases, so we can
assume that the getDouble() has the same input. Using "1.001" as well
as the double approximation for 1.001 in every way I can on both
platforms, and I always get the same long decimal approximation on
Linux as I do on Solaris. It's not unpredictable:
1.0010000000000001119104808822157792747020721435546875. I believe the
problem is somewhere in the IBM driver's code, that it comes up with
1.0010000000000001.

I may be taking up too much of your time, but I thank you for what you've
posted so far. Can you direct me to any IBM forum that might be more
closely interested in JDBC? I would pursue it there. Or if you have some
generic Java code that you'd suggest to prove/disprove your theory that
it's the JVM, I'll run it in both places.
thanks again,
Joe Weinstein at BEA
Knut Stolze - 20 Feb 2006 17:24 GMT
> I have also compared the doubles. It is only the Linux IBM getDouble()
> method that fails. The getBigDecimal obviously gets the correct input
> from the DBMS and produces an exact 1.001 in both cases, so we can
> assume that the getDouble() has the same input.

Have you used the "Double.doubleToRawLongBits" and printed that number?
This is giving you the internal binary representation.

Signature

Knut Stolze
DB2 Information Integration Development
IBM Germany

Joe Weinstein - 21 Feb 2006 16:15 GMT
>>I have also compared the doubles. It is only the Linux IBM getDouble()
>>method that fails. The getBigDecimal obviously gets the correct input
[quoted text clipped - 3 lines]
> Have you used the "Double.doubleToRawLongBits" and printed that number?
> This is giving you the internal binary representation.

Hi, yes I did. If the internal binary representation was different in any way,
then the BigDecimal constructor, which makes an exact decimal version of the
given double, would also differ. On both platforms, the double that prints as
1.001 has the same raw bits pattern as well as BigDecimal conversion:
doubleToRawLongBits gives 4607186922399644778
for a BigDecimal value   1.000999999999999889865875957184471189975738525390625
This is the same for the Solaris getDouble().

It is *only* the Double returned by the driver's
getDouble() on Linux that prints as 1.0010000000000001,
with a raw bits pattern of 4607186922399644779,
which makes a BigDecimal 1.0010000000000001119104808822157792747020721435546875

I can send you or anyone else in IBM the standalone
repro.
thanks
Joe Weinstein at BEA Systems
Knut Stolze - 21 Feb 2006 17:24 GMT
> Hi, yes I did. If the internal binary representation was different in any
> way, then the BigDecimal constructor, which makes an exact decimal version
[quoted text clipped - 5 lines]
> getDouble() on Linux that prints as 1.0010000000000001,
> with a raw bits pattern of 4607186922399644779,

Now I got it, I believe.  Sorry that I was a bit too dense until now.

I tried this myself, and on Linux I get the raw bits: 4607186922399644778
from the following SQL statement:

SELECT DECIMAL(1.001, 5, 3) FROM sysibm.sysdummy1

I'm using the "com.ibm.db2.jcc.DB2Driver" and a type 4 driver.

So it would be a good idea to know more details about your environment, i.e.
DB2 version and FP level, etc.

Signature

Knut Stolze
DB2 Information Integration Development
IBM Germany

Joe Weinstein - 21 Feb 2006 17:38 GMT
>>Hi, yes I did. If the internal binary representation was different in any
>>way, then the BigDecimal constructor, which makes an exact decimal version
[quoted text clipped - 17 lines]
> So it would be a good idea to know more details about your environment, i.e.
> DB2 version and FP level, etc.

Thanks much. Database version is 08.02.0000. The driver is type-2.
That is the likely reason for the bug (platform dependence)
Joe
Joe Weinstein - 20 Feb 2006 16:13 GMT
Oh, I just checked that last reply, sorry: The exact decimal
representation of the double I get on both platforms for
1.001, either directly declared ar via a string constructor
or the getDouble() on Solaris, is actually:

1.000999999999999889865875957184471189975738525390625

On both platforms, Linux and Solaris, the double I construct
with those bits, or from the Solaris getDouble() prints as
1.001.

The value I misquoted in the last post was what I get
by displaying the bits exactly of the double that the
driver's getDouble() on linux returned. It prints as
1.0010000000000001

it's bits (as decimal) are:

1.0010000000000001119104808822157792747020721435546875
Bernd Hohmann - 17 Feb 2006 09:12 GMT
> Hi. Below is a simple JDBC program to insert and extract
> a numerical value. When ResultSet.getDouble() is called,
> the same program produces different output on solaris
> than it does on Linux.

String-Output of double/float values depends on the JVM.

But this isn't a problem of Java - its a problem we have since the
invention of floating point operations.

Thats why mercantile applications are using BCD, fixed point or simply
rounding every result to the precision needed.

Bernd
Joe Weinstein - 17 Feb 2006 18:09 GMT
>> Hi. Below is a simple JDBC program to insert and extract
>> a numerical value. When ResultSet.getDouble() is called,
[quoted text clipped - 10 lines]
>
> Bernd

Thanks. I should have checked this out. I will.
Joe
Bernd Hohmann - 17 Feb 2006 18:23 GMT
> Thanks. I should have checked this out. I will.

Glad to help. If you need more or something else - DB2 JDBC bothers me
since years :-)

Bernd
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.