0

Aside from the obvious expected loss of fractional values, I don't see why this should happen...

Consider the following snippet from a homework assignment used to convert numerical digits to english phrases:

int main() {
    float dollars;

    cout << "Please supply a dollar amount between $0.01 and $9999.99 (without the dollar sign): ";
    while (true) {
        cin >> dollars;

        if (cin.fail() || dollars < 0.01 || dollars > 9999.99) {
            cin.clear();
            cin.ignore();
            cout << "You provided an invalid number. Please try again: ";
        } else {
            // Test code:
            cout << "You entered: " << dollars << endl;
            cout << "Times 100 without cast: " << dollars * 100 << endl;
            cout << "Times 100 with cast: " << (int)(dollars * 100) << endl;
            cout << "Cents: " << ((int)(dollars * 100)) % 100 << endl;
            break;
        }
    }

    printDollarsAsString(dollars);

    return 0;
}

I've noticed that when supplying the value 9999.21, the outputs of the second and third cout statements in the else block differ by 1. For example, here is the output when I run this code:

Please supply a dollar amount between $0.01 and $9999.99 (without the dollar sign): 9999.21
You entered: 9999.21
Times 100 without cast: 999921
Times 100 with cast: 999920
Cents: 20

How can I fix this so that the cents value is retrieved correctly? Is there a different approach I can take?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Mirrana
  • 1,601
  • 6
  • 28
  • 66
  • Possible duplicate of http://stackoverflow.com/questions/1286394/for-loop-in-c-using-double-breaking-out-one-step-early-boundary-value-not-rea – Angelus Mortis Jan 28 '16 at 18:34
  • Do any of these answers help? http://stackoverflow.com/questions/2544394/c-floating-point-to-integer-type-conversions – John Schwartz Jan 28 '16 at 18:36
  • 1
    I would suggest to use an integer type instead of a floating point type. You could store the number of **cents** in the variable instead of the number of **dollars**. Just be careful when reading values out. – e0k Jan 28 '16 at 18:37
  • see [this](http://stackoverflow.com/a/35045719/4342498) about storing currency as a floating point number. – NathanOliver Jan 28 '16 at 18:38
  • also lines like `dollars < 0.01` can cause you problem, when comparing up close value differing by precision, since dollars is float while `0.01` is double -> https://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats-so-dont-compare-them/ – Angelus Mortis Jan 28 '16 at 18:41

4 Answers4

6
        cout << "Times 100 without cast: " << dollars * 100 << endl;
        cout << "Times 100 with cast: " << (int)(dollars * 100) << endl;

When you cast to int you are truncating the number, not rounding. In this case, the .21 cannot be storted exactly by binary floating point, because it actually comes out to a repeating number. The repeating number is slightly less than .21. So when you multiply by 100 you get 999920.9... which truncates to 999920 with the cast.

2

This should fix it:

    cout << "Times 100 without cast: " << dollars * 100 << endl;
    cout << "Times 100 with cast: " << (int)(dollars * 100 + 0.5) << endl;

It is because of rounding.

full code:

   } else {
        // Test code:
        cout << "You entered: " << dollars << endl;
        cout << "Times 100 without cast: " << dollars * 100 << endl;
        cout << "Times 100 with cast: " << (int)(dollars * 100 + 0.5) << endl;
        cout << "Cents: " << ((int)(dollars * 100 + 0.5)) % 100 << endl;
        break;
   }

alternatively:

   } else {
        // Test code:
        cout << "You entered: " << dollars << endl;
        cout << "Times 100 without cast: " << dollars * 100 << endl;
        cout << "Times 100 with cast: " << (int)round(dollars * 100) << endl;
        cout << "Cents: " << ((int)round(dollars * 100)) % 100 << endl;
        break;
   }
Arno Duvenhage
  • 1,910
  • 17
  • 36
1

Just because You've asked about different approach :)

#include <iostream> 
#include <vector>
#include <string>
#include <sstream>

...

      std::string str_dollars;
      char delim = '.';
      std::vector<std::string> elems;

      std::cin >> str_dollars;
      std::stringstream ss(str_dollars);
      std::string item;
      while (std::getline(ss, item, delim)) {
         elems.push_back(item);
      }
      cout << "Cents: " << elems[1] << endl;
StahlRat
  • 1,199
  • 13
  • 25
0

I ended up solving it with the help of round() for float in C++.

This also allows me to accept fractional cents, and I can round it accordingly. The following outputs what I expect:

            cout << "Times 100 without cast: " << round(dollars * 100) << endl;
            cout << "Times 100 with cast: " << (int) round(dollars * 100) << endl;
            cout << "Cents: " << (int) round(dollars * 100) % 100 << endl;
Community
  • 1
  • 1
Mirrana
  • 1,601
  • 6
  • 28
  • 66