Logically questionable? - java

double x = 1;
double y = 3 * (1.0 / 3);
x == y
In a powerpoint I am studying, it said the statement is logically questionable. I cannot find out why it is thus, I mean you use == for primitives correct, or is it Logically questionable because doubles are not stored exactly or am I missing something obvious? Thanks

I think you've got it: since the data types are doubles, rather than int or Integer, the resulting x and y may not be precisely equal.

It is logically questionable because the compare statement at the end would evaluate to false. Doubles are stored as a series of powers of two. So values like 1/2 and 1/4 and 1/8 could actually be expressed in floating point formats exactly, but not 1/3. It will be approximated to 1/4 + 1/64 + ... there is now way it could exactly be apprroximate 1/3
Correct way to compare the floats is like this:
Math.double.abs ( x - y ) > tol
where tol is set to something sufficiently small, depending on your application. For example most graphics applications work well with tol = 0.00001

Because 1.0 / 3 is 0.3333..., up to the capacity of a double. 3 * 0.3333... is 0.9999..., up to the capacity of a double.
So we have the question 1 == 0.9999..., which I guess you could call "logically questionable".

It's because of roundoff error. The problem is analogous to the precision problems you have have with decimals when dealing with numbers that cannot be precisely expressed in the format you are using.
For example, with six digits of decimal precision, the best you can do for 1/3 is .333333. But:
1/3 + 1/3 + 1/3 -> .33333 + .333333 + .33333 = .999999 != 1.000000
Ouch. For 2/3, you can use either .666666 or .666667 but either way, you have problems.
If 2/3 -> .666666 then:
2/3 + 1/3 -> .333333 + .666666 != 1.000000
Ouch.
And if 2/3. -> .666667 then:
1/3 * 2 - 2/3 -> .333333 * 2.00000 - .666667 = .666666 - .666667 != 0
Ouch.
It's analogous with doubles. This paper is considered the authoritative work on the subject. In simple terms -- never compare floating point numbers for equality unless you know exactly what you're doing.

Logically questionable because doubles are not stored exactly
More or less.
As a general rule, a double cannot represent the precise value of a Real number. The value 1/3 is an example.
Some numbers can be represented precisely as double values. 1.0 and 3.0 are examples. However, the division operator produces a number (in this case) that cannot be represented.
In general, any code that uses == to compare double or float values is questionable ... in the sense that you need to analyse each case carefully to know whether the use of == is correct. (And the analysis is not intuitive for people who were taught at a very early age to do arithmetic in base 10!)
From the software engineering standpoint, the fact that you need to do a case-by-case analysis of == usage makes it questionable practice. Good software engineering is (in part) about eliminating mistakes and sources of mistakes.

Related

Comparison of floating point numbers in Java

