0

For example, float a is 2.15, float b is 1.15, but a-b will be 1.0000001,

My first question is how to get the right answer 2.15-1.15=1?

My second question is how to check the result of a minus b is an integer?

Cositanto
  • 682
  • 5
  • 15
  • 1
    To get the precise answer use BigDecimal, if you are working the money use BigDecimal. Note that BigDecimal can be a bit slower, so depending on the nature of your calculations you may want to avoid it, because in most cases an imprecise float/double is more than suitable when rounding is applied where necessary for the final output/display. – sorifiend Sep 02 '22 at 02:23
  • You can only check whether `a - b` is *close* to an integer, if you use `float` or `double` – tgdavies Sep 02 '22 at 02:32

3 Answers3

4

For example, float a is 2.15, float b is 1.15.

Wrong. That's not possible.

A float value is defined to be 32 bits large. That's a problem - basic mathematics: A 32-bit value can only contain 2^32 different possible values. That's about 4 billion. I know for a fact that there are far more than 4 billion numbers between 0 and 1 (in fact, there are an infinite amount of numbers). Let alone how many numbers exist between positive and negative infinity!

So how does this work? Is your computer a magic voodoo god that breaks the universe? No, of course not. Instead, only a bit less than 4 billion numbers are representable as a float and all other numbers simply are not. Imagine the number line drawn out on a big board. Somebody tossed 4 billion darts at this thing, made a picture, and decreed: Only the darts are what you can store with a float value. Anything in between 2 darts? Tough luck. Not representable.

2.15, 1.15? No dart. There is a dart really close to 2.15, and also one really really close to 1.15, though.

The rules of float math are as follows: If the result of any mathematical operation is not a 'dart number', then it is silently rounded to the nearest dart.

The darts are not linearly distributed. There are many, many darts around 0 and 1. In fact, about as many darts are between 0 and 1 as there are darts above 1. As you get away from 0, there are fewer and fewer darts. Eventually (around 2^26 or so), the distance between 2 darts is more than 1, meaning, reallyLargeFloat + 1 == reallyLargeFloat is true!! (because it is rounded to the nearest dart, which is the same dart).

The problem with 2.15-1.15 is: The actual thing that happens is:

  • Find the dart nearest 2.15. Let's say its 2.1500001.
  • Find the dart nearest 1.15. Let's say 1.149999999999998.
  • Calculate 2.1500001 - 1.149999999999998. Then take the result and round that to the nearest dart.
  • That happens to end up being 1.0000001.

You can't ask the system 'not to do the rounding silliness'. Because the universe and math and all that: 32-bits are what they are.

Thus, a few conclusions:

  • Any time you do an equality check on any result of math on double or float, you really shouldn't do that, instead, do an 'epsilon compare': Is the difference between the 2 below some really tiny number? If yes, let's call them equal. So, don't do a - b == 1.0, do: Math.abs(a - b) - 1.0 < 0.00001. Yes, this is annoying.
  • If you require precision, do not use float or double. Instead, use ints/longs (e.g. if storing an amount in EU currency, do not store, say, 5.25 for something that costs 5 euros and 25 cents. Store 525 - the amount in cents. In a long. Or, use BigDecimal, but that is a very complicated tool that has a steep learning curve.
  • If you want to know where the darts are, write down 1/3 in decimal notation. You can't: It's 0.3333 endlessly repeating. Computers count in binary (base 2), not decimal (base 10). A computer counts: 0, 1, 10, 11, 100, 101, 110, 111, 1000, etcetera. For this reason, various things that 'work' in decimal, such as, say, 3/10ths (0.3 in decimal), would have infinite digits (just like 1/3 does) in binary. This should hopefully help that it is really difficult to reason about where they are. Read up on IEEE754 which describes the way its done, if you want to know the details.
rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
2
  1. You can either still using float and round the result, or use double instead.
  2. If you mean the result has no fraction (decimal part) you can check with % 1 == 0. It should give you true.

Something like:

public class MyClass {
    public static void main(String args[]) {
      double x = 2.15;
      double y = 1.15;
      double z = x - y;

      System.out.println("Sum of x-y = " + z);
      System.out.println("Sum of x-y = " + Math.round(z));
      System.out.println("Sum of x-y = " + (z % 1 == 0));
    }
}
dk_016
  • 36
  • 1
  • 5
1

The first question as covered in comments has an answer here Is floating point math broken?. To get the precise answer you can use BigDecimal, or work in Integer units of 100's or 1000's to suit your needs eg 215-115 = 100.

For your second question you can call Integer.parseInt(float), and as long as a NumberFormatException is not thrown, then you know it was a valid Integer. You could do it a bit like this with a helper method:

float a = 2.15f;
float b = 1.15f;
float result = a-b;

System.out.println(isValidInteger(result));

//Helper method to check if the number is a valid integer
public static boolean isValidInteger(float number) {
    try{
        Integer.parseInt(number+"");
        return true;
    }
    catch (NumberFormatException ex)
    {
        return false;
    }
}

Hint: The result will always be false. If you want to get an integer from a float then use rounding with a tolerance as shown in the linked question.

sorifiend
  • 5,927
  • 1
  • 28
  • 45