2010-10-07 8 views
1

У меня есть этот статический метод, он получает двойной и «разрезает» его дробный хвост, оставляя две цифры после точки. работы почти все время. Я заметил, что когда получает 2,3, он превращает его в 2.29. Этого не происходит для 0,3, 1,3, 3,3, 4,3 и 102,3. Код в основном умножает число на 100 использует modf делит целочисленное значение на 100 и возвращает его. Вот код ловит этот один конкретный номер и печатает:modf возвращает 1 как дробное:

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; 
} 

он печатает:

число * = коэффициент: 230

дробно: 1

целое число: 229

Кто-нибудь знает, почему это происходит, и как я могу это исправить? Спасибо, и у вас отличный выходной.

ответ

5

Помните, что число с плавающей запятой не может точно представлять десятичные числа. 2.3 * 100 на самом деле дает 229.99999999999997. Таким образом, modf возвращает 229 и 0,9999999999999716.

Однако формат cout по умолчанию показывает только числа с плавающей запятой до 6 знаков после запятой. Таким образом, 0,9999999999999716 показан как 1.


Вы можете использовать (примерно) верхний предел погрешности, что значение представляет в плавающей точке, чтобы избежать ошибок 2.3:

#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; 
} 

Результат: http://www.ideone.com/ywmua

+0

Так есть лучший способ «отрезать» этот хвост и оставить только первые 2 цифры после запятой без округления? – Matti

+0

@Matti: См. Обновление. – kennytm

0

Вот способ без округления:

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

Даже если вы хотите г ounding в соответствии с 3-го знака после запятой, вот решение:

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

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

    return x/1000.0; 
}