United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6837951 Incorrect answer while summing serialized / deserialized BigDecimals
JDK-6837951 : Incorrect answer while summing serialized / deserialized BigDecimals

Details
Type:
Bug
Submit Date:
2009-05-06
Status:
Resolved
Updated Date:
2011-02-16
Project Name:
JDK
Resolved Date:
2009-05-08
Component:
core-libs
OS:
solaris_8,windows_xp
Sub-Component:
java.math
CPU:
x86
Priority:
P1
Resolution:
Fixed
Affected Versions:
6u14
Fixed Versions:
6u14 (b07)

Related Reports
Backport:
Relates:
Relates:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.6.0_14-ea"
Java(TM) SE Runtime Environment (build 1.6.0_14-ea-b03)
Java HotSpot(TM) Client VM (build 14.0-b12, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP / Professional Edition x64 / 2003 / SP3

A DESCRIPTION OF THE PROBLEM :
When this program is run with JDKs prior to jdk1.6.0_14 the correct answer is given and no errors occur.  When run with jdk1.6.0_14 (up to b05) then the incorrect answer is given and an error is detected.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No error printed on System.out.  The answer is:  1108240.4666936930160
ACTUAL -
An error printed on System.out.  Bad answer is:  781240.4666940200160

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package com.certive.cs.util;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;

public class BDTest
{
   protected static String[] S_arrayOfStringValues = new String[]
   {
      "117000.0", "83200.0", "0", "0", "0", "5280.0", "0", "69426.0",
      "6500.0", "7603.4919740918050", "7819.1205269716930", "9506.8541926295180",
      "31600.0", "39828.0", "71200.0", "24000.0", "14760.0", "84000.0",
      "0", "113184.0", "96333.0", "279000.0", "48000.0"
   };
   
   /**
    * @param args
    */
   public static void main(String[] args)
   {
      String         sFile = "C:/BDData.bd";
      BigDecimal[]   arrayOfBigDecimals = null;
            
      // convert strings to BigDecimals
      arrayOfBigDecimals = toBigDecimals( S_arrayOfStringValues );
      
      // write to the file?
      write( arrayOfBigDecimals, sFile );
            
      // read from the file?
      arrayOfBigDecimals = read( sFile );
      
      // print out the sum of the big decimals
      System.out.print( sum( arrayOfBigDecimals ) );
   }
   
   protected static void write( BigDecimal[] abd, String sFile )
   {
      try
      {
         FileOutputStream  fos = new FileOutputStream( sFile );
         DataOutputStream  dos = new DataOutputStream( fos );
         int               iValues = abd.length;
         
         dos.writeInt( iValues );
         
         for( int i = 0; i < iValues; i++ )
         {
            BigDecimal  bd = abd[i];
                        
            byte[]      aBytes = bd.unscaledValue().toByteArray();
            short       iBytes = (short) aBytes.length;
            short       iScale = (short) bd.scale();

            dos.writeShort( iBytes );
            dos.write( aBytes );
            dos.writeShort( iScale );
         }
         
         if ( dos != null )
            dos.close();
      }
      catch( Throwable t )
      {
         t.printStackTrace();
      }
   }
   
   protected static BigDecimal[] read( String sFile)
   {
      BigDecimal[]   abd = null;
      
      try
      {
         FileInputStream   fis = new FileInputStream( sFile );
         DataInputStream   dis = new DataInputStream( fis );
         int               iValues = dis.readInt();
         
         abd = new BigDecimal[iValues];
         
         for( int i = 0; i < iValues; i++ )
         {
            int         iBytes = dis.readShort();
            byte[]      aBytes = new byte[iBytes];
            
            dis.read( aBytes );
            
            int         iScale = dis.readShort();
            
            abd[i] = new BigDecimal(new BigInteger(aBytes), iScale);
         }
         
         if ( dis != null )
            dis.close();
      }
      catch( Throwable t )
      {
         t.printStackTrace();
      }
      
      return abd;
   }
   
   protected static BigDecimal sum( BigDecimal[] abd)
   {
      int         iLen = abd == null ? 0 : abd.length;
      BigDecimal  bdSum = iLen > 0 ? new BigDecimal( 0 ) : null;
      
      for( int i = 0; i < iLen; i++ )
      {
         BigDecimal  bdAdd = abd[i];
         BigDecimal  bdNewSum = bdSum.add( bdAdd );
         System.out.println( "Start: " + bdSum + "  Add: " + abd[i] + "  End: " + bdNewSum );
         
         if ( bdNewSum.intValue() == bdSum.intValue() && bdAdd.intValue() != 0 )
            System.out.println( "ERROR!  We didn't add a non-zero correctly!" );
         
         bdSum = bdNewSum;
      }
      
      return bdSum;
   }
   
   protected static BigDecimal[] toBigDecimals( String[] strValues )
   {
      int            iLen = strValues == null ? 0 : strValues.length;
      BigDecimal[]   abd = new BigDecimal[iLen];
      for ( int i = 0; i < iLen; i++ )
      {
         abd[i] = new BigDecimal( strValues[i] );
      }
      return abd;
   }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
None.  The JDK simply does not give the correct answer.

Release Regression From : 6u14
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

                                    

Comments
EVALUATION

The root cause of the failure is that in the BigDecimal(BigInteger ..) constructor, once we detect that the unscaled value can be made to fit into long, we forgot to set the intVal to be null. The BigDecimal.add method and many other places after the optimization put in assumed that if the unscaled value of the BigDecimal is not INFLATED, the intVal field should be null.

The fix is to set the intVal field to be null once we detect that the intCompact field is not INFLATED.
                                     
2009-05-07



Hardware and Software, Engineered to Work Together