Ошибка из-за точности двойного типа в C++
Я написал класс для расчета суммы денег для меня. Ниже код вкратце.
class RMB
{
private:
int yuan;
int jiao;
int fen;
bool mark;
public:
RMB(int yu, int ji, int fe, bool mar = true)
{
yuan = yu;
jiao = ji;
fen = fe;
mark = mar;
}
RMB(double money)
{
int money1 = int(money * 100);
yuan = money1 / 100;
fen = money1 % 10;
jiao = (money1 - yuan * 100 - fen)/10;
if (money < 0) mark = 0;
else mark = 1;
}
operator double()
{
double money = yuan + double(jiao) / 10 + double(fen) / 100;
if (mark == false) return -money;
return money;
}
};
int main()
{
RMB a(1,2,3);RMB b(2,3,4);
cout << "a + c = " << RMB(a + c) << endl;//assume I have override "<<" and ">>"
cout << "a - b = " << RMB(a - b) << endl;
}
Но когда я тестировал свой код, случилось так:
a + c = 2 yuan 4 jiao 5 fen
a - b = -1 yuan -1 jiao 0 fen
Я проверяю это в VS2015, я отладил его, и я вижу точное значение double(a-b)
-1.199999998. Итак, как я могу исправить эту ошибку и что я могу сделать, чтобы избежать такой ошибки?
3 ответа
Самая простая вещь, которую можно сделать за милю страны, - это использовать целочисленный тип для денежных значений и работы в центах. Использование двоичных типов с плавающей точкой для точных десятичных значений никогда не закончится хорошо.
Если они есть у вашего компилятора, используйте std::uint64_t
как тип, или std::int64_t
если вам нужна концепция отрицательной суммы. Это более чем достаточно для мирового ВВП, выраженного в зимбабвийских долларах. Вы могли бы даже обернуть это в class
для проверки будущего.
C++ намеренно не предоставляет десятичный тип "из коробки".
RMB(double money)
{
int money1 = int(money * 100);
Если деньги отрицательны, значит money1 тоже!
yuan = money1 / 100;
fen = money1 % 10;
jiao = (money1 - yuan * 100 - fen)/10;
Так будут ваши участники!
if (money < 0)
mark = 0;
else
mark = 1;
И вы храните знак дополнительно (для использования в operator double
):
return mark ? money : -money;
Предположим, что вы должны сделать своих членов положительными вместо этого (и я бы предпочел неподписанных членов тогда):
RMB(double money)
: mark(money >= 0)
{
unsigned int money1 = (int)((mark ? money : -money) * 100);
// ...
}
Я бы порекомендовал это прочитать: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
Для более глубокого изучения предмета я рекомендую: "Что должен знать каждый компьютерный специалист об арифметике с плавающей точкой"