Lets say, I have the following:
float x= ...
float y = ...
What I like to do is just compare them whether x is greater than y or not. I am not interested in their equality.
My question is, should I take into account precision when just performing a > or a < check on floating point values? Am I correct to assume that precision is only taken into account for equality checks?
Since you already have two floats, named x and y, and if there hasn't been any casting before, you can easily use ">" and "<" for comparison. However, let's say if you had two doubles d1 and d2 with d1 > d2 and you cast them to f1 and f2, respectively, you might get f1 == f2 because of precision problems.
There is already a wheel you don't need to invent:
if (Float.compare(x, y) < 0)
// x is less than y
All float values have the same precision as each other.
It really depends on where those two floats came from. If there was roundoff earlier in their computation, then if the accumulated roundoff is large enough to exceed the difference between the "ideal" answers, you may not get the results you expect for any of the comparison values.
As a general rule, any comparison of floats must be considered fuzzy. If you must do it, you are responsible for understanding the sources of roundoff error and deciding whether you care about it and how you want to handle it if so. It's usually better to avoid comparing floats entirely unless your algorithm absolutely requires that you do so... and if you must, to make sure that a "close but not quite" comparison will not break your program. You may be able to restructure the formulas and order of computation to reduce loss of precision. Going up to double will reduce the accumulated error but is not a complete solution.
If you must compare, and the comparison is important, don't use floats. If you need absolute precision of floating-like numbers, use an infinite-precision math package like bignums or a rational-numbers implementation and accept the performance cost. Or switch to scaled integers -- which also round off, but round off in a way that makes more sense to humans.
This is a difficult question. The answer really depends on how you got those numbers.
First, you need to understand that floating point numbers ARE precise, but that they don't necessarily represent the number that you thought they did. Floating point types in typical programming language represent a finite subset of the infinite set of Real numbers. So if you have an arbitrary real number the chances are that you cannot represent it precisely using a float or double type. In fact ...
The only Real numbers that can be represented exactly as float or
double values have the form
mantissa * power(2, exponent)
where "mantissa" and "exponent" are integers in prescribed ranges.
And the corollary is that most "decimal" numbers don't have an exact float or double representation either.
So in fact, we end up with something like this:
true_value = floating_point_value + delta,
where "delta" is the error; i.e. the small (or not so small) difference between the true value and the float or double value.
Next, when you perform a calculation using floating point values, there are cases where the exact result cannot be represented as a floating point value. An obvious example is:
1.0f / 3.0f
for which the true value is 0.33333... recurring, which is not representable in any finite base 2 (or base 10!) floating point representation. Instead what happens is that a result is produced by rounding to the nearest float or double value ... introducing more error.
As you perform more and more calculations, the errors can potentially grow ... or stay stable ... depending on the sequence of operations that are performed. (There's a branch of mathematics that deals with this: Numerical Analysis.)
So back to your questions:
"Should I take into account precision when just performing a > or a < check on floating point values?"
It depends on how you got those floating point values, and what you know about the "delta" values relative to the true Real values they nominally represent.
If there are no errors (i.e. delta < the smallest representable difference for the value), then you can safely compare using ==, < or >.
If there are possible errors, then you need to take account of those errors ... if the semantic of the comparison are intended to couched in terms of the (nominal) true values. Furthermore, you need to have a credible estimate of the "delta" (the accumulated error) when you code the comparison.
In short, there is no simple (correct) answer ...
"Am I correct to assume that precision is only taken into account for equality checks? "
In fact precision is not "taken into account" in any of the comparison operators. These operators treat the operands as precise values, and compare them accordingly. It is up to your code to take account of precision issues, based on your error estimates for the preceding calculations.
If you have estimates for the deltas, then a mathematically sound < comparison would be something like this:
// Assume true_v1 = v1 +- delta_v1 ... (delta_v1 is a non-negative constant)
if (v1 + delta_v1 < v2 - delta_v2) {
// true_v1 is less than true_v2
}
and so on ...

Why is comparing floats inconsistent in Java?

class Test{
public static void main(String[] args){
float f1=3.2f;
float f2=6.5f;
if(f1==3.2){
System.out.println("same");
}else{
System.out.println("different");
}
if(f2==6.5){
System.out.println("same");
}else{
System.out.println("different");
}
}
}
output:
different
same
Why is the output like that? I expected same as the result in first case.
The difference is that 6.5 can be represented exactly in both float and double, whereas 3.2 can't be represented exactly in either type. and the two closest approximations are different.
An equality comparison between float and double first converts the float to a double and then compares the two. So the data loss.
You shouldn't ever compare floats or doubles for equality; because you can't really guarantee that the number you assign to the float or double is exact.
This rounding error is a characteristic feature of floating-point computation.
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.
Check What Every Computer Scientist Should Know About Floating-Point Arithmetic for more!
They're both implementations of different parts of the IEEE floating point standard. A float is 4 bytes wide, whereas a double is 8 bytes wide.
As a rule of thumb, you should probably prefer to use double in most cases, and only use float when you have a good reason to. (An example of a good reason to use float as opposed to a double is "I know I don't need that much precision and I need to store a million of them in memory.") It's also worth mentioning that it's hard to prove you don't need double precision.
Also, when comparing floating point values for equality, you'll typically want to use something like Math.abs(a-b) < EPSILON where a and b are the floating point values being compared and EPSILON is a small floating point value like 1e-5. The reason for this is that floating point values rarely encode the exact value they "should" -- rather, they usually encode a value very close -- so you have to "squint" when you determine if two values are the same.
EDIT: Everyone should read the link #Kugathasan Abimaran posted below: What Every Computer Scientist Should Know About Floating-Point Arithmetic for more!
To see what you're dealing with, you can use Float and Double's toHexString method:
class Test {
public static void main(String[] args) {
System.out.println("3.2F is: "+Float.toHexString(3.2F));
System.out.println("3.2 is: "+Double.toHexString(3.2));
System.out.println("6.5F is: "+Float.toHexString(6.5F));
System.out.println("6.5 is: "+Double.toHexString(6.5));
}
}
$ java Test
3.2F is: 0x1.99999ap1
3.2 is: 0x1.999999999999ap1
6.5F is: 0x1.ap2
6.5 is: 0x1.ap2
Generally, a number has an exact representation if it equals A * 2^B, where A and B are integers whose allowed values are set by the language specification (and double has more allowed values).
In this case,
6.5 = 13/2 = (1+10/16)*4 = (1+a/16)*2^2 == 0x1.ap2, while
3.2 = 16/5 = ( 1 + 9/16 + 9/16^2 + 9/16^3 + . . . ) * 2^1 == 0x1.999. . . p1.
But Java can only hold a finite number of digits, so it cuts the .999. . . off at some point. (You may remember from math that 0.999. . .=1. That's in base 10. In base 16, it would be 0.fff. . .=1.)
class Test {
public static void main(String[] args) {
float f1=3.2f;
float f2=6.5f;
if(f1==3.2f)
System.out.println("same");
else
System.out.println("different");
if(f2==6.5f)
System.out.println("same");
else
System.out.println("different");
}
}
Try like this and it will work. Without 'f' you are comparing a floating with other floating type and different precision which may cause unexpected result as in your case.
It is not possible to compare values of type float and double directly. Before the values can be compared, it is necessary to either convert the double to float, or convert the float to double. If one does the former comparison, the conversion will ask "Does the the float hold the best possible float representation of the double's value?" If one does the latter conversion, the question will be "Does the float hold a perfect representation of the double's value". In many contexts, the former question is the more meaningful one, but Java assumes that all comparisons between float and double are intended to ask the latter question.
I would suggest that regardless of what a language is willing to tolerate, one's coding standards should absolutely positively forbid direct comparisons between operands of type float and double. Given code like:
float f = function1();
double d = function2();
...
if (d==f) ...
it's impossible to tell what behavior is intended in cases where d represents a value which is not precisely representable in float. If the intention is that f be converted to a double, and the result of that conversion compared with d, one should write the comparison as
if (d==(double)f) ...
Although the typecast doesn't change the code's behavior, it makes clear that the code's behavior is intentional. If the intention was that the comparison indicate whether f holds the best float representation of d, it should be:
if ((float)d==f)
Note that the behavior of this is very different from what would happen without the cast. Had your original code cast the double operand of each comparison to float, then both equality tests would have passed.
In general is not a good practice to use the == operator with floating points number, due to approximation issues.
6.5 can be represented exactly in binary, whereas 3.2 can't. That's why the difference in precision doesn't matter for 6.5, so 6.5 == 6.5f.
To quickly refresh how binary numbers work:
100 -> 4
10 -> 2
1 -> 1
0.1 -> 0.5 (or 1/2)
0.01 -> 0.25 (or 1/4)
etc.
6.5 in binary: 110.1 (exact result, the rest of the digits are just zeroes)
3.2 in binary: 11.001100110011001100110011001100110011001100110011001101... (here precision matters!)
A float only has 24 bits precision (the rest is used for sign and exponent), so:
3.2f in binary: 11.0011001100110011001100 (not equal to the double precision approximation)
Basically it's the same as when you're writing 1/5 and 1/7 in decimal numbers:
1/5 = 0,2
1,7 = 0,14285714285714285714285714285714.
Float has less precision than double, bcoz float is using 32bits inwhich 1 is used for Sign, 23 precision and 8 for Exponent . Where as double uses 64 bits in which 52 are used for precision, 11 for exponent and 1for Sign....Precision is important matter.A decimal number represented as float and double can be equal or unequal depends is need of precision( i.e range of numbers after decimal point can vary). Regards S. ZAKIR

Weird Java fraction behavior

I have seen a very weird behaviour in Java's double variable, as I'm trying to simply add small fractions to a double and I see a completely bizarre results.
double test = 0;
test += 0.71;
test += 0.2;
Now I'd expect the result to be:
test = 0.91
Right? Wrong!
In reality, this is the number I get in my test double:
test = 0.9099999999999999
Now while this is very close, it's a very bizarre fraction loss, and in the long run it causes serious bugs in my program.
With a float I've gotten even a weirder result.
Any help would be greatly appreciated.
Thanks
There is nothing bizarre about it at all. 0.91, 0.71 and 0.2 are not representable as a IEEE754 floating point values as they would have a recurring fractional part when represented in binary. The situation is entirely analogous to trying to represent 1/3 in base 10 with a finite number of digits. You can't do it.
What you are seeing is a rounding error that is normal when doing floating point calculations. You have to code around it. So for instance, you can't reliably compare for equality, you have to see the two numbers are within some small delta of each other. For a slightly more in depth but still understandable explanation see The Floating Point Guide.
That's the magic of binary encoding of floating point values (look for IEEE754 : http://en.wikipedia.org/wiki/IEEE_754-2008 ). If you want to be sure to never have this kind of things, you're maybe looking for BigDecimal :
http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html
Basic rules :
don't use equality tests when dealing with floating point numbers (you must test gaps)
round numbers you're displaying (usually using DecimalFormat)
don't use floating point numbers for financial applications
the float is generally the way to go for scientific or industrial operations, as long as you understand IEEE754
double can only approximate most fractional values. This means you need to use some rounding if you want to get your expect result. Or you can use BigDecimal which takes care of this issue for you.
double test = 0;
test += 0.71;
test += 0.2;
System.out.printf("%.2f%n", test);
prints
0.91
For your own interest
System.out.println("0.71 is actually " + new BigDecimal(0.71));
System.out.println("0.2 is actually " + new BigDecimal(0.2));
System.out.println("0.71+0.2 is actually " + new BigDecimal(0.71 + 0.2));
System.out.println("0.91 is actually " + new BigDecimal(0.91));
System.out.println("0.71+0.2 == 0.91 is " + (0.71 + 0.2 == 0.91));
prints
0.71 is actually 0.70999999999999996447286321199499070644378662109375
0.2 is actually 0.200000000000000011102230246251565404236316680908203125
0.71+0.2 is actually 0.9099999999999999200639422269887290894985198974609375
0.91 is actually 0.91000000000000003108624468950438313186168670654296875
0.71+0.2 == 0.91 is false
Java uses something called floating-point to represent decimals. They use exponential notation. Here's what I mean:
There is a multiplier (M), and an exponent between 1023 and -1022 (E).
A number (N) is represented like this: M * 2^E.
4.25 is represented like this:
17 * 2^-2.
0.91 cannot be represented in base 2 exactly, but Java can get pretty close:
0.909999999999..
Therefore, it is impossible to accurately add these numbers together.

Java:Why should we use BigDecimal instead of Double in the real world? [duplicate]

This question already has answers here:
Double vs. BigDecimal?
(7 answers)
Closed 7 years ago.
When dealing with real world monetary values, I am advised to use BigDecimal instead of Double.But I have not got a convincing explanation except, "It is normally done that way".
Can you please throw light on this question?
I think this describes solution to your problem: Java Traps: Big Decimal and the problem with double here
From the original blog which appears to be down now.
Java Traps: double
Many traps lay before the apprentice programmer as he walks the path of software development. This article illustrates, through a series of practical examples, the main traps of using Java's simple types double and float. Note, however, that to fully embrace precision in numerical calculations you a text book (or two) on the topic is required. Consequently, we can only scratch the surface of the topic. That being said, the knowledge conveyed here, should give you the fundamental knowledge required to spot or identify bugs in your code. It is knowledge I think any professional software developer should be aware of.
Decimal numbers are approximations
While all natural numbers between 0 - 255 can be precisely described using 8 bit, describing all real numbers between 0.0 - 255.0 requires an infinitely number of bits. Firstly, there exists infinitely many numbers to describe in that range (even in the range 0.0 - 0.1), and secondly, certain irrational numbers cannot be described numerically at all. For example e and π. In other words, the numbers 2 and 0.2 are vastly differently represented in the computer.
Integers are represented by bits representing values 2n where n is the position of the bit. Thus the value 6 is represented as 23 * 0 + 22 * 1 + 21 * 1 + 20 * 0 corresponding to the bit sequence 0110. Decimals, on the other hand, are described by bits representing 2-n, that is the fractions 1/2, 1/4, 1/8,... The number 0.75 corresponds to 2-1 * 1 + 2-2 * 1 + 2-3 * 0 + 2-4 * 0 yielding the bits sequence 1100 (1/2 + 1/4).
Equipped with this knowledge, we can formulate the following rule of thumb: Any decimal number is represented by an approximated value.
Let us investigate the practical consequences of this by performing a series of trivial multiplications.
System.out.println( 0.2 + 0.2 + 0.2 + 0.2 + 0.2 );
1.0
1.0 is printed. While this is indeed correct, it may give us a false sense of security. Coincidentally, 0.2 is one of the few values Java is able to represent correctly. Let's challenge Java again with another trivial arithmetical problem, adding the number 0.1 ten times.
System.out.println( 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f );
System.out.println( 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d );
1.0000001
0.9999999999999999
According to slides from Joseph D. Darcy's blog the sums of the two calculations are 0.100000001490116119384765625 and 0.1000000000000000055511151231... respectively. These results are correct for a limited set of digits. float's have a precision of 8 leading digits, while double has 17 leading digits precision. Now, if the conceptual mismatch between the expected result 1.0 and the results printed on the screens were not enough to get your alarm bells going, then notice how the numbers from mr. Darcy's slides does not seem to correspond to the printed numbers! That's another trap. More on this further down.
Having been made aware of mis-calculations in seemingly the simples possible scenarios, it is reasonable to contemplate on just how quickly the impression may kick in. Let us simplify the problem to adding only three numbers.
System.out.println( 0.3 == 0.1d + 0.1d + 0.1d );
false
Shockingly, the imprecision already kicks in at three additions!
Doubles overflow
As with any other simple type in Java, a double is represented by a finite set of bits. Consequently, adding a value or multiplying a double can yield surprising results. Admitedly, numbers have to be pretty big in order to overflow, but it happens. Let's try multiplying and then dividing a big number. Mathematical intuition says that the result is the original number. In Java we may get a different result.
double big = 1.0e307 * 2000 / 2000;
System.out.println( big == 1.0e307 );
false
The problem here is that big is first multiplied, overflowing, and then the overflowed number is divided. Worse, no exception or other kinds of warnings are raised to the programmer. Basically, this renders the expression x * y completely unreliable as no indication or guarantee is made in the general case for all double values represented by x, y.
Large and small are not friends!
Laurel and Hardy were often disagreeing about a lot of things. Similarly in computing, large and small are not friends. A consequence of using a fixed number of bits to represent numbers is that operating on really large and really small numbers in the same calculations will not work as expected. Let's try adding something small to something large.
System.out.println( 1234.0d + 1.0e-13d == 1234.0d );
true
The addition has no effect! This contradicts any (sane) mathematical intuition of addition, which says that given two numbers positive numbers d and f, then d + f > d.
Decimal numbers cannot be directly compared
What we have learned so far, is that we must throw away all intuition we have gained in math class and programming with integers. Use decimal numbers cautiously. For example, the statement for(double d = 0.1; d != 0.3; d += 0.1) is in effect a disguised never ending loop! The mistake is to compare decimal numbers directly with each other. You should adhere to the following guide lines.
Avoid equality tests between two decimal numbers. Refrain from if(a == b) {..}, use if(Math.abs(a-b) < tolerance) {..} where tolerance could be a constant defined as e.g. public static final double tolerance = 0.01
Consider as an alternative to use the operators <, > as they may more naturally describe what you want to express. For example, I prefer the form
for(double d = 0; d <= 10.0; d+= 0.1) over the more clumsy
for(double d = 0; Math.abs(10.0-d) < tolerance; d+= 0.1)
Both forms have their merits depending on the situation though: When unit testing, I prefer to express that assertEquals(2.5, d, tolerance) over saying assertTrue(d > 2.5) not only does the first form read better, it is often the check you want to be doing (i.e. that d is not too large).
WYSINWYG - What You See Is Not What You Get
WYSIWYG is an expression typically used in graphical user interface applications. It means, "What You See Is What You Get", and is used in computing to describe a system in which content displayed during editing appears very similar to the final output, which might be a printed document, a web page, etc. The phrase was originally a popular catch phrase originated by Flip Wilson's drag persona "Geraldine", who would often say "What you see is what you get" to excuse her quirky behavior (from wikipedia).
Another serious trap programmers often fall into, is thinking that decimal numbers are WYSIWYG. It is imperative to realize, that when printing or writing a decimal number, it is not the approximated value that gets printed/written. Phrased differently, Java is doing a lot of approximations behind the scenes, and persistently tries to shield you from ever knowing it. There is just one problem. You need to know about these approximations, otherwise you may face all sorts of mysterious bugs in your code.
With a bit of ingenuity, however, we can investigate what really goes on behind the scene. By now we know that the number 0.1 is represented with some approximation.
System.out.println( 0.1d );
0.1
We know 0.1 is not 0.1, yet 0.1 is printed on the screen. Conclusion: Java is WYSINWYG!
For the sake of variety, let's pick another innocent looking number, say 2.3. Like 0.1, 2.3 is an approximated value. Unsurprisingly when printing the number Java hides the approximation.
System.out.println( 2.3d );
2.3
To investigate what the internal approximated value of 2.3 may be, we can compare the number to other numbers in a close range.
double d1 = 2.2999999999999996d;
double d2 = 2.2999999999999997d;
System.out.println( d1 + " " + (2.3d == d1) );
System.out.println( d2 + " " + (2.3d == d2) );
2.2999999999999994 false
2.3 true
So 2.2999999999999997 is just as much 2.3 as the value 2.3! Also notice that due to the approximation, the pivoting point is at ..99997 and not ..99995 where you ordinarily round round up in math. Another way to get to grips with the approximated value is to call upon the services of BigDecimal.
System.out.println( new BigDecimal(2.3d) );
2.29999999999999982236431605997495353221893310546875
Now, don't rest on your laurels thinking you can just jump ship and only use BigDecimal. BigDecimal has its own collection of traps documented here.
Nothing is easy, and rarely anything comes for free. And "naturally", floats and doubles yield different results when printed/written.
System.out.println( Float.toString(0.1f) );
System.out.println( Double.toString(0.1f) );
System.out.println( Double.toString(0.1d) );
0.1
0.10000000149011612
0.1
According to the slides from Joseph D. Darcy's blog a float approximation has 24 significant bits while a double approximation has 53 significant bits. The morale is that In order to preserve values, you must read and write decimal numbers in the same format.
Division by 0
Many developers know from experience that dividing a number by zero yields abrupt termination of their applications. A similar behaviour is found is Java when operating on int's, but quite surprisingly, not when operating on double's. Any number, with the exception of zero, divided by zero yields respectively ∞ or -∞. Dividing zero with zero results in the special NaN, the Not a Number value.
System.out.println(22.0 / 0.0);
System.out.println(-13.0 / 0.0);
System.out.println(0.0 / 0.0);
Infinity
-Infinity
NaN
Dividing a positive number with a negative number yields a negative result, while dividing a negative number with a negative number yields a positive result. Since division by zero is possible, you'll get different result depending on whether you divide a number with 0.0 or -0.0. Yes, it's true! Java has a negative zero! Don't be fooled though, the two zero values are equal as shown below.
System.out.println(22.0 / 0.0);
System.out.println(22.0 / -0.0);
System.out.println(0.0 == -0.0);
Infinity
-Infinity
true
Infinity is weird
In the world of mathematics, infinity was a concept I found hard to grasp. For example, I never acquired an intuition for when one infinity were infinitely larger than another. Surely Z > N, the set of all rational numbers is infinitely larger than the set of natural numbers, but that was about the limit of my intuition in this regard!
Fortunately, infinity in Java is about as unpredictable as infinity in the mathematical world. You can perform the usual suspects (+, -, *, / on an infinite value, but you cannot apply an infinity to an infinity.
double infinity = 1.0 / 0.0;
System.out.println(infinity + 1);
System.out.println(infinity / 1e300);
System.out.println(infinity / infinity);
System.out.println(infinity - infinity);
Infinity
Infinity
NaN
NaN
The main problem here is that the NaN value is returned without any warnings. Hence, should you foolishly investigate whether a particular double is even or odd, you can really get into a hairy situation. Maybe a run-time exception would have been more appropriate?
double d = 2.0, d2 = d - 2.0;
System.out.println("even: " + (d % 2 == 0) + " odd: " + (d % 2 == 1));
d = d / d2;
System.out.println("even: " + (d % 2 == 0) + " odd: " + (d % 2 == 1));
even: true odd: false
even: false odd: false
Suddenly, your variable is neither odd nor even!
NaN is even weirder than Infinity
An infinite value is different from the maximum value of a double and NaN is different again from the infinite value.
double nan = 0.0 / 0.0, infinity = 1.0 / 0.0;
System.out.println( Double.MAX_VALUE != infinity );
System.out.println( Double.MAX_VALUE != nan );
System.out.println( infinity != nan );
true
true
true
Generally, when a double have acquired the value NaN any operation on it results in a NaN.
System.out.println( nan + 1.0 );
NaN
Conclusions
Decimal numbers are approximations, not the value you assign. Any intuition gained in math-world no longer applies. Expect a+b = a and a != a/3 + a/3 + a/3
Avoid using the ==, compare against some tolerance or use the >= or <= operators
Java is WYSINWYG! Never believe the value you print/write is approximated value, hence always read/write decimal numbers in the same format.
Be careful not to overflow your double, not to get your double into a state of ±Infinity or NaN. In either case, your calculations may be not turn out as you'd expect. You may find it a good idea to always check against those values before returning a value in your methods.
It's called loss of precision and is very noticeable when working with either very big numbers or very small numbers. The binary representation of decimal numbers with a radix is in many cases an approximation and not an absolute value. To understand why you need to read up on floating number representation in binary. Here is a link: http://en.wikipedia.org/wiki/IEEE_754-2008. Here is a quick demonstration:
in bc (An arbitrary precision calculator language) with precision=10:
(1/3+1/12+1/8+1/15) = 0.6083333332
(1/3+1/12+1/8) = 0.541666666666666
(1/3+1/12) = 0.416666666666666
Java double:
0.6083333333333333
0.5416666666666666
0.41666666666666663
Java float:
0.60833335
0.5416667
0.4166667
If you are a bank and are responsible for thousands of transactions every day, even though they are not to and from one and same account (or maybe they are) you have to have reliable numbers. Binary floats are not reliable - not unless you understand how they work and their limitations.
While BigDecimal can store more precision than double, this is usually not required. The real reason it used because it makes it clear how rounding is performed, including a number of different rounding strategies. You can achieve the same results with double in most cases, but unless you know the techniques required, BigDecimal is the way to go in these case.
A common example, is money. Even though money is not going to be large enough to need the precision of BigDecimal in 99% of use cases, it is often considered best practice to use BigDecimal because the control of rounding is in the software which avoids the risk that the developer will make a mistake in handling rounding. Even if you are confident you can handle rounding with double I suggest you use helper methods to perform the rounding which you test thoroughly.
This is primarily done for reasons of precision. BigDecimal stores floating point numbers with unlimited precision. You can take a look at this page that explains it well. http://blogs.oracle.com/CoreJavaTechTips/entry/the_need_for_bigdecimal
When BigDecimal is used, it can store a lot more data then Double, which makes it more accurate, and just an all around better choice for the real world.
Although it is a lot slower and longer, it's worth it.
Bet you wouldn't want to give your boss inaccurate info, huh?
Another idea: keep track of the number of cents in a long. This is simpler, and avoids the cumbersome syntax and slow performance of BigDecimal.
Precision in financial calculations is extra important because people get very irate when their money disappears due to rounding errors, which is why double is a terrible choice for dealing with money.

Rounding Errors?

In my course, I am told:
Continuous values are represented approximately in memory, and therefore computing with floats involves rounding errors. These are tiny discrepancies in bit patterns; thus the test e==f is unsafe if e and f are floats.
Referring to Java.
Is this true? I've used comparison statements with doubles and floats and have never had rounding issues. Never have I read in a textbook something similar. Surely the virtual machine accounts for this?
It is true.
It is an inherent limitation of how floating point values are represented in memory in a finite number of bits.
This program, for instance, prints "false":
public class Main {
public static void main(String[] args) {
double a = 0.7;
double b = 0.9;
double x = a + 0.1;
double y = b - 0.1;
System.out.println(x == y);
}
}
Instead of exact comparison with '==' you usually decide on some level of precision and ask if the numbers are "close enough":
System.out.println(Math.abs(x - y) < 0.0001);
This applies to Java just as much as to any other language using floating point. It's inherent in the design of the representation of floating point values in hardware.
More info on floating point values:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Yes, representing 0.1 exactly in base-2 is the same as trying to represent 1/3 exactly in base 10.
This is always true. There are some numbers which cannot be represented accurately using float point representation. Consider, for example, pi. How would you represent a number which has infinite digits, within a finite storage? Therefore, when comparing numbers you should check if the difference between them is smaller then some epsilon. Also, there are several classes which exist that can help you achieve greater accuracy such as BigDecimal and BigInteger.
It is right. Note that Java has nothing to do with it, the problem is inherent in floating point math in ANY language.
You can often get away with it with classroom-level problems but it's not going to work in the real world. Sometimes it won't work in the classroom.
An incident from long ago back in school. The teacher of an intro class assigned a final exam problem that was proving a real doozy for many of the better students--it wasn't working and they didn't know why. (I saw this as a lab assistant, I wasn't in the class.) Finally some started asking me for help and some probing revealed the problem: They had never been taught about the inherent inaccuracy of floating point math.
Now, there were two basic approaches to this problem, a brute force one (which by chance worked in this case as it made the same errors every time) and a more elegant one (which would make different errors and not work.) Anyone who tried the elegant approach would hit a brick wall without having any idea why. I helped a bunch of them and stuck in a comment explaining why and to contact me if he had questions.
Of course next semester I hear from him about this and I basically floored the entire department with a simple little program:
10 X = 3000000
20 X = X + 1
30 If X < X + 1 goto 20
40 Print "X = X + 1"
Despite what every teacher in the department thought, this WILL terminate. The 3 million seed is simply to make it terminate faster. (If you don't know basic: There are no gimmicks here, just exhausting the precision of floating point numbers.)
Yes, as other answers have said. I want to add that I recommend you this article about floating point accuracy: Visualizing floats
Of course it is true. Think about it. Any number must be represented in binary.
Picture: "1000" as 0.5or 1/2, that is, 2 ** -1. Then "0100" is 0.25 or 1/4. You can see where I'm going.
How many numbers can you represent in this manner? 2**4. Adding more bits duplicates the available space, but it is never infinite. 1/3 or 1/10, for the matter 1/n, any number not multiple of 2 cannot be really represented.
1/3 could be "0101" (0.3125) or "0110" (0.375). Either value if you multiply it by 3, will not be 1. Of course you could add special rules. Say you "when you add 3 times '0101', make it 1"... this approach won't work in the long run. You can catch some but then how about 1/6 times 2?
It's not a problem of binary representation, any finite representation has numbers that you cannot represent, they are infinite after all.
Most CPUs (and computer languages) use IEEE 754 floating point arithmetic. Using this notation, there are decimal numbers that have no exact representation in this notation, e.g. 0.1. So if you divide 1 by 10 you won't get an exact result. When performing several calculations in a row, the errors sum up. Try the following example in python:
>>> 0.1
0.10000000000000001
>>> 0.1 / 7 * 10 * 7 == 1
False
That's not really what you'd expect mathematically.
By the way:
A common misunderstanding concerning floating point numbers is, that the results are not precise and cannot be comapared safely. This is only true if you really use fractions of numbers. If all your math is in the integer domain, doubles and floats do exactly the same as ints and also can be compared safely. They can be safely used as loop counters, for example.
yes, Java also uses floating point arithmetic.

Categories

Resources