Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Float greater or less than zero

Tags:

java

android

I have the following code which sometimes return as true and sometimes doesn't.

Any idea what might be causing the varying result?

The 0.00 string comes from a JSON object.

(code simplified)

if(new Float("0.00")>0){
    // do something
}

EDIT:

What I have is some floats which I want to determine if its zero, less than zero or more than zero. The values could be stuff like 0.00, 0.001 or -0.001. How do I determine whether whether they are positive, negative or zero?

EDIT:

Perhaps I should clarify how I get the values. I might be totally something else that is causing the problem because I read about BigDecimal and tried to use it to no avail.

The values are extracted from a JSON feed (which has this format LT:0.000) using this code:

price = new BigDecimal(stocksJSONArray.getJSONObject(i).getString("LT"));

Then to test if price is greater or less than zero, I used the following conditional statements:

if(price.compareTo(BigDecimal.ZERO)==1){
    // greater than zero
}
else if(price.compareTo(BigDecimal.ZERO)==-1){
    // less than zero
}

This code is looped for many values read out from the JSON feed. And from the results, some price which is zero gets processed as greater than zero and some gets processed as less than zero. I'm suspecting that something else is causing the problem here?

I also did a test to see if the problem was with the accuracy of the data. So I did this:

DecimalFormat frmt = new DecimalFormat("0.000000000000000000000000");
String formatted = frmt.format(stock.change);

And for the zeros that got recognized as positives and negatives, the trace value for it was still 0.000000000000000000000000, not 0.000000000000000000000001 or something like that.

like image 964
mushroom Avatar asked Apr 30 '12 15:04

mushroom


2 Answers

Your expression will reliably produce a result of false in Java.

However, suppose that the zero is, for example, the result of -1 divided by plus infinity. In this case it will be internally represented as, roughly said, -0.00. In some circumstances it will still be printed as a zero (without the minus sign), in others it will behave differently from 0.00.

Floats can generally be compared for less-than in the same way as integers - there is a risk of a rounding error, but that error is not helped by adding or subtracting a random small value. It is different with comparison for equality.

I suggest you double check your facts and do more reading about floating point behavior.

Edit: I am greatly simplifying above to answer the original question. To answer the edit to the question, we need to go deeper.

For any operation on floating point numbers one should know and consider the precision and accuracy of inputs, and the desired accuracy of the output. Sometimes the task is solvable and sometimes it is not - the input accuracy may not be sufficient to produce the answer.

In your case, the precision is 32 bit, out of which 24 bits are the mantissa and 8 bits exponent. That means that this data type safely distinguishes 0.001 from 0.00100001 but not from 0.001000001 as you can easily see:

 System.out.println((float)0.001 < (float)0.001000001);

(Note that you would get a different result if you did not force single precision comparison by the casts. In that case the computation would be done in double precision and the numbers would be safely distinguished - until you bring them even closer together.)

So, precision is determined by the data type. Not so accuracy. Input accuracy is often much more challenging to determine than precision because it has nothing to do with the data type, except that accuracy can never be better than the precision.

A mathematical real number can find itself in four possible circumstances with regard to representability in a particular floating point type, and those correspond to different treatments that it receives when it occurs as a literal, in the human readable decimal notation.

  • It might be representable accurately in binary. For example, 0 or 0.25. Then it is as accurate as an integer would be in an integer variable.
  • Or it is approximately representable with accuracy corresponding to the type's precision. For example, 1/3 or 0.1 or 0.001. This happens when the exponent needed fits in the number of exponent bits available, but when the binary expansion of the number is either longer than the mantissa, or outright infinite.
  • Or it is approximately representable with drastically distorted accuracy. These are denormal (subnormal) numbers. It's not only inaccurate, arithmetics on it may slow down to a crawl, which is typically documented correct behavior, and even a respectable Java compiler may sweat a little upon seeing literals of this kind which is a however a bug.
  • Or it does not fit at all and compilers will refuse the literal as too large.

So, in your case, we have only three valid inputs: 0 (accurate), 0.001 (approximate) and -0.001 (approximate), and that makes your problem solvable. Simply compare your numbers with a 0 literal (which is accurate, by the way) and you will always get the expected boolean values (fully accurate output).

This however depends on your inputs being directly derived from literals. If your inputs were one of 0.001, -0.001 and (float)1000 * (float)0.001 - 1, this would be a different question and you would have to get the answers, for example like this:

if (-0.00001 < x && x < 0.00001) // then x is zero 

And if you allow any inputs whatsoever, not just those three magic values, and have no clue about the input's accuracy, then that's just mission impossible. Even a literal starting as 0.000000000... with some garbage digits far down at the end will be converted by a Java compiler to a perfectly neutral zero, and after this happens, no amount of Java code will ever tell it from the accurate and beautiful 0.00, or from what happens if you add a minus sign to the underflowing value. It's all the same, inaccurate zero, the same bit pattern in your variable, rather than 3 distinct values.

like image 78
Jirka Hanika Avatar answered Oct 20 '22 00:10

Jirka Hanika


Floats are inaccurate. You need to specify a margin of error, like:

float result = new Float("0.00");
float expected = 0;
if (Math.abs(result - expected) < 0.00001)
...
like image 30
dhaag23 Avatar answered Oct 20 '22 00:10

dhaag23