双精度不能正确减去小数



我正在编写一个类似轮盘赌的C++命令行程序。用户可以输入十进制值/数字进行投注。我正在使用双类型变量来实现这一目标。但是,例如,如果我从 1 美元开始,然后 0.23 美元输了,然后下注 0.55 美元输了,然后下注 0.07 美元再输,我不能下注 0.15 美元,即使程序声称我实际上有 0.15 美元(你不能下比你拥有的更多的钱)。程序似乎错误地减去。但是,我仍然可以下注0.149美元。对于它的价值,我使用字符串流将用户的投注输入转换为双精度类型值。有人可以解释一下这里发生了什么吗?

这是我的代码:

#include <iostream>
#include <sstream>
using namespace std; //Std namespace.
void string_to_number(string input, double& destination);
class Roulette {
private:
int randoms;
double money, choice, bet;
string input;
public:
int play = 0;
void start_amount() {
    cout<<"How much money do you have?: ";
    getline(cin, input);
    string_to_number(input, money);
}
void betting() {
   cout<<"How much money would you like to bet?: ";
    getline(cin, input);
    string_to_number(input, bet);
    while (bet > money) {
        cout<<"You can't bet more money than you have ("<<money<<" dollars). Please enter again: ";
        getline(cin, input);
        string_to_number(input, bet);
    }
}
void choose_number() {
    cout<<"Which number do you choose? (0-35): ";
    getline(cin, input);
    string_to_number(input, choice);
}
void random_number() {
    cout<<"The wheel is spinning..."<<endl<<flush;
    randoms = (rand())%36;
}
void scenarios() {
    cout<<"The wheel shows number "<<randoms;
    if (randoms == choice) {
        money += bet;
        cout<<", which means that you win "<<bet<<" dollars! You currently have "<<money<<" dollars."<<flush<<endl;
    }
    else {
        money -= bet;
        cout<<", which means that you lose "<<bet<<" dollars. You currently have "<<money<<" dollars."<<flush<<endl;
    }
}
};
int main(int argc, const char * argv[])
{
srand(unsigned(time(0)));
Roulette a;
a.start_amount();
while (a.play == 0) {
    a.betting();
    a.choose_number();
    a.random_number();
    a.scenarios();
}
return 0;
}

void string_to_number(string input, double& destination) {
stringstream convert(input);
if ( !(convert >> destination) )
    destination = 0;
}
这不是因为程序减法错误 - 这是因为二进制分数和小数

部分不是"完全数学兼容" - 有限小数部分通常是无限周期二进制分数。

因此,对于一些像 0.15

这样的小数,存在几个有效的双近似值,作为减法的结果,你得到了其中一个 (A),作为从字符串"0.15"转换的结果,你得到了另一个 (B)。并且偶然地 B 出现大于 A。

您应该使用整数美分,而不是双倍美元来保持精确的小数舍入。更通用的解决方案是使用一些十进制数类(像这样),它很容易使用整数实现十进制分数算术。

一些十进制(和二进制)数字类实现了任意精度算法 - 它解决了定点部分大于硬件支持的双精度类型的任务。在规定舍入到美分(2 个十进制数字)的应用程序中,您不需要这样做。

舍入误差可能会对数学密集型程序造成严重破坏,因为数学运算会使误差复杂化。

http://www.learncpp.com/cpp-tutorial/25-floating-point-numbers/

对我来说,

我首先尝试通过将小数乘以 100 来移出小数,例如,0.99 将变为 99。然后减去后,我简单地将小数点除以 100 放回去。

建议不要使用浮点数进行货币计算。由于浮点数小,浮点数很快就会变得不精确。

对于货币计算,您可以使用具有固定刻度的整数(例如 100 变为 1 美元)。这样,您需要的小数位数具有 100% 的准确性。