Precision nightmare in Java and SQL Server - java

I've been struggling with precision nightmare in Java and SQL Server up to the point when I don't know anymore. Personally, I understand the issue and the underlying reason for it, but explaining that to the client half way across the globe is something unfeasible (at least for me).
The situation is this. I have two columns in SQL Server - Qty INT and Price FLOAT. The values for these are - 1250 and 10.8601 - so in order to get the total value its Qty * Price and result is 13575.124999999998 (in both Java and SQL Server). That's correct. The issue is this - the client doesn't want to see that, they see that number only as 13575.125 and that's it. On one place they way to see it in 2 decimal precision and another in 4 decimals. When displaying in 4 decimals the number is correct - 13575.125, but when displaying in 2 decimals they believe it is wrong - 13575.12 - should instead be 13575.13!
Help.

Your problem is that you are using floats. On the java side, you need to use BigDecimal, not float or double, and on the SQL side you need to use Decimal(19,4) (or Decimal(19,3) if it helps jump to your precision level). Do not use the Money type because math on the Money type in SQL causes truncation, not rounding. The fact that the data is stored as a float type (which you say is unchangeable) doesn't affect this, you just have to convert it at first opportunity before doing math on it.
In the specific example you give, you need to first get the 4 decimal precision number and put it in a BigDecimal or Decimal(19,4) as the case may be, and then further round it to 2 decimal precision. Then (if you are rounding up) you will get the result you want.

Use BigDecimal. Float is not an approciate type to represent money. It will handle the rounding properly. Float will always produce rounding errors.

For storing monetary amounts floating point values are not the way to go. From your description I would probably handle amounts as long integers with as value the monetary amount multiplied by 10^5 as database storage format.
You need to be able to handle calculations with amounts that do not loose precision, so here again floating point is not the way to go. If the total sums between debit and credit are off by 1 cent in a ledger, the ledger fails in the eyes of financial people, so make sure your software operates in their problem domain, not yours. If you can not use existing classes for monetary amounts, you need to build your own class that works with amount * 10^5 and formats according to the precision wanted only for input and output purposes.

Don't use the float datatype for
price. You should use "Money" or
"SmallMoney".
Here's a reference for [MS SQL
DataTypes][1].
[1]:
http://webcoder.info/reference/MSSQLDataTypes.html
Correction: Use Decimal(19,4)
Thanks Yishai.

I think I see the problem.
10.8601 cannot be represented perfectly, and so while the rounding to 13575.125 works OK it's difficult to get it to round to .13 because adding 0.005 just doesn't quite get there. And to make matters worse, 0.005 doesn't have an exact representation either, so you end up just slightly short of 0.13.
Your choices are then to either round twice, once to three digits and then once to 2, or do a better calculation to start with. Using long or a high precision format, scale by 1000 to get *.125 to *125. Do the rounding using precise integers.
By the way, it's not entirely correct to say one of the endlessly repeated variations on "floating point is inaccurate" or that it always produces errors. The problem is that the format can only represent fractions that you can sum negative powers of two to create. So, of the sequence 0.01 to 0.99, only .25, .50, and .75 have exact representations. Consequently, FP is best used, ironically, by scaling it so that only integer values are used, then it is as accurate as integer datatype arithmetic. Of course, then you might as well have just used fixed point integers to start with.
Be careful, scaling, say, 0.37 to 37 still isn't exact unless rounded. Floating point can be used for monetary values but it's more work than it is worth and typically the necessary expertise isn't available.

The FLOAT data type can't represent fractions accurately because it is base2 instead of base10. (See the convenient link :) http://gregs-blog.com/2007/12/10/dot-net-decimal-type-vs-float-type/).
For financial computations or anything that requires fractions to be represented accurately, the DECIMAL data type must be used.

If you can't fix the underlying database you can fix the java like this:
import java.text.DecimalFormat;
public class Temp {
public static void main(String[] args) {
double d = 13575.124999999;
DecimalFormat df2 = new DecimalFormat("#.##");
System.out.println( " 2dp: "+ Double.valueOf(df2.format(d)) );
DecimalFormat df4 = new DecimalFormat("#.####");
System.out.println( " 4dp: "+Double.valueOf(df4.format(d)) );
}
}

