2010-10-24 4 views
2

Возможные Дублировать:
Why don't operations on double-precision values give expected results?проблема Округление с двойным типом

Я испытываю особую проблему в C++. Я создал переменную типа Double. Затем я сделал некоторые вычисления с некоторыми значениями, назначенными другим переменным, и назначил результат двойной объявленной переменной I. Это дало мне результат с длинной десятичной частью. Я хочу, чтобы он округлялся до двух знаков после запятой. и сохраните его в переменной. Но даже после нескольких попыток округления я не мог обойти его до двух знаков после запятой.

Затем я попробовал другой способ проверить, какова настоящая проблема. Я создал двойную переменную и присвоил ей значение 1.11. Но когда я отлаживал его, поставив точку останова и добавив часы для этой переменной, я мог обнаружить, что значение, которое теперь хранится в переменной, равно 1.109999999999.

Мой вопрос в том, почему это так выглядит? Разве нет способа, по которому мы можем округлить переменную до двух десятичных знаков? Почему он показывает длинную десятичную часть, даже если мы назначим число с двумя десятичными знаками?

Пожалуйста, предложите способ хранения чисел - независимо от того, рассчитана или назначена напрямую - как есть, в двойной переменной, а не в номере с длинной десятичной частью.

+2

Не использовать арифметику с плавающей для вычислений с фиксированной точкой. –

+0

double d = 1.3894637; int i = ceil (d * 10); –

ответ

11

В наборе двойных значений нет такой величины, как число 1.11, поскольку внутри, double использует двоичное представление (в отличие от людей, которые используются для десятичного представления). Большинство конечных десятичных чисел (например, 1.11) имеют бесконечное представление в двоичном выражении, но поскольку память ограничена, вы теряете некоторую точность из-за округления.

Ближайший доступ к 1.11 с двойным типом данных: 1.1100000000000000976996261670137755572795867919921875, что внутренне представлено как 0x3ff1c28f5c28f5c3.

Ваше требование двух знаков после запятой звучит так, будто вы работаете с деньгами. Простое решение для хранения центов в целое число (в отличие от долларов в двойной):

int cents = 111; 

Таким образом, вы не потеряете точность. Другим решением является использование специального десятичного типа данных.

2

Не каждое десятичное число имеет точное, конечное, двоичное представление с плавающей запятой. Вы уже нашли один пример, но другой - 0,1 (десятичный) = 0.0001100110011 ... (двоичный).

Вам либо нужно жить с этим, либо использовать десятичную библиотеку с плавающей запятой (которая будет менее эффективной).

Моя рекомендация - хранить номера до полной точности и только округлять, когда вам нужно отображать их для людей.

+1

Это зависит от использования. Финансовые учреждения смутно рассматривают любые ошибки округления (особенно если это приносит пользу клиенту). Таким образом, если вы делаете что-либо с деньгами, вам нужен десятичный тип. –

3

типы с плавающей точкой, такие как float и double, не являются точными на 100%. Они могут хранить 14.3 как 14.299999 ... и в этом нет ничего плохого. Вот почему вы НИКОГДА не должны сравнивать два поплавка или удвоения с оператором ==, instread, вы должны проверить, меньше ли абсолютная величина их разницы, чем определенный epsilon, например 0.000000001

Теперь, если вы хотите вывести номер в приятный способ, вы можете использовать setprecision из <iomanip>

Э.Г.

#include <iostream> 
#include <iomanip> 

int main() 
{ 
    double d = 1.389040598345; 
    std::cout << setprecision(2) << d; //outputs 1.39 
} 

Если вы хотите, чтобы получить значение г округляется 2 знака после запятой после точки, то вы можете использовать эту формулу

d = floor((d*100)+0.5)/100.0; //d is now 1.39 
+2

Кастинг для 'int' - очень плохая идея, потому что большинство двойных значений слишком велики, чтобы вписаться в набор целых чисел. Вместо этого я предлагаю назвать «пол». – fredoverflow

+0

@FredOverflow: Хорошо, соглашайтесь, меняя –

Смежные вопросы