views:

43

answers:

2

I have this static method, it receives a double and "cuts" its fractional tail leaving two digits after the dot. works almost all the time. I have noticed that when it receives 2.3 it turns it to 2.29. This does not happen for 0.3, 1.3, 3.3, 4.3 and 102.3. Code basically multiplies the number by 100 uses modf divides the integer value by 100 and returns it. Here the code catches this one specific number and prints out:

static double dRound(double number) {

    bool c = false;
    if (number == 2.3)
        c = true;


    int factor = pow(10, 2);
    number *= factor;


    if (c) {
        cout << " number *= factor : " << number << endl;
        //number = 230;// When this is not marked as comment the code works well.
    }


    double returnVal;
    if (c){
        cout << " fractional : " << modf(number, &returnVal) << endl;
        cout << " integer : " <<returnVal << endl;
    }


    modf(number, &returnVal);
    return returnVal / factor;
}

it prints out:

number *= factor : 230

fractional : 1

integer : 229

Does anybody know why this is happening and how can i fix this? Thank you, and have a great weekend.

+3  A: 

Remember floating point number cannot represent decimal numbers exactly. 2.3 * 100 actually gives 229.99999999999997. Thus modf returns 229 and 0.9999999999999716.

However, cout's format will only display floating point numbers to 6 decimal places by default. So the 0.9999999999999716 is shown as 1.


You could use (roughly) the upper error limit that a value represents in floating point to avoid the 2.3 error:

#include <cmath>
#include <limits>
static double dRound(double d) {
   double inf = copysign(std::numeric_limits<double>::infinity(), d);
   double theNumberAfter = nextafter(d, inf);
   double epsilon = theNumberAfter - d;

   int factor = 100;
   d *= factor;
   epsilon *= factor/2;
   d += epsilon;

   double returnVal;
   modf(number, &returnVal);
   return returnVal / factor;
}

Result: http://www.ideone.com/ywmua

KennyTM
So is there a better way to "cut" this tail and leave only the first 2 digits after the dot without rounding?
Matti
@Matti: See update.
KennyTM
A: 

Here is a way without rounding:

double double_cut(double d)
{
    long long x = d * 100;
    return x/100.0;
}

Even if you want rounding according to 3rd digit after decimal point, here is a solution:

double double_cut_round(double d)
{
    long long x = d * 1000;

    if (x > 0)
        x += 5;
    else
        x -= 5;

    return x / 1000.0;
}
Donotalo