-1

I have wrote a class to calculate the amount of money for me. Below is the code in brief.

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

But when I test my code,it happened that:

a + c = 2 yuan 4 jiao 5 fen

a - b = -1 yuan -1 jiao 0 fen

I test it in VS2015,I have debugged it, and I see exact value of double(a-b) is -1.199999998. So how can I fix this bug and what can I do to avoid bug like this kind?

Black Bob
  • 105
  • 1
  • 13
  • 10
    *what can I do to avoid bug like this kind?* Don't use double to represent currency. – Borgleader Mar 29 '17 at 13:27
  • 3
    What is e.g. `2,3,4` supposed to do in the construction of the `b` object? Perhaps you should read more about [the comma operator](http://en.cppreference.com/w/c/language/operator_other#Comma_operator)? – Some programmer dude Mar 29 '17 at 13:27
  • related: http://stackoverflow.com/questions/149033/best-way-to-store-currency-values-in-c – NathanOliver Mar 29 '17 at 13:29
  • In fact I write a constructor to initialize the class. I just forgot it. – Black Bob Mar 29 '17 at 13:31
  • Well known fact that you have just discovered: Floating point (and double) representation is "approximate". – DisappointedByUnaccountableMod Mar 29 '17 at 13:31
  • Use integer or a type that does exact computations. Don't use `double` -- it is the wrong tool to use for this job. – PaulMcKenzie Mar 29 '17 at 13:31
  • [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – Borgleader Mar 29 '17 at 13:32
  • 1
    You should provide a [minimal, complete and verivfiable example](http://stackoverflow.com/help/mcve) to get appropriate help. Your example is not complete (one (used!) constructor, operator+ and operator<< lacking). On the other hand, the constructor accepting double apparently is not used at all (-> leave it out -> minimality!). – Aconcagua Mar 29 '17 at 13:34
  • Sorry, need to correct myself: Of course, double ctor *is* used (implicit double casts...). – Aconcagua Mar 29 '17 at 13:51
  • My teacher asked me to write a type conversion function for double, I am wondering if it is possible to do it without bug now. – Black Bob Mar 29 '17 at 13:51
  • @AlexThomson -- What are you converting the double to? BCD? Fixed floating point? Unless that conversion function is akin to something like that, the teacher is leading you astray. – PaulMcKenzie Mar 31 '17 at 03:52

3 Answers3

10

The easiest thing to do by a country mile is to use an integral type for money values and work in cents. Using binary floating point types for exact decimal values is never going to end well.

If your compiler has them, then use std::uint64_t as the type, or std::int64_t if you need the concept of a negative amount. That's more than large enough for world GDP expressed in Zimbabwean Dollars. You could even wrap this in a class for future proofing.

C++ intentionally does not provide a decimal type "out of the box".

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
0
RMB(double money)
{
    int money1 = int(money * 100);

If money is negative, money1 is too!

    yuan = money1 / 100;
    fen = money1 % 10;
    jiao = (money1 - yuan * 100 - fen)/10;

So will be your members!

    if (money < 0)
        mark = 0;
    else
        mark = 1;

And you store the sign additionally (to be used in operator double):

return mark ? money : -money;

Suppose you should make your members positive instead (and I'd prefer unsigned members then):

RMB(double money)
    : mark(money >= 0)
{
    unsigned int money1 = (int)((mark ? money : -money) * 100);
    // ...
}
Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • Actually ,it improved my code. I just show the main code to express my idea. – Black Bob Mar 29 '17 at 13:53
  • My output in double is `3.57` and `1.1` (as operator << is missing). Shouldn't you have gotten 3y, 5j, 7f in your sample output? With my proposed ctor, I get `3.57` and `-1.11` which I assume is what you intended to get... – Aconcagua Mar 29 '17 at 14:00
  • Problem might persist even with int64_t as parameter type (which per se is a good idea!), by the way, as you still have a sign fault! Rounding errors might be excluded by additionally adding .5 (`money * 100 + .5`), provided you do not use units smaller than fen... – Aconcagua Mar 29 '17 at 14:04
0

I would recommend this read: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

For a more in depth treatment of the subject, I recommend: "What Every Computer Scientist Should Know About Floating-Point Arithmetic"

Llopeth
  • 406
  • 5
  • 11