Thread: modf() anomalous behaviour

  1. #1
    Registered User
    Join Date
    Jan 2008
    Posts
    20

    Question modf() anomalous behaviour

    Hi

    There is something annoying me for a while...
    When I execute the program
    Code:
    int main(void)
    {
       double integer_part,decimal_part;
    
       decimal_part=modf(0.46/0.01,&integer_part);
       printf("%G=(%G)+(%G)\n",0.46/0.01,integer_part,decimal_part); 
      
       decimal_part=modf(0.47/0.01,&integer_part);
       printf("%G=(%G)+(%G)\n",0.47/0.01,integer_part,decimal_part);
    
       decimal_part=modf(0.48/0.01,&integer_part);
       printf("%G=(%G)+(%G)\n",0.48/0.01,integer_part,decimal_part);
       
       decimal_part=modf(0.56/0.01,&integer_part);
       printf("%G=(%G)+(%G)\n",0.56/0.01,integer_part,decimal_part); 
      
       decimal_part=modf(0.57/0.01,&integer_part);
       printf("%G=(%G)+(%G)\n",0.57/0.01,integer_part,decimal_part);
    
       decimal_part=modf(0.58/0.01,&integer_part);
       printf("%G=(%G)+(%G)\n",0.58/0.01,integer_part,decimal_part);
    
       decimal_part=modf(0.59/0.01,&integer_part);
       printf("%G=(%G)+(%G)\n",0.59/0.01,integer_part,decimal_part);
    
       return 0;
    }
    I get as results:

    46=(46)+(0)
    47=(46)+(1)
    48=(48)+(0)
    56=(56)+(7.10543E-15)
    57=(56)+(1)
    58=(57)+(1)
    59=(58)+(1)

    But shouldn't all decimal parts just vanish? It seems to work for 0.46/0.01 and 0.48/0.01 but not for the other values. And it keeps like this for other value ranges...

    Thanks

  2. #2
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Floating point numbers are not precise, 7.10543E-15 is "the lowest bit" in a floating point number. So when you subtract one number from another, sometimes the result ends up "one more" or "one less" on the final bit of the calculation. Just like 1/3 can't be exactly described as a number with decimals, and if you use a calculator, most likely (1/3) * 3 will end up being 0.99999999999 - if you subtract that from 1, you may find that you get 0.0000000001 [excuse number of zeros/nines not matching - you get the idea].

    The same thing can happen when you translate a decimal number (as written in your code or typed in by the user) to binary - it can't be described EXACTLY in binary, so the when you do math on the result, there's a tiny bit of the "actual number" missing, e.g 56 becomes 55.9999999..., and when you subtract that from the original 56, it comes up a tiny bit short.

    This is exactly what happens here. When you subtract one part from the other, it gets a "teensy bit left behind".

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #3
    Registered User
    Join Date
    Jan 2008
    Posts
    20
    Quote Originally Posted by matsp View Post
    Floating point numbers are not precise, 7.10543E-15 is "the lowest bit" in a floating point number. So when you subtract one number from another, sometimes the result ends up "one more" or "one less" on the final bit of the calculation. Just like 1/3 can't be exactly described as a number with decimals, and if you use a calculator, most likely (1/3) * 3 will end up being 0.99999999999 - if you subtract that from 1, you may find that you get 0.0000000001 [excuse number of zeros/nines not matching - you get the idea].

    The same thing can happen when you translate a decimal number (as written in your code or typed in by the user) to binary - it can't be described EXACTLY in binary, so the when you do math on the result, there's a tiny bit of the "actual number" missing, e.g 56 becomes 55.9999999..., and when you subtract that from the original 56, it comes up a tiny bit short.

    This is exactly what happens here. When you subtract one part from the other, it gets a "teensy bit left behind".

    --
    Mats
    Thanks

    But how can I test if a division results an integer or not? At least bounded by some defined precision, say 1E-15.

    I though using something like

    if x/y- the integer part of x/y<some precision then x/y results an integer...
    In this case, the integer part would be given by modf...

    But it seems not to be the case. Does it?
    If not, can you point me another way?

    Thanks a lot!!!
    Last edited by John Connor; 02-04-2008 at 03:10 PM.

  4. #4
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    You can compare with a "small number" - unfortunately, the number "grows" as your number grows, so say 14000000000 would have a different smallest number representable than a 0.000000014. But for numbers of "rather common size" (e.g. earthly distances rather than interplanetary distances or values appearing of most peoples bank statements rather than Bill Gates's or such) it would be sufficient to compare with something like 1E-9.

    We did have a discussion about how to come up with a flexible representation, which was based on overlaying a double precision number with a 64-bit integer, and then do some bit manipulation to create the smallest possible number that was representable. You may be able to find it if you search for "how to create epsilon".

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #5
    Registered User
    Join Date
    Jan 2008
    Posts
    20
    Quote Originally Posted by matsp View Post
    You can compare with a "small number" - unfortunately, the number "grows" as your number grows, so say 14000000000 would have a different smallest number representable than a 0.000000014. But for numbers of "rather common size" (e.g. earthly distances rather than interplanetary distances or values appearing of most peoples bank statements rather than Bill Gates's or such) it would be sufficient to compare with something like 1E-9.

    We did have a discussion about how to come up with a flexible representation, which was based on overlaying a double precision number with a 64-bit integer, and then do some bit manipulation to create the smallest possible number that was representable. You may be able to find it if you search for "how to create epsilon".

    --
    Mats
    Thanks

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. cache behaviour
    By cyberfish in forum C++ Programming
    Replies: 10
    Last Post: 12-25-2008, 07:48 AM
  2. Undefined Behaviour (Palindromic Number Finder)
    By pobri19 in forum C++ Programming
    Replies: 12
    Last Post: 09-28-2008, 04:54 AM
  3. ofstream and FILE behaviour
    By MrLucky in forum C++ Programming
    Replies: 7
    Last Post: 06-21-2007, 05:45 PM
  4. String overflow behaviour
    By Morgan in forum C Programming
    Replies: 15
    Last Post: 10-10-2003, 02:37 AM