23

Do we need to be careful when comparing a double value against zero?

if ( someAmount <= 0){
.....
}
  • 1
    See [BigDecimal](http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html). – karlgrz Jan 12 '09 at 07:05
  • This is not completely true. There are definitely more operations that can cause the value to not be exact when using floating point calculations. For instance 0.3-0.2-0.1 will not evaluate to exactly 0, but rather to -0.000000000000000027755575615628914 which in turn is != 0 – SteinNorheim Jan 12 '09 at 07:30
  • Pretty much *any* operation in floating point can give confusing results. Addition and subtraction can certainly do it - particularly if the numbers being added (or subtracted) are at very different extremes, e.g. 1e10 + 1e-10. – Jon Skeet Jan 12 '09 at 07:35
  • @norheim: In your particular examples, the problem isn't in the subtraction operation - it's in the conversion of "0.3" (etc) to doubles to start with. If you look at the *exact* values being subtracted, I think you'll see exact subtraction. – Jon Skeet Jan 12 '09 at 07:36
  • I believe the typical approach is to use some tolerance value and test for difference being smaller then that. – Miserable Variable Jan 12 '09 at 08:11
  • 2
    Use BigDecimal, if its appropriate. – Adeel Ansari Jan 12 '09 at 08:12
  • Agreed. BigDecimal would definitely be necessary for any computations. – karlgrz Jan 12 '09 at 16:22
  • 3
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – JMax Aug 30 '12 at 13:56
  • https://github.com/mortezaadi/bigdecimal-utils – Morteza Adi Sep 30 '18 at 11:50

7 Answers7

18

If you want to be really careful you can test whether it is within some epsilon of zero with something like

double epsilon = 0.0000001;
if      ( f <= ( 0 - epsilon ) ) { .. }
else if ( f >= ( 0 + epsilon ) ) { .. }
else { /* f "equals" zero */ }

Or you can simply round your doubles to some specified precision before branching on them.

For some interesting details about comparing error in floating point numbers, here is an article by Bruce Dawson.

Crashworks
  • 40,496
  • 12
  • 101
  • 170
9

For equality: (i.e. == or !=) yes.

For the other comparative operators (<, >, <=, >=), it depends whether you are about the edge cases, e.g. whether < is equivalent to <=, which is another case of equality. If you don't care about the edge cases, it usually doesn't matter, though it depends where your input numbers come from and how they are used.

If you are expecting (3.0/10.0) <= 0.3 to evaluate as true (it may not if floating point error causes 3.0/10.0 to evaluate to a number slightly greater than 0.3 like 0.300000000001), and your program will behave badly if it evaluates as false -- that's an edge case, and you need to be careful.

Good numerical algorithms should almost never depend on equality and edge cases. If I have an algorithm which takes as an input 'x' which is any number between 0 and 1, in general it shouldn't matter whether 0 < x < 1 or 0 <= x <= 1. There are exceptions, though: you have to be careful when evaluating functions with branch points or singularities.

If I have an intermediate quantity y and I am expecting y >= 0, and I evaluate sqrt(y), then I have to be certain that floating-point errors do not cause y to be a very small negative number and the sqrt() function to throw an error. (Assuming this is a situation where complex numbers are not involved.) If I'm not sure about the numerical error, I would probably evaluate sqrt(max(y,0)) instead.

For expressions like 1/y or log(y), in a practical sense it doesn't matter whether y is zero (in which case you get a singularity error) or y is a number very near zero (in which case you'll get a very large number out, whose magnitude is very sensitive to the value of y) -- both cases are "bad" from a numerical standpoint, and I need to reevaluate what it is I'm trying to do, and what behavior I'm looking for when y values are in the neighborhood of zero.

Jason S
  • 184,598
  • 164
  • 608
  • 970
6

Depending on how your someAmount is computed, you may expect some odd behaviour with float/doubles

Basically, converting numeric data to their binary representation using float / doubles is error prone, because some numbers cannot be represented with a mantis/exponent.

For some details about this you can read this small article

You should consider using java.lang.Math.signum or java.math.BigDecimal , especially for currency & tax computing

WiseTechi
  • 3,528
  • 1
  • 22
  • 15
  • 1
    Interesting article, but seemed a bit dated. I liked this quote: "In most computers, floating point arithmetic is usually much slower than integer arithmetic, though on the Intel Pentium it is usually faster because the integer unit was not given the same care as the floating point unit." a lot. :) – unwind Jan 12 '09 at 07:30
  • @unwind: but that's pretty much the only part that's no longer relevant. Everything except the performance is unchanged since then. – Joachim Sauer Jan 12 '09 at 08:19
5

Watch out for auto-unboxing:

Double someAmount = null;
if ( someAmount <= 0){

Boom, NullPointerException.

Thilo
  • 257,207
  • 101
  • 511
  • 656
1

Yes you should be careful.

Suggestion : One of the good way would be using BigDecimal for checking equality/non-equality to 0:

BigDecimal balance = pojo.getBalance();//get your BigDecimal obj

0 != balance.compareTo(BigDecimal.ZERO)

Explanation :

The compareTo() function compares this BigDecimal with the specified BigDecimal. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. This method is provided in preference to individual methods for each of the six boolean comparison operators (<, ==, >, >=, !=, <=). The suggested idiom for performing these comparisons is: (x.compareTo(y) <op> 0), where is one of the six comparison operators.

(Thanks to SonarQube documentation)

Floating point math is imprecise because of the challenges of storing such values in a binary representation. Even worse, floating point math is not associative; push a float or a double through a series of simple mathematical operations and the answer will be different based on the order of those operation because of the rounding that takes place at each step.

Even simple floating point assignments are not simple:

float f = 0.1; // 0.100000001490116119384765625
double d = 0.1; // 0.1000000000000000055511151231257827021181583404541015625

(Results will vary based on compiler and compiler settings);

Therefore, the use of the equality (==) and inequality (!=) operators on float or double values is almost always an error. Instead the best course is to avoid floating point comparisons altogether. When that is not possible, you should consider using one of Java's float-handling Numbers such as BigDecimal which can properly handle floating point comparisons. A third option is to look not for equality but for whether the value is close enough. I.e. compare the absolute value of the difference between the stored value and the expected value against a margin of acceptable error. Note that this does not cover all cases (NaN and Infinity for instance).

Vivek
  • 895
  • 11
  • 23
0

If you don't care about the edge cases, then just test for someAmount <= 0. It makes the intent of the code clear. If you do care, well... it depends on how you calculate someAmount and why you're testing for the inequality.

quant_dev
  • 6,181
  • 1
  • 34
  • 57
  • No matter what the intention is, if you want your code to work definitively, In my view, you should never write double comparisons like that. Always, use EPSILON or BigDecimal – endless Jul 25 '13 at 17:35
0

Check a double or float value is 0, an error threshold is used to detect if the value is near 0, but not quite 0. I think this method is the best what I met.

How to test if a double is zero? Answered by @William Morrison

public boolean isZero(double value, double threshold){
  return value >= -threshold && value <= threshold;
}

For example, set threshold as 0. like this,

System.out.println(isZero(0.00, 0));
System.out.println(isZero(0, 0));
System.out.println(isZero(0.00001, 0));

The results as true, true and false from above example codes.

Have Fun @.@

Community
  • 1
  • 1
Luna Kong
  • 3,065
  • 25
  • 20