3

In my code I usede the greedy algorithm in order to use the minimum amaount of coins. For example: I must return $0.41, the minimum amount of coins I can use is 4:

1 - 0,25;
1 - 0.10;
1 - 0.05;
1 - 0.01;

There are 4 types of coins: 0.25,0.10,0.05,0.01.

Here is my code:

#include <stdio.h>
#include <cs50.h>

int main(void)
{
    printf("Enter the sum, that you want to return you:");
    float sum = GetFloat();
    float quaters = 0.25;
    float dime = 0.10;
    float nickel = 0.05;
    float penny = 0.01;
    int count_q = 0,count_d = 0,count_n = 0,count_p = 0;


    while(sum<0){
        printf("Incorrect, enter the positive float number");
        sum = GetFloat();
    }

    while(sum > 0){
        if(sum - quaters >=0){
            sum -=quaters;
            count_q +=1;
        }
        else if((sum -quaters <0) && (sum -dime>=0)){
            sum -= dime;
            count_d +=1;
        }
        else if((sum - dime <0) &&(sum - nickel>=0) ){
            sum -= nickel;
            count_n +=1;
        }
        else if(sum - nickel <0){
            sum -= penny;
            count_p +=1;
        }
    }
    printf("The number of quaters: %i\n",count_q);
    printf("The number of dimes: %i\n",count_d);
    printf("The number of nickels: %i\n",count_n);
    printf("The number of pennies: %i\n",count_p);
}

This code calculates how many coins of each type of was used to return the sum. In most cases it works fine.

But sometimes, for example, when i enter the number 1.12 it gives me wrong result:

Enter the sum, that you want to return you:1.12
The number of quaters: 4
The number of dimes: 1
The number of nickels: 0
The number of pennies: 3

I think, that the problem is in last else if statement. But i don't know how can I correct it.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Daniel Yefimov
  • 860
  • 1
  • 10
  • 24
  • 6
    Probably a rounding issue. Use integers instead of floats (i.e. 112 instead of 1.12). – Ruud Helderman Sep 01 '16 at 14:27
  • 1
    by the way, using `int`s you need not a loop: `count_q = sum / quarter; sum %= quarter; count_d = sum / dime; sum %= dime;...` – Serge Sep 01 '16 at 14:32

2 Answers2

8

To my understanding, there is no bug in your code in the strictest sense, as the reasoning on which the implementation is based (a greedy algorithm) is correct. You are most likely experiencing rounding errors due to repeated subtraction, as you use float, the single-precision floating type to represent your values. Perhaps, if you change float to double in your code, the output will be as expected for your example input.

However, this only pushes the boundaries of the limitation. Perhaps it would be better to internally represent the amount of money in pennies as int.

Note that, when first confronted with the fact that floating point representations are inaccurate, I believed that the impossibility to represent some values and accumulation of rounding errors would be an issue only when you absolutely do some rocket science calculations, but would never be relevant for what I considered to be layman's calculations. However, this is not the case.

Codor
  • 17,447
  • 9
  • 29
  • 56
  • 1
    `int` representation of money have its short-comings too: UB on overflow, trouble with `pow()` (used in interest), limited range and calculations rounding using a truncate toward zero rather than round to nearest. Certain `int` works fine counting change and for learning. – chux - Reinstate Monica Sep 01 '16 at 18:51
  • 1
    Yes indeed, thanks for providing more context. Certainly using `int` is not _ultimately_ solving all problems of representing money. – Codor Sep 01 '16 at 19:11
1

Following up on what others have said, this will probably do the job: replace the variable declarations in your existing code with the below. The computation loop doesn't need to change since you wisely used named quantities rather than hard-coded constants.

float dollars = GetFloat();
int sum = (int)(dollars*100.0 + 0.5);
int quaters = 25;
int dime = 10;
int nickel = 5;
int penny = 1;

Edit:

The changes above have to be carried through wherever input happens. For example:

while(dollars<0){                      /***/
    printf("Incorrect, enter the positive float number");
    dollars = GetFloat();              /***/
    sum = (int)(dollars*100.0 + 0.5);  /***/
}
printf("%d pennies\n", sum);           /* For debugging */

I added the +0.5 to round-to-nearest rather than truncating - that might fix the 1.13 and 1.14 cases. If not, I would suggest seeing what your debugger tells you. If you are still stuck after that, please by all means post another question with your latest, updated code and test cases.

cxw
  • 16,685
  • 2
  • 45
  • 81
  • It doesn't work: 1)It gives wrong result in, for example, 1.13 and 1.14 2)When i enter, for example, the negative number, the algorithm fails completely – Daniel Yefimov Sep 01 '16 at 15:13
  • It works perfectly! Thank you. Just one more question: In general, is your approach safe? Or it could be unstable? – Daniel Yefimov Sep 01 '16 at 15:50
  • @Dan_yef Glad to hear it! What do you mean by "safe" and "unstable"? I don't know a simpler way to do the same job, if that's what you mean. `(int)(x+0.5)` is the standard way to round-to-nearest while converting to `int` in C, to the best of my knowledge (although I haven't personally used it with negative numbers). – cxw Sep 01 '16 at 16:05
  • Rather than use `(int)(dollars*100.0 + 0.5)` which fails to round correctly for many corner cases, the standard library provides well written rounding functions. Suggest `sum = lround(dollars*100.0)`. – chux - Reinstate Monica Sep 01 '16 at 19:22