views:

314

answers:

2

I have a csv file where amount and quantity fields are present in each detail record except header and trailer record. Trailer record has a total charge values which is the total sum of quantity multiplied by amount field in detail records . I need to check whether the trailer total charge value is equal to my calculated value of amount and quantity fields. I am using the double data type for all these calculations

In the csv file amount field appears as "10.12" or "10" or "10.0" or "10.456" or "10.4555" or "-10.12". Also amount can have a positive or negative value.

In csv file

H,ABC.....

"D",....,"1","12.23"

"D",.....,"3","-13.334"

"D",......,"2","12"

T,csd,123,12.345

------------------------------ While Validation i am having the below code --------------------

              double detChargeCount =0; 

              //From csv file i am reading trailer records charge value 
              String totChargeValue = items[3].replaceAll("\"","").trim(); 

              if (null != totChargeValue && !totChargeValue.equals("")) { 
                  detChargeCount = new Double(totChargeValue).doubleValue(); 

              if(detChargeCount==calChargeCount) 
                  validflag=true; 

-----------------------While reading CSV File i am having the below code

               if (null != chargeQuan && !chargeQuan.equals("")) { 
                      tmpChargeQuan=Long(chargeQuan).longValue(); 
                     } 

                if (null != chargeAmount && !chargeAmount.equals("")) { 
                      tmpChargeAmt=new Double(chargeAmount).doubleValue(); 
                          calChargeCount=calChargeCount+(tmpChargeQuan*tmpChargeAmt); 
                          } 

I had declared the variables tmpChargeQuan, tmpChargeAmt, calChargeCount as double

When i searched web i came to know that double might give issues for financial calculations so need to use BIGDECIMAL. But i am wondering is this scenario applies for my calculation. In my case amount value can have upto 5 or 6 digits after the decimal point" Can i use double datatype for this calculation? I am using it for validation. Will it create an problem if i use the above code with multiplication using double?

+2  A: 

Its not the matter of size, its a matter of expressing floating point numbers exactly. How the BigDecimal Class Helps Java Get its arithmetic right.

Adeel Ansari
Thanks a lot for the links. In BIGDECIMAL What is the default rounding. I dont want any rounding to happen because i am doing here a validation. sum each record's (quantity * amount) and finally check with the total charge amt value. If it matches i will add the values to tables.
Arav
Reading the documentation doesn't hurt. :) It will answer all your questions about Rounding:http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html
Jay
+3  A: 

I'll expand on what Adeel has already succintly answered. You can fit those numbers into a double datatype. The problem is when numbers get calculated, will they get calculated correctly? The answer is no - they will not. Generally it's not that much of a problem if you account for that with a delta, that is, your leeway in assuming whether or not a double value is equivalent to another double value. But for calculations involving exact numbers, such as monetary calculations, you must use a type such as BigDecimal to hold the values.

When you have this number: 1.23445 as a double, it may look like 1.23445 but it may actually be something like 1.234450000003400345543034

When you perform multiple calculations on numbers such as that, generally those extra places don't matter - however, over time, they will yield inaccurate results. With BigDecimal, when a number is specified as its String representation, it is that number - it does not suffer the "almost as good" problem doubles do.

I am updating this answer to include some notes from the double constructor of BigDecimal, found at this address.

The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.

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

When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

MetroidFan2002
Thanks a lotSo your saying even if i have 4 digits after the decimal point (Low precision). I might get errors while multiplication. Also i noticed bigdecimal has a setscale method for setting the number of digits after the decimal point and rounding method. In my case i dont need to do any rounding because i am doing a verification of the value only. Also my input might sometime have 2 or 3 or 0 digits after decimal point. Is there any default rounding for BigDecimal. I am going to assign the below wayBigDecimal aa =new BigDecimal("11.2359");
Arav
There's no default rounding for BigDecimal, unless you had already specified the number as a double. What you put in as a string is what you get. The rounding mode on setScale is definitely required if your scale is shorter than the current result - let's say you have 6.35, and you say setScale(1) - that will make it 6.3 or 6.4 depending on a rounding mode that you choose.
MetroidFan2002
Thanks a lot. So your saying if i enclose it in string BigDecimal("11.2359") and do a multiplication with another BigDecimal ("12") type it will not round the results? If i give it as double BigDecimal(11.2359) what kind of default rounding it does when i do a multiplication or any other operation. If i want to compare the values of two big decimals to see whether they are equal do i need to get the doublevalue and both and check?
Arav
Multiplication may expand the scale, so there's no rounding needed. Division may reduce the scale, so there may have to be rounding if the original scale was not big enough to hold the true result. When you use a double in BigDecimal, it stores it as the actual double representation - from the Javadoc linked above as regards to the double constructorhttp://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#BigDecimal(double)0.1 is *not* 0.1 when stored as a double, but new BigDecimal("0.1") is what you'd think it is.
MetroidFan2002
String cd="12.234" double abc =12.234; if i do a new BigDecimal(cd) or new BigDecimal(BigDecimal.valueof(abc) and use it for calculations does both give me the same result? also i am assuming valueof method uses Double.toString(double) method so it's same as giving new BigDecimal(Double.toString(abc)); I went thru the documentation. Also I didn't get what is difference between unscaled , scaled and precision. Could you post an example how Bigdecimal stores positive / negative value and what is being stored in unscaled,scaled,precision value
Arav
In regards to your first question, not if the String returned from abc is not exactly equal to the String "12.234". Output the String value of Double.toString(12.234) on your machine to see. If "12.234".equals(Double.toString(12.234)) then yes.Unscaled is your number without a decimal point. "12234" is your cd value *unscaled*. Scaled has a decimal point - "12.234" has a scale of 3, because there are three digits to the right of the decimal. Precision is how precise your number is - your number is prcise up to the thousanths decimal place.
MetroidFan2002
A comment on how BigDecimal stores its values, on the other hand, is far too much information - BigDecimal exists so we don't have to think about it too much. If you really want to, you can explore its source - just know that a scale is how many decimal places you have and that if you do calculations that might go over the scale of one of the numbers, rounding may have to be involved to fit the scale of that number if the calculation does not expand the scale automatically.
MetroidFan2002
so if i have number 13.12 then i can say unscaled value of this number is 1312 and scale value is 2. if i have a negative number -2.1234 then unscaled value of this number is 21234 and scale value of this number is 4 Hope i am correct?
Arav
Well, it depends - the unscaled value *is* 1312, and its scale, returned from BigDecimal.scale(), will be 2 - but if you meant the scaled value, the scaled value is still 13.12. The scaled value is the unscaled value including the decimal point - the scale just dictates where the decimal point goes. In your second query, I'm not sure if the unscaled value includes the negative - so you may be correct if it does not (otherwise it'd be -21234), the scale of that number is 4 and its scaled value is 2.1234 (which may include the negative, so it may be -2.1234).
MetroidFan2002
Thanks a lot for your detailed explanation.
Arav