Mirrana
Mirrana

Reputation: 1731

Losing data when casting float to int in c++

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?

Upvotes: 0

Views: 1633

Answers (4)

Sergio
Sergio

Reputation: 1299

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;

Upvotes: 1

Mirrana
Mirrana

Reputation: 1731

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;

Upvotes: 0

Arno Duvenhage
Arno Duvenhage

Reputation: 1960

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

Upvotes: 2

user5853538
user5853538

Reputation:

        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.

Upvotes: 6

Related Questions