Although you shouldn't be storing the price as a float in the first place, you can consider converting it to decimal(38, 4), say, or money (note that money has some issues since results of expressions involving it do not have their scale adjusted dynamically), and exposing that in a view on the way out of SQL Server:
SELECT Qty * CONVERT(decimal(38, 4), Price)

So, given that you can't change the database structure (which would probably be the best option, given that you are using a non-fixed-precision to represent something that should be fixed/precise, as many others have already discussed), hopefully you can change the code somewhere. On the Java side, I think something like #andy_boot answered with would work. On the SQL side, you basically would need to cast the non-precise value to the highest precision you need and continue to cast down from there, basically something like this in the SQL code:
declare #f float,
#n numeric(20,4),
#m money;
select #f = 13575.124999999998,
#n = 13575.124999999998,
#m = 13575.124999999998
select #f, #n, #m
select cast(#f as numeric(20,4)), cast(cast(#f as numeric(20,4)) as numeric(20,2))
select cast(#f as money), cast(cast(#f as money) as numeric(20,2))

You can also do a DecimalFormat and then round using it.
DecimalFormat df = new DecimalFormat("0.00"); //or "0.0000" for 4 digits.
df.setRoundingMode(RoundingMode.HALF_UP);
String displayAmt = df.format((new Float(<your value here>)).doubleValue());
And I agree with others that you should not be using Float as a DB field type to store currency.

If you can't change the database to a fixed decimal datatype, something you might try is rounding by taking truncate((x+.0055)*10000)/10000. Then 1.124999 would "round" to 1.13 and give consistent results. Mathematically this is unreliable, but I think it would work in your case.

Related

Is it sufficient to convert a double to a BigDecimal just before addition to retain original precision?

We are solving a numeric precision related bug. Our system collects some numbers and spits their sum.
The issue is that the system does not retain the numeric precision, e.g. 300.7 + 400.9 = 701.599..., while expected result would be 701.6. The precision is supposed to adapt to the input values so we cannot just round results to fixed precision.
The problem is obvious, we use double for the values and addition accumulates the error from the binary representation of the decimal value.
The path of the data is following:
XML file, type xsd:decimal
Parse into a java primitive double. Its 15 decimal places should be enough, we expect values no longer than 10 digits total, 5 fraction digits.
Store into DB MySql 5.5, type double
Load via Hibernate into a JPA entity, i.e. still primitive double
Sum bunch of these values
Print the sum into another XML file
Now, I assume the optimal solution would be converting everything to a decimal format. Unsurprisingly, there is a pressure to go with the cheapest solution. It turns out that converting doubles to BigDecimal just before adding a couple of numbers works in case B in following example:
import java.math.BigDecimal;
public class Arithmetic {
public static void main(String[] args) {
double a = 0.3;
double b = -0.2;
// A
System.out.println(a + b);//0.09999999999999998
// B
System.out.println(BigDecimal.valueOf(a).add(BigDecimal.valueOf(b)));//0.1
// C
System.out.println(new BigDecimal(a).add(new BigDecimal(b)));//0.099999999999999977795539507496869191527366638183593750
}
}
More about this:
Why do we need to convert the double into a string, before we can convert it into a BigDecimal?
Unpredictability of the BigDecimal(double) constructor
I am worried that such a workaround would be a ticking bomb.
First, I am not so sure that this arithmetic is bullet proof for all cases.
Second, there is still some risk that someone in the future might implement some changes and change B to C, because this pitfall is far from obvious and even a unit test may fail to reveal the bug.
I would be willing to live with the second point but the question is: Would this workaround provide correct results? Could there be a case where somehow
Double.valueOf("12345.12345").toString().equals("12345.12345")
is false? Given that Double.toString, according to javadoc, prints just the digits needed to uniquely represent underlying double value, so when parsed again, it gives the same double value? Isn't that sufficient for this use case where I only need to add the numbers and print the sum with this magical Double.toString(Double d) method? To be clear, I do prefer what I consider the clean solution, using BigDecimal everywhere, but I am kind of short of arguments to sell it, by which I mean ideally an example where conversion to BigDecimal before addition fails to do the job described above.
If you can't avoid parsing into primitive double or store as double, you should convert to BigDecimal as early as possible.
double can't exactly represent decimal fractions. The value in double x = 7.3; will never be exactly 7.3, but something very very close to it, with a difference visible from the 16th digit or so on to the right (giving 50 decimal places or so). Don't be mislead by the fact that printing might give exactly "7.3", as printing already does some kind of rounding and doesn't show the number exactly.
If you do lots of computations with double numbers, the tiny differences will eventually sum up until they exceed your tolerance. So using doubles in computations where decimal fractions are needed, is indeed a ticking bomb.
[...] we expect values no longer than 10 digits total, 5 fraction digits.
I read that assertion to mean that all numbers you deal with, are to be exact multiples of 0.00001, without any further digits. You can convert doubles to such BigDecimals with
new BigDecimal.valueOf(Math.round(doubleVal * 100000), 5)
This will give you an exact representation of a number with 5 decimal fraction digits, the 5-fraction-digits one that's closest to the input doubleVal. This way you correct for the tiny differences between the doubleVal and the decimal number that you originally meant.
If you'd simply use BigDecimal.valueOf(double val), you'd go through the string representation of the double you're using, which can't guarantee that it's what you want. It depends on a rounding process inside the Double class which tries to represent the double-approximation of 7.3 (being maybe 7.30000000000000123456789123456789125) with the most plausible number of decimal digits. It happens to result in "7.3" (and, kudos to the developers, quite often matches the "expected" string) and not "7.300000000000001" or "7.3000000000000012" which both seem equally plausible to me.
That's why I recommend not to rely on that rounding, but to do the rounding yourself by decimal shifting 5 places, then rounding to the nearest long, and constructing a BigDecimal scaled back by 5 decimal places. This guarantees that you get an exact value with (at most) 5 fractional decimal places.
Then do your computations with the BigDecimals (using the appropriate MathContext for rounding, if necessary).
When you finally have to store the number as a double, use BigDecimal.doubleValue(). The resulting double will be close enough to the decimal that the above-mentioned conversion will surely give you the same BigDecimal that you had before (unless you have really huge numbers like 10 digits before the decimal point - the you're lost with double anyway).
P.S. Be sure to use BigDecimal only if decimal fractions are relevant to you - there were times when the British Shilling currency consisted of twelve Pence. Representing fractional Pounds as BigDecimal would give a disaster much worse than using doubles.
It depends on the Database you are using. If you are using SQL Server you can use data type as numeric(12, 8) where 12 represent numeric value and 8 represents precision. similarly, for my SQL DECIMAL(5,2) you can use.
You won't lose any precision value if you use the above-mentioned datatype.
Java Hibernate Class :
You can define
private double latitude;
Database:

Is double the correct datatype to calculate decimal percentage?

I want to calculate percentage in my project and I am using double for that. Suggest me the correct data type to calculate percentage in decimal like 18% of 5.368 should give 0.966 exact.
I want the result truncated to 3 decimal places.
I am using this:
EditText kundan = (EditText) findViewById(R.id.kundan);
double kundangiven = Double.parseDouble(kundan.getText().toString());
EditText loss = (EditText)findViewById(R.id.losspercentage);
double lossinkundan = Double.parseDouble(loss.getText().toString());
losspercent = (lossinkundan * kundangiven) / 100 ;
losspercent = losspercent % 10 ;
displayTotalloss(losspercent);
Your original code works fine regarding storing percentage calculations in doubles. Regarding formatting the result to 3 decimal places, use String.format(), e.g.
String.format("%.3f", losspercent);
Now that I better understand your needs, this is the way to get a double to 3 digits of precision for display:
String.format("%.3f", losspercent);
This returns a String, so it can be returned from a function, passed directly to your display function, or stored in a variable of type String
It sounds like you are asking whether you are using the correct data type to store your computed percentage, or whether there is a better option.
Given your present code structure, I would say that yes, double is the right choice. Any time you are dividing (or multiplying for that matter) non-integer numbers, there is a good chance that an exact result requires a higher level of precision for the output than for the inputs. The memory cost of using double instead of float here is probably negligible, so double seems like the obvious choice.
If you are really, really concerned about accuracy, then you could use BigDecimal. BigDecimal is not a primitive though, so it would cost more in memory and processing (although still probably not noticeable in this example).
Of course, if you really want to use the "right" data type, and you have control over the code base, you could create your own data type. I think it is unnecessary here though. double is perfectly suitable. If you are concerned about readability, you may consider creating a function that takes a double x and an int p, and returns a double representing p percent of x.

Subtraction of numbers double and long

In my JAVA program there is code like this:
int f_part = (int) ((f_num - num) * 100);
f_num is double and num is long. I just want to take the fractional part out and assign it to f_part. But some times f_part value is one less than it's value. Which means if f_num = 123.55 and num = 123, But f_part equals to 54. And it happens only f_num and num is greater than 100. I don't know why this happening. Please can someone explain why this happens and way to correct it.
This is due to the limited precision in doubles.
The root of your problem is that the literal 123.55 actually represents the value 123.54999....
It may seem like it holds the value 123.55 if you print it:
System.out.println(123.55); // prints 123.55
but in fact, the printed value is an approximation. This can be revealed by creating a BigDecimal out of it, (which provides arbitrary precision) and print the BigDecimal:
System.out.println(new BigDecimal(123.55)); // prints 123.54999999999999715...
You can solve it by going via Math.round but you would have to know how many decimals the source double actually entails, or you could choose to go through the string representation of the double in fact goes through a fairly intricate algorithm.
If you're working with currencies, I strongly suggest you either
Let prices etc be represented by BigDecimal which allows you to store numbers as 0.1 accurately, or
Let an int store the number of cents (as opposed to having a double store the number of dollars).
Both ways are perfectly acceptable and used in practice.
From The Floating-Point Guide:
internally, computers use a format (binary floating-point) that cannot
accurately represent a number like 0.1, 0.2 or 0.3 at all.
When the code is compiled or interpreted, your “0.1” is already
rounded to the nearest number in that format, which results in a small
rounding error even before the calculation happens.
It looks like you're calculating money values. double is a completely inappropriate format for this. Use BigDecimal instead.
int f_part = (int) Math.round(((f_num - num) * 100));
This is one of the most often asked (and answered) questions. Floating point arithmetics can not produce exact results, because it's impossible to have an inifinity of real numbers inside 64 bits. Use BigDecimal if you need arbitrary precision.
Floating point arithmetic is not as simple as it may seem and there can be precision issues.
See Why can't decimal numbers be represented exactly in binary?, What Every Computer Scientist Should Know About Floating-Point Arithmetic for details.
If you need absolutely sure precision, you might want to use BigDecimal.

ArithmeticException thrown during BigDecimal.divide

I thought java.math.BigDecimal is supposed to be The Answer™ to the need of performing infinite precision arithmetic with decimal numbers.
Consider the following snippet:
import java.math.BigDecimal;
//...
final BigDecimal one = BigDecimal.ONE;
final BigDecimal three = BigDecimal.valueOf(3);
final BigDecimal third = one.divide(three);
assert third.multiply(three).equals(one); // this should pass, right?
I expect the assert to pass, but in fact the execution doesn't even get there: one.divide(three) causes ArithmeticException to be thrown!
Exception in thread "main" java.lang.ArithmeticException:
Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide
It turns out that this behavior is explicitly documented in the API:
In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3. If the quotient has a non-terminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.
Browsing around the API further, one finds that in fact there are various overloads of divide that performs inexact division, i.e.:
final BigDecimal third = one.divide(three, 33, RoundingMode.DOWN);
System.out.println(three.multiply(third));
// prints "0.999999999999999999999999999999999"
Of course, the obvious question now is "What's the point???". I thought BigDecimal is the solution when we need exact arithmetic, e.g. for financial calculations. If we can't even divide exactly, then how useful can this be? Does it actually serve a general purpose, or is it only useful in a very niche application where you fortunately just don't need to divide at all?
If this is not the right answer, what CAN we use for exact division in financial calculation? (I mean, I don't have a finance major, but they still use division, right???).
If this is not the right answer, what CAN we use for exact division in financial calculation? (I mean, I don't have a finance major, but they still use division, right???).
Then I was in primary school1, they taught me that when you divide by 1 by 3 you get a 0.33333... i.e. a recurring decimal. Division of numbers represented in decimal form is NOT exact. In fact for any fixed base there will be fractions (the result of dividing one integer by another) that cannot be represented exactly as a finite precision floating point number in that base. (The number will have a recurring part ...)
When you do financial calculations involving division, you have to consider the what to do with a recurring fraction. You can round it up, or down, or to the nearest whole number, or something else, but basically you cannot just forget about the issue.
The BigDecimal javadoc says this:
The BigDecimal class gives its user complete control over rounding behavior. If no rounding mode is specified and the exact result cannot be represented, an exception is thrown; otherwise, calculations can be carried out to a chosen precision and rounding mode by supplying an appropriate MathContext object to the operation.
In other words, it is your responsibility to tell BigDecimal what to do about rounding.
EDIT - in response to these followups from the OP.
How does BigDecimal detect infinite recurring decimal?
It does not explicitly detect the recurring decimal. It simply detects that the result of some operation cannot be represented exactly using the specified precision; e.g. too many digits are required after the decimal point for an exact representation.
It must keep track of and detect a cycle in the dividend. It COULD HAVE chosen to handle this another way, by marking where the recurring portion is, etc.
I suppose that BigDecimal could have been specified to represent a recurring decimal exactly; i.e. as a BigRational class. However, this would make the implementation more complicated and more expensive to use2. And since most people expect numbers to be displayed in decimal, and the problem of recurring decimal recurs at that point.
The bottom line is that this extra complexity and runtime cost would be inappropriate for typical use-cases for BigDecimal. This includes financial calculations, where accounting conventions do not allow you to use recurring decimals.
1 - It was an excellent primary school. You may have been taught this in high school.
2 - Either you try to remove common factors of the divisor and dividend (computationally expensive), or allow them to grow without bounds (expensive in space usage and computationally expensive for subsequent operations).
The class is BigDecimal not BigFractional. From some of your comments it sounds like you just want to complain that someone didn't build in all possible number handling algorithms into this class. Financial apps do not need infinite decimal precision; just perfectly accurate values to the precision required (typically 0, 2, 4, or 5 decimal digits).
Actually I have dealt with many financial applications that use double. I don't like it but that was the way they are written (not in Java either). When there are exchange rates and unit conversions then there are both the potential of rounding and bruising problems. BigDecimal eliminates the later but there is still the former for division.
If you want to work with decimals, not rational numbers, and you need exact arithmetics before the final rounding (rounding to cents or something), here's a little trick.
You can always manipulate your formulas so that there's only one final division. That way you won't lose precision during calculations and you'll always get the correctly rounded result. For instance
a/b + c
equals
(a + bc) / b.
By the way, I'd really appreciate
insight from people who've worked with
financial software. I often heard
BigDecimal being advocated over double
In financial reports we use alwasy BigDecimal with scale = 2 and ROUND_HALF_UP, since all printed values in a report must be lead to a reproducable result. If someone checks this using a simple calculator.
In switzerland they round to 0.05 since they no longer have 1 or 2 Rappen coins.
You should prefer BigDecimal for finance calculations. Rounding should be specified by the business. E.g. an amount (100,00$) has to be split equally across three accounts. There has to be a business rule which account takes the extra cent.
Double, floats are not approriate for use in financial applications because they can not represent fractions of 1 precisely that are not exponentials of 2. E.g. consider 0.6 = 6/10 = 1*1/2 + 0*1/4 + 0*1/8 + 1*1/16 + ... = 0.1001...b
For mathematic calculations you can use a symbolic number, e.g. storing denominator and numerator or even a whole expression (e.g. this number is sqrt(5)+3/4). As this is not the main use case of the java api you won' find it there.
Is there a need for
a=1/3;
b=a*3;
resulting in
b==1;
in financial systems? I guess not. In financial systems it is defined, which roundmode and scale has to be used, when doing calculations. In some situations, the roundmode and scale is defined in the law. All components can rely on such a defined behaviour. Returning b==1 would be a failure, because it would not fulfill the specified behaviour. This is very important when calculating prices etc.
It is like the IEEE 754 specifications for representing floats in binary digits. A component must not optimize a "better" representation without loss of information, because this will break the contract.
To divide save, you have to set the MATHcontext,
BigDecimal bd = new BigDecimal(12.12, MathContext.DECIMAL32).divide(new BigDecimal(2)).setScale(2, RoundingMode.HALF_UP);
I accept that Java doesn't have great support for representing fractions, but you have to realise that it is impossible to keep things entirely precise when working with computers. At least in this case, the exception is telling you that precision is being lost.
As far as I know, "infinite precision arithmetic with decimal numbers" just isn't going to happen. If you have to work with decimals, what you're doing is probably fine, just catch the exceptions. Otherwise, a quick google search finds some interesting resources for working with fractions in Java:
http://commons.apache.org/math/userguide/fraction.html
http://www.merriampark.com/fractions.htm
Best way to represent a fraction in Java?
Notice we are using a computer... A computer has a lot of ram and precision takes ram. So when you want an infinite precision you need
(infinite * infinite) ^ (infinite * Integer.MAX_VALUE) terrabyte ram...
I know 1 / 3 is 0.333333... and it should be possible to store it in ram like "one divided by three" and then you can multiply it back and you should have 1. But I don't think Java has something like that...
Maybe you have to win the Nobel Price for writing something doing that. ;-)

data type to represent a big decimal in java

Which data type is apt to represent a decimal number like "10364055.81".
If tried using double:
double d = 10364055.81;
But when I try to print the number, its displaying as "1.036405581E7", which I don't want.
Should I use BigDecimal? But its displaying as 10364055.81000000052154064178466796875.
Is there any datatype that displays the values as it is? Also the number may be bigger than the one taken as example.
BTW, will using BigDecimal effect the performance of the application?? I might use this in almost all my DTOs.
You should use BigDecimal - but use the String constructor, e.g.:
new BigDecimal("10364055.81");
If you pass a double to BigDecimal, Java must create that double first - and since doubles cannot represent most decimal fractions accurately, it does create the value as 10364055.81000000052154064178466796875 and then passes it to the BigDecimal constructor. In this case BigDecimal has no way of knowing that you actually meant the rounder version.
Generally speaking, using non-String constructors of BigDecimal should be considered a warning that you're not getting the full benefit of the class.
Edit - based on rereading exactly what you wanted to do, my initial claim is probably too strong. BigDecimal is a good choice when you need to represent decimal values exactly (money handling being the obvious choice, you don't want 5.99 * one million to be 5990016.45 for example.
But if you're not worried about the number being stored internally as a very slightly different value to the decimal literal you entered, and just want to print it out again in the same format, then as others have said, an instance of NumberFormat (in this case, new DecimalFormat("########.##")) will do the trick to output the double nicely, or String.format can do much the same thing.
As for performance - BigDecimals will naturally be slower than using primitives. Typically, though, unless the vast majority of your program involves mathematical manipulations, you're unlikely to actually notice any speed difference. That's not to say you should use BigDecimals all over; but rather, that if you can get a real benefit from their features that would be difficult or impossible to realise with plain doubles, then don't sweat the miniscule performance difference they theoretically introduce.
How a number is displayed is distinct from how the number is stored.
Take a look at DecimalFormat for controlling how you can display your numbers when a double (or float etc.).
Note that choosing BigDecimal over double (or vice versa) has pros/cons, and will depend on your requirements. See here for more info. From the summary:
In summary, if raw performance and
space are the most important factors,
primitive floating-point types are
appropriate. If decimal values need to
be represented exactly, high-precision
computation is needed, or fine control
of rounding is desired, only
BigDecimal has the needed
capabilities.
A double would be enough in order to save this number. If your problem is you don't like the format when printing or putting it into a String, you might use NumberFormat: http://java.sun.com/javase/6/docs/api/java/text/NumberFormat.html
you can use double and display if with System.out.printf().
double d = 100003.81;
System.out.printf("%.10f", d);
.10f - means a double with precision of 10

Categories

Resources