简体   繁体   中英

C++ floating point accuracy in while loops

I am trying to count the amount of dollar and coin denominations in a grand total by using a series of while loops. When I get down to the coins however, I am off by a penny. When I enter say 99.95, I get the output 3 quarters, 1 dime, 1 nickel, and 4 pennies. I've narrowed the problem down to a floating point accuracy issue. However all the solutions I've researched haven't been applicable in my situation. Any pointers?

#include <iostream>
using namespace std;

int main()

{
   float amount;
   cout<<"enter amount" << endl;
   cin>>amount;
   int pennies=0, nickels=0, dimes=0, quarters=0, ones=0, fives=0, 
tens=0, 
twenties=0, fifties=0, hundreds=0;

   while (amount >= 100) 
   {
      hundreds = hundreds +1;
      amount = amount - 100;

   }
   while (amount >= 50)
   {
      fifties = fifties +1;
      amount = amount - 50;

   }
   while (amount >= 20)
   {
      twenties = twenties +1;
      amount = amount - 20;

   }
   while (amount >= 10)
   {
      tens = tens +1;
      amount = amount - 10;

   }
   while (amount >= 5)
   {
      fives = fives +1;
      amount = amount - 5;

   }
   while (amount >= 1)
   {
      ones = ones +1;
      amount = amount - 1;

   }
   while (amount >= .25)
   {
      quarters = quarters +1;
      amount = amount - .25;

   }
   while (amount >= .10)
   {
      dimes = dimes +1;
      amount = amount - .10;

   }
   while (amount >= .05)
   {
      nickels = nickels +1;
      amount = amount - .05;

   }
   while (amount >= .01)
   {
      pennies = pennies +1;
      amount = amount - .01;

   }


   cout<<endl<<"pennies:"<< pennies;
   cout<<endl<<"nickels:"<<nickels;
   cout<<endl<<"dimes:"<<dimes;
   cout<<endl<<"quarters:"<<quarters;
   cout<<endl<<"ones:"<<ones;
   cout<<endl<<"fives:"<<fives;
   cout<<endl<<"tens:"<<tens;
   cout<<endl<<"twenties:"<<twenties;
   cout<<endl<<"fifties:"<<fifties;
   cout<<endl<<"hundreds:"<<hundreds<<endl;







return 0;
}

Don't use floating point in cases where you need exact values. 99.95 can't be exactly represented in a float or double, a bit like 1/3 can't be exactly represented using a finite number of normal decimal digits.

As Doug T. suggested, you can use an integer to hold the number of pennies. When the user types 123.45, read it as two integers, and then store it as 12345 pennies, not as 123.45 dollars.

In this case, you can also try to change your last while (amount >= .01) to something like while (amount >= .005) . It's not a solution that can be generally recommended, and if this is a real-life bank application you really should avoid it, but it will help against at least some of the errors.

You should be using fixed-point values in this case, not floating-point.

Although people think of money in terms of dollars, which aren't exact, money is measured in cents, which are exact. Just count the number of cents, divide by 100 to get the dollar amount, and take the modulus to get the cent amount. Floating-point is not the proper tool in this case.

Binary floating point numbers cannot, in general, represent fractional decimal values, even if the total number of decimals is small. For example, 0.1 cannot be represented exactly.

To deal with exact values different approaches exist which all amount to using a different base than 2 . Depending on tge specific needs the approaches are more or less involved. The easieast approach for your case is to use a fixed precision instead of a variable precision. To compute with fixed precision you'd just use an integer which represents the value you actually want multiplied by a suitable power of 10 . Depending on the amount of computations you do you can either package the logic into a class or distribute it throughout the code. In a "real" application I'd package it into a class, in an assignment (homework, interview, etc.) I'd probably just apply the logic directly to an int .

Here's the fixed code, I used TJD's advice and instead of using .01 i used .0099 instead. For my problem that seems to work, thanks guys!

    #include <iostream>
using namespace std;

int main()

{
   float amount;
   cout<<"enter amount" << endl;
   cin>>amount;
   int pennies=0, nickels=0, dimes=0, quarters=0, ones=0, fives=0, 
tens=0, 
twenties=0, fifties=0, hundreds=0;
   float p = .0099, n = .0499, d = .099, q = .2499;   
   while (amount >= 100) 
   {
      hundreds = hundreds +1;
      amount = amount - 100;

   }
   while (amount >= 50)
   {
      fifties = fifties +1;
      amount = amount - 50;

   }
   while (amount >= 20)
   {
      twenties = twenties +1;
      amount = amount - 20;

   }
   while (amount >= 10)
   {
      tens = tens +1;
      amount = amount - 10;

   }
   while (amount >= 5)
   {
      fives = fives +1;
      amount = amount - 5;

   }
   while (amount >= 1)
   {
      ones = ones +1;
      amount = amount - 1;

   }
   while (amount >= q)
   {
      quarters = quarters +1;
      amount = amount - q;

   }
   while (amount >= d)
   {
      dimes = dimes +1;
      amount = amount - d;

   }
   while (amount >= n)
   {
      nickels = nickels +1;
      amount = amount - n;

   }
   while (amount >= p)
   {
      pennies = pennies +1;
      amount = amount - p;

   }


   cout<<endl<<"pennies:"<< pennies;
   cout<<endl<<"nickels:"<<nickels;
   cout<<endl<<"dimes:"<<dimes;
   cout<<endl<<"quarters:"<<quarters;
   cout<<endl<<"ones:"<<ones;
   cout<<endl<<"fives:"<<fives;
   cout<<endl<<"tens:"<<tens;
   cout<<endl<<"twenties:"<<twenties;
   cout<<endl<<"fifties:"<<fifties;
   cout<<endl<<"hundreds:"<<hundreds<<endl;







return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM