Round with floor problem in Objective-C

10,458

Solution 1

This will be due to floating point calculations.

Your calculation

g * 100

already brings back

229.99999999999997

From where your issue stems.

Have a look at INFO: Precision and Accuracy in Floating-Point Calculations

Also have a look at Floating point

Accuracy problems

The fact that floating-point numbers cannot precisely represent all real numbers, and that floating-point operations cannot precisely represent true arithmetic operations, leads to many surprising situations. This is related to the finite precision with which computers generally represent numbers.

Solution 2

As others have already mentioned, this is due to the limited precision of floating point numbers in computers. These imprecisions show up everywhere a hard yes/no decision about a floating point number is made. In order to resolve the problem, you can add/subtract a small number to find an answer that is correct up to a certain accuracy.

You may find functions like these useful:

#define ACC 1e-7

double floorAcc( double x ) { return floor(x + ACC);}
double ceilAcc( double x ) { return ceil(x - ACC); }
bool isLessThanAcc( double x, double y ) { return (x + ACC) < y; }
bool isEqualAcc( double x, double y ) { return (x + ACC) > y && (x - ACC) < y; }

Of course, these work only in a limited number range. When working with very small or very large numbers, you need to pick another value for ACC.

Note that the value of 'ACC' is in general dependent on the accuracy of the numbers in your application, not on the value of x. For example, comparing two numbers a and b for equality can be done in two ways: isEqualAcc(a, b) and isEqualAcc(a-b, 0). You would want the same result from both ways, even though in the second way the number x is likely much smaller.

Share:
10,458
Admin
Author by

Admin

Updated on June 04, 2022

Comments

  • Admin
    Admin about 2 years

    I am calculating g with e and s, which are all doubles. After that I want to cut off all digits after the second and save the result in x, for example:

    g = 2.123 => x = 2.12

    g = 5.34995 => x = 5.34

    and so on. I Use...

    g = 0.5*e + 0.5*s;
    x = floor(g*100)/100;
    

    ...and it works fine most of the time. But sometimes I get strange results. For example:

    e = 3.0 s = 1.6 g = 2.30 but x = 2.29!!!

    So I tried to track down the error:

    g = 0.5*e + 0.5*s;
    NSLog(@"%f",g);
    

    gives me g = 2.30

    g = g * 100;
    NSLog(@"%f",g);
    

    gives me g = 230.0

    x = floor(g);
    NSLog(@"%f",x);
    

    results in x = 229.0 !!!

    I don't get it! Help please! :-)

  • Admin
    Admin over 14 years
    Thanks a lot. Do you have a quick solution to avoid this problem?
  • Adriaan Stander
    Adriaan Stander over 14 years
    Have a look at social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/‌​…. try decimal vs double for precision if you can. And maybe stackoverflow.com/questions/618535/…
  • outis
    outis over 14 years
    the decimal type is a .Net type; OP is using Objective-C & the Cocoa API (note the call to NSLog), so decimal isn't an option.
  • Adriaan Stander
    Adriaan Stander over 14 years
    Sorry, when i originally posted the answer it also stated c#, please see the revisions to the question.
  • Albert Renshaw
    Albert Renshaw over 5 years
    Surely ACC should be calculable and not just hard coded? If we know the size of x can't we determine the size of potential floating point errors?
  • Albert Renshaw
    Albert Renshaw over 5 years
    I did notice __FLT_EPSILON__ in C documentation; perhaps useful here.
  • fishinear
    fishinear over 5 years
    ACC is almost always dependent on the general accuracy of the numbers in your application, not relative to x. See added explanation in answer.