Двойное округление снова

В моей программе есть некоторые точности (некоторое положительное целое число, в большинстве случаев оно должно иметь вид х * 10 ^ п) для некоторых пар, так что double * precision должно стать целым числом.

Но, как мы все знаем, числа с плавающей запятой неточны, так, например, 1.3029515 может быть сохранен как 1.3029514999999998..., и в моей программе мне нужно записать такое число с плавающей запятой в файл, но я хочу это 1.3029515 быть написано вместо чего-то вроде 1.3029514999999998...,

Ранее только точность формы 10 ^ n, 0 <= n <= 8 был использован в моей программе, и я достиг желаемого результата с помощью кода, как показано ниже:

// I have a function for doubles equality check
inline bool sameDoubles(const double& lhs, const double& rhs, const double& epsilon) {
    return fabs(lhs - rhs) < epsilon;
}

inline void roundDownDouble(double& value, const unsigned int& numberOfDigitsInFraction = 6) {
    assert(numberOfDigitsInFraction <= 9);
    double factor = pow(10.0, numberOfDigitsInFraction);
    double oldValue = value;
    value = (((int)(value * factor)) / factor);
    // when, for example, 1.45 is stored as 1.4499999999..., we can get wrong value, so, need to do the check below
    double diff = pow(10.0, 0.0 - numberOfDigitsInFraction);
    if(sameDoubles(diff, fabs(oldValue - value), 1e-9)) {
        value += diff;
    }
};

Но теперь я не могу достичь желаемых результатов с помощью той же техники, я пробовал с помощью функции ниже, но не удалось:

// calculates logarithm of number with given base
double inline logNbase(double number, double base) {
    return log(number)/log(base);
}

// sameDoubles function is the same as in above case

inline void roundDownDouble(double& value, unsigned int precision = 1e+6) {
    if(sameDoubles(value, 0.0)) { value = 0; return; }
    double oldValue = value;
    value = ((long int)(value * precision) / (double)precision);
    // when, for example, 1.45 is stored as 1.4499999999..., we can get wrong value, so, need to do the check below
    int pwr = (int)(logNbase((double)precision, 10.0));
    long int coeff = precision / pow(10, pwr);
    double diff = coeff * pow(10, -pwr);
    if(sameDoubles(diff, fabs(oldValue - value),  diff / 10.0)) {
        if(value > 0.0) {
            value += diff;
        } else {
            value -= diff;
        }
    }
}

За 1.3029515 значение и precision = 2000000 эта функция возвращает неверный 1.302951 значение (выражение (long int)(value * precision) становится равным 2605902 вместо 2605903).

Как я могу это исправить? Или, может быть, есть какой-то умный способ сделать это округление правильно?

1 ответ

Вы делаете свое округление трудным путем. Сделайте это простым способом:

double rounding = 0.5;
if (value < 0.0) rounding = -0.5;
value = ((long int)(value * precision + rounding) / (double)precision);

Теперь нет необходимости в остальной части кода.

Другие вопросы по тегам