Comparing floats

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • saneman

    Comparing floats

    I have a function:

    int F(double a)
    {
    if (a = =1.0)
    {
    return 22;
    }
    return 44;
    }


    When I call F(1.0) it returns 22. But if I call F from a loop like:

    double a = 0.0;
    for (int j = 0; j <= 1000; j++)
    {
    std::cout << F(a) <<std::endl;
    a = a + 0.001;
    }

    It returns 44 instead when 'a' reaches 1 instead of 22!

    After a few hours of messing with my brains I found a website where they say
    that comparing floats in C++ is not guranteed to return an expected result.
    Instead something like this should be used:

    if (fabs(result - expectedResult) < 0.00001)

    But before I rewrite all my code that compare floats I would like to hear if
    anyone has any better suggestion.


  • Sam

    #2
    Re: Comparing floats

    saneman writes:
    I have a function:
    >
    int F(double a)
    {
    if (a = =1.0)
    {
    return 22;
    }
    return 44;
    }
    >
    >
    When I call F(1.0) it returns 22. But if I call F from a loop like:
    >
    double a = 0.0;
    for (int j = 0; j <= 1000; j++)
    {
    std::cout << F(a) <<std::endl;
    a = a + 0.001;
    }
    >
    It returns 44 instead when 'a' reaches 1 instead of 22!
    This is because:

    1) When internally represented in binary, fractional amounts are irrational.

    2) Addition accumulates roundoff errors.

    Add the following to your inner loop:

    std::cout << a << std::endl;

    You'll see what's happening.


    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.9 (GNU/Linux)

    iEYEABECAAYFAki qHNQACgkQx9p3GY HlUOJ76QCfZOmuH Bpnr3NKnTpqiEGN +SYl
    +LEAn2v4NZIKi7a CQiuyjGhpU52aOy US
    =mT+9
    -----END PGP SIGNATURE-----

    Comment

    • Gennaro Prota

      #3
      Re: Comparing floats

      saneman wrote:
      I have a function:
      >
      int F(double a)
      {
      if (a = =1.0)
      {
      return 22;
      }
      return 44;
      }
      >
      >
      When I call F(1.0) it returns 22. But if I call F from a loop like:
      >
      double a = 0.0;
      for (int j = 0; j <= 1000; j++)
      {
      std::cout << F(a) <<std::endl;
      a = a + 0.001;
      }
      >
      It returns 44 instead when 'a' reaches 1 instead of 22!
      >
      After a few hours of messing with my brains I found a website where they say
      that comparing floats in C++ is not guranteed to return an expected result.
      Instead something like this should be used:
      >
      if (fabs(result - expectedResult) < 0.00001)
      It depends on what you want to do. Most times it is wrong as well. But
      this isn't C++ specific; for starters, study the paper "What Every
      Computer Scientist Should Know About Floating-Point Arithmetic", by
      David Goldberg.

      --
      Gennaro Prota | name.surname yahoo.com
      Breeze C++ (preview): <https://sourceforge.net/projects/breeze/>
      Do you need expertise in C++? I'm available.

      Comment

      • saneman

        #4
        Re: Comparing floats

        >This is because:
        >
        >1) When internally represented in binary, fractional amounts are
        >irrational.
        >
        >2) Addition accumulates roundoff errors.
        >
        >Add the following to your inner loop:
        >
        >std::cout << a << std::endl;
        >
        >You'll see what's happening.


        I have tried doing std::cout << a << std::endl; but it just prints the
        numbers from 0.001 to 1 as expected.


        Comment

        • Sam

          #5
          Re: Comparing floats

          saneman writes:
          >>This is because:
          >>
          >>1) When internally represented in binary, fractional amounts are
          >>irrational.
          >>
          >>2) Addition accumulates roundoff errors.
          >>
          >>Add the following to your inner loop:
          >>
          >>std::cout << a << std::endl;
          >>
          >>You'll see what's happening.
          >
          >
          >
          I have tried doing std::cout << a << std::endl; but it just prints the
          numbers from 0.001 to 1 as expected.
          Probably the precision is not enough. Use std::setw and std::setprecisi on to
          increase the precision shown.



          -----BEGIN PGP SIGNATURE-----
          Version: GnuPG v1.4.9 (GNU/Linux)

          iEYEABECAAYFAki qqVwACgkQx9p3GY HlUOLihQCfdkVHJ Bzm5Qc5Dhu07NeX b6zh
          2LQAn1M/jz6n9OX2okAqRvt JeQVDMGGo
          =vAtO
          -----END PGP SIGNATURE-----

          Comment

          • Juha Nieminen

            #6
            Re: Comparing floats

            saneman wrote:
            After a few hours of messing with my brains I found a website where they say
            that comparing floats in C++ is not guranteed to return an expected result.
            There are a few flaws in that statement:

            - This is not related to C++ in particular, but computers in general.
            All programming languages which perform direct floating point
            comparisons will present the same issue. (Some programming languages
            might perform an automatic range comparison behind the scenes, but C++
            only does exactly what you tell it to do, nothing more.)

            - Whether the result you are seeing is "expected" or not is a question
            of definition. For example, assume that I expect this comparison to
            return true:

            0.3333333 + 0.3333333 + 0.3333333 == 1.0

            but instead, it returns false. My expectation was wrong. In fact, the
            comparison returning false is what should be expected. Why? Because if
            you calculate that sum, you get 0.9999999, which is different from 1.0.
            Thus they should obviously compare unequal.

            In your example program you are summing 0.001 to itself. However,
            computers do not calculate with decimal numbers. They calculate with
            base-2 numbers with a limited amount of bits. Base-2 floating point
            values with a limited amount of bits cannot represent 0.001 accurately
            (for the exact same reason that decimal numbers cannot represent 1/3
            accurately).

            Thus when you write 0.001, internally the actual value will be just a
            tiny bit smaller than that (exactly like 0.3333333333333 33 is a tiny bit
            smaller than 1/3). When you sum that with itself a thousand times, what
            you get is 1000 times that a-bit-smaller value, which ends up being
            a-bit-smaller than 1.0, which obviously compares unequal to 1.0.

            Thus summing 0.001 to itself 1000 times is *expected* to give a result
            which is smaller than 1.0 because 0.001 is actually a bit smaller than
            what it looks.

            (This doesn't mean that no decimal number can be represented
            accurately with base-2 floating point numbers. For example 1.0 and 0.5
            can be represented accurately, without any rounding off.)

            The result of long calculations can accumulate these types of rounding
            errors, and thus the result may differ from what an infinitely-accurate
            mathematical formula would tell you. Thus you have to make ranged
            comparisons.

            Another option is to avoid floating point numbers altogether. In many
            situations they are just not needed. For example, if you just need all
            the values between 0.0 and 1.0, you can do this:

            for(int i = 0; i <= 1000; ++i)
            someFunctionTak ingADouble(i/1000.0);

            Now, surprisingly, the last value given to the function will be
            exactly 1.0.

            Comment

            Working...