Float precision with specific numbers - java

The following value gives me wrong precision. It is observed with only specific numbers. It might be a floating representation problem, but wanted to know the specific reason.
String m = "154572.49"; //"154,572.49";
Float f = Float.parseFloat(m);
System.out.println(f);
The output it is printing is 154572.48 instead of 154572.49.

If you want decimal numbers to come out as exactly as you entered them in Java, use BigDecimal instead of float.
Floating point numbers are inherently inaccurate for decimals because many numbers that terminate in decimal (e.g. 0.1) are recurring numbers in binary and floating point numbers are stored as a binary representation.

You must read this What Every Computer Scientist Should Know About Floating-Point Arithmetic
Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. Although there are infinitely many integers, in most programs the result of integer computations can be stored in 32 bits. In contrast, given any fixed number of bits, most calculations with real numbers will produce quantities that cannot be exactly represented using that many bits. Therefore the result of a floating-point calculation must often be rounded in order to fit back into its finite representation. This rounding error is the characteristic feature of floating-point computation.

Float offers a base 2 representation of a decimal number. When you parse, it is parsing the binary representation of the decimal number that will almost never be exact. You may get .4856 from its binary representation (well, I didn't do the calculation and its just a guess to get you the idea).

Related

Math.cos(Math.radians(90)) doesn't give 0 [duplicate]

alert(Math.cos(Math.PI/2));
Why the result is not exact zero? Is this inaccurancy, or some implementation error?
Math.PI/2 is an approximation of the real value of pi/2. Taking the exact cosine of this approximated value won't yield zero. The value you get is an approximation of this exact value up to the precision of the underlying floating point datatype.
Using some arbitrary precision library, you can evaluate the difference between pi/2 in double precision and the exact value to
0.0000000000000000612323399573676588613032966137500529104874722961...
Since the slope of the cosine close to its zeros is 1, you would expect the cosine of the approximation of pi/2 to be approximately equal to this difference, and indeed it is.
Floating-point numbers are normally approximations. Since floating-point numbers are represented in memory as binary numbers multiplied by an exponent only numbers that are sums of powers of 2 can usually be represented.
Fractions such as 1/3 can't be written as a binary number and as such have no accurate floating-point representation. Even some numbers that can be written accurately in decimal, such as 0.1, can't be represented accurately in binary and so will not be represented correctly in floating point.
PI is an irrational number and can't be represented as floating-point, so there will be rounding errors. Do not compare floating-point numbers for equality without including a tolerance parameter. This link has a good explanation of the basics.
Comparing calculated floating point numbers for equality is almost always a bad idea, since (as others have stated) they are approximations, and errors appear.
Instead of checking for a==b, check for equality to within a threshold that makes sense for your application, as with Math.abs(a-b) < .00001. This is good practice in any programming language that represents numbers as floating point values.
If you're storing integers in floating point variables and just adding, subtracting, and multiplying, they'll stay integers (at least until they go out of bounds). But dividing, using trig functions, etc., will introduce errors that must be allowed for.
-m#

Why is 8099.99975f != 8100f?

Edit: I know floating point arithmetic is not exact. And the arithmetic isn't even my problem. The addition gives the result I expected. 8099.99975f doesn't.
So I have this little program:
public class Test {
public static void main(String[] args) {
System.out.println(8099.99975f); // 8099.9995
System.out.println(8099.9995f + 0.00025f); // 8100.0
System.out.println(8100f == 8099.99975f); // false
System.out.println(8099.9995f + 0.00025f == 8099.99975f); // false
// I know comparing floats with == can be troublesome
// but here they really should be equal in every bit.
}
}
I wrote it to check if 8099.99975 is rounded to 8100 when written as an IEEE 754 single precision float. To my surprise Java converts it to 8099.9995 when written as a float literal (8099.99975f). I checked my calculations and the IEEE standard again but couldn't find any mistakes. 8100 is just as far away from 8099.99975 as 8099.9995 but the last bit of 8100 is 0 which should make it the right representation.
So I checked the Java language spec to see if I missed something. After a quick search I found two things:
The Java programming language requires that floating-point arithmetic behave as if every floating-point operator rounded its floating-point result to the result precision. Inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, the one with its least significant bit zero is chosen.
The Java programming language uses round toward zero when converting a floating value to an integer [...].
I noticed here that nothing was said about float literals. So I thought that float literals maybe are just doubles which when cast to float are rounded to zero similarly to the float to int casting. That would explain why 8099.99975f was rounded to zero.
I wrote the little program you can see above to check my theory and indeed found that when adding two float literals that should result in 8100 the correct float is computed. (Note here that 8099.9995 and 0.00025 can be represented exactly as single floats so there's no rounding that could lead to a different result) This confused me since it didn't make much sense to me that float literals and computed floats behaved differently so I dug around in the language spec some more and found this:
A floating-point literal is of type float if it is suffixed with an ASCII letter F or f [...]. The elements of the types float [...] are those values that can be represented using the IEEE 754 32-bit single-precision [...] binary floating-point formats.
This ultimately states that the literal should be rounded according to the IEEE standard which in this case is to 8100. So why is it 8099.9995?
The key point to realise is that the value of a floating point number can be worked out in two different ways, that aren't in general equal.
There's the value that the bits in the floating point number give the exact binary representation of.
There's the "decimal display value" of a floating point number, which is the number with the least decimal places that is closer to that floating point number than any other number.
To understand the difference, consider the number whose exponent is 10001011 and whose significand is 1.11111010001111111111111. This is the exact binary representation of 8099.99951171875. But the decimal value 8099.9995 has fewer decimal places, and is closer to this floating point number than to any other floating point number. Therefore, 8099.9995 is the value that will be displayed when you print out that number.
Note that this particular floating point number is the next lowest one after 8100.
Now consider 8099.99975. It's slightly closer to 8099.99951171875 than it is to 8100. Therefore, to represent it in single precision floating point, Java will pick the floating point number which is the exact binary representation of 8099.99951171875. If you try to print it, you'll see 8099.9995.
Lastly, when you do 8099.9995 + 0.00025 in single precision floating point, the numbers involved are the exact binary representations of 8099.99951171875 and 0.0002499999827705323696136474609375. But because the latter is slightly more than 1/2^12, the result of addition will be closer to 8100 than to 8099.99951171875, and so it will be rounded up, not down at the end, making it 8100.
The decimal value 8099.99975 has nine significant digits. This is more than can be represented exactly in a float. If you use the floating point analysis tool at CUNY you'll see that the binary representation closest to 8099.9995 is 45FD1FFF. When you attempt to add 0.00025 you are suffering a "loss of significance". In order not to lose significant (left-hand) digits of the larger number, the significand of the smaller has to be shifted right to match the scale (exponent) of the larger. When this happens, its value becomes ZERO as it shifts off the right end of the register.
Decimal Exponent Significand
--------- -------------- -------------------------
8099.9995 10001011 (+12) 1.11111010001111111111111
0.00025 01110011 (-12) 1.00000110001001001101111
To line these up for addition, the second one has to shift right 24 bits, but there are only 23 bits in the significand of a single-precision float. The significand disappears, leaving zero, so the addition has no effect.
If you want this to work, switch to double-precision arithmetic.

Double parseDouble adding extra 0's when adding the numbers

I am running weird situation. I am counting the numbers using Double parseDouble. In some situation i am getting extra 0's for instance instead of 109.1 i am getting ... 109.10000001.
I am trying to add string from json.
Here is the line of code I am executing.
points = points + Double.parseDouble(x.points);
Java uses IEEE floating point numbers for its double (and float) datatypes. A double has a lot of precision, but not infinite precision. Some numbers are only approximated.
In addition (no pun intended), adding numbers like this together compounds the floating point errors. Eventually the error is large enough to notice, such as with your issue here.
If you can accept a performance hit, then use BigDecimal, which is arbitrary precision.
It is probably unrelated to the Double.parseDouble, but rather a floating-point precision issue. Floats and doubles are encoded in binary as a mantissa and an exponent. The part after the decimal point is a particular problem because what can be represented easily in base 10 cannot necessarily be represented well in base 2. For example, the decimal number 0.2 becomes infinitely repeating in binary, rather like 1/3 in decimal (0.333...). I suggest rounding to a fixed number of digits for display, or perhaps using java.math.BigDecimal instead.
This actually has a very complicated answer, but essentially floating point numbers are often only approximations of the number you give it.
A good example from wikipedia is 0.1, which in floating point is 0.009999999776482582092285156250.

Assign to Float from string with precision

I have the following code, I want to assign a decimal value to float without losing precision.
String s1= "525.880005";
Float f = new Float(s1);
System.out.println(f);
Output:
5.88
Expected Output:
525.880005
Float only has 7-8 significant digits of precision. The "5" in your example is the 9th digit.
Even if it had enough precision, I don't know whether 525.880005 is exactly representable as a binary floating point number. Most decimal values aren't :)
You should use BigDecimal if the exact decimal representation is important to you.
There's a real contradiction implied in the question :
Assign to float <--> wiht precision
525.880005 is jus the number in this float-domain that is closest to 525.88.
The reason that floating numbers cannot be mapped to all numbers is because of the mismatch between the decimal and the binary system for fractions.
Other types, such as decimal and money, use other, much more memory consuming, techniques to store the number (for example , in a string you can store any number, but of course this is not the most performant way to do math)
a simple example : 0.3 in my own binary system :
0.1b (inary) would be 0.5 d (ecimal) so too much...
0.01b --> 0.25d (1/4 too little)
0.011 --> 0.375 (1/4 + 1/8 too much)
0.0101 --> 0.3125 (1/4 + 1/16 still too much)
...
0.010011 --> 1/4 +1/32 + 1/64 = 0.296875
Suppose my system has 6 bits to represent the fraction, 0.296875 would be the closest for this domain. The right number cannot be reached due to the decimal/binary system.
For examples see also :
Floating point inaccuracy examples
And an excellent elaborate explenation of your problems is to be found here:
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
Another note : it is really about mismatch, not about 'quality' of systems : in decimal notation for example, you cannot represent 1/3 100% accurate, while this would be perfectly possible in other systems.
The float type cannot hold every possible value (nor can double). If you're assigning from a string, you may prefer BigDecimal as BigDecimal can precisely hold anything that you can reasonably represent with a string.
Note that BigDecimal also cannot hold every possible value (it can't precisely represent 1/3rd, for instance, for the same reason we can't write 1/3rd precisely in our decimal notation system — it would never end). But again, if your source is a string value, BigDecimal will more closely align with your possible values than will float or double. Of course, there's a cost. float and double are designed to be very fast in computation; BigDecimal is designed to be very precise with decimal values, at the expense of speed.
Float doesn't have enough significant digits to represent your number. Try Double.

Why does this subtraction not equal zero?

I happened upon these values in my ColdFusion code but the Google calculator seems to have the same "bug" where the difference is non-zero.
416582.2850 - 411476.8100 - 5105.475 = -2.36468622461E-011
http://www.google.com/search?hl=en&rlz=1C1GGLS_enUS340US340&q=416582.2850+-+411476.8100+-+5105.475&aq=f&oq=&aqi=
JavaCast'ing these to long/float/double doesn't help- it results in other non-zero differences.
This is because decimal numbers that "look" round in base 10, are not exactly representable in base 2 (which is what computers use to represent floating point numbers). Please see the article What Every Computer Scientist Should Know About Floating-Point Arithmetic for a detailed explanation of this problem and workarounds.
Floating-point inaccuracies (there are an infinite number of real numbers and only a finite number of 32- or 64-bit numbers to represent them with).
If you can't handle tiny errors, you should use BigDecimal instead.
Use PrecisionEvaluate() in ColdFusion (it'll use BigDecimal in Java)
zero = PrecisionEvaluate(416582.2850 - 411476.8100 - 5105.475);
unlike Evaulate(), no "" is needed.
Since computer stores numbers in binary, float numbers are imprecise. 1E-11 is a tiny difference due to rounding these decimal numbers to the nearest representable binary number.
This "bug" is not a bug. It's how floating point arithmetic works. See: http://docs.sun.com/source/806-3568/ncg_goldberg.html
If you want arbitrary precision in Java, use BigDecimal:
BigDecimal a = new BigDecimal("416582.2850");
BigDecimal b = new BigDecimal("411476.8100");
BigDecimal c = new BigDecimal("5105.475");
System.out.println(a.subtract(b).subtract(c)); // 0.0
The problem is the inexact representation of floating point types. Because these can't be exactly represented as floats, you get some precision loss that results in operations have small errors. Typically with floats you want to compare whether the result is equal to another value within some small epislon (error factor).
These are floating point issues and using BigDecimal will fix it.
Changing the order of subtraction also yields zero in Google.
416582.2850 - 5105.475 - 411476.8100 = 0

Categories

Resources