0

Hi so I'm struggling to figure out what's wrong with my code. I'm really new at this so bear with me. Comments might be a little messy atm.

#include <stdio.h>

int main(void)
{
    double cost, gstCost, newCost; // initial cost, cost of gst by itself, new cost after gst 
    int loonies, quarters, dimes, nickels, pennies; // used for quantity of coins
    float loonreq, quartreq, dimereq, nickreq, penreq; //  cost required after deduction of the corresponding coin

    printf("Please enter the amount to be paid: $");
    scanf("%lf", &cost); // scanf allows to input an amount as a double ("%lf")

    gstCost = cost * .13 + .005; // .13 for gst and add 0.005 to round up or down
    printf("GST: %.2lf\n", gstCost);

    newCost = cost + gstCost;
    printf("Balance owing: $%.2lf\n", newCost); // original cost + gst cost 

    loonies = newCost; // loonies will output as integer when cost has decimals 
    printf("Loonies required: %d", loonies);
    if (loonies == 0) // == used to compare equality
        loonies = 1; // if loonies = 0, loonreq will be infinite causing code to crash and stop

    loonreq = (float)((int)(100 * newCost) % (100 * loonies)) / 100; // casting int and * 100 on float values for modulo since it can only calculate for integer value
    printf(", balance owing $%.2f\n", loonreq); // %.2f shows a float with 2 decimal places 

    quarters = 100 * loonreq / 25; // 100*loonreq allows the code to find how many quarters by dividing by 25
    printf("Quarters required: %d", quarters);
    if (quarters == 0)
        quarters = 1;

    quartreq = (float)((int)(100 * loonreq) % (int)(100 * (quarters * .25))) / 100;
    printf(", balance owing $%.2f\n", quartreq);

    dimes = 100 * quartreq / 10;
    printf("Dimes required: %d", dimes);
    if (dimes == 0)
        dimes = 1;

    dimereq = (float)((int)(100 * quartreq) % (int)(100 * (dimes * .10))) / 100;
    printf(", balance owing $%.2f\n", dimereq);

    nickels = 100 * dimereq / 5;
    printf("Nickels required: %d", nickels);
    if (nickels == 0)
        nickels = 1;

    nickreq = (float)((int)(100 * dimereq) % (int)(100 * (nickels * .05))) / 100;
    printf(", balance owing $%.2f\n", nickreq);

    pennies = 100 * nickreq / 1;
    printf("Pennies required: %d", pennies);
    if (pennies == 0)
        pennies = 1;

    penreq = (float)((int)(100 * nickreq) % (int)(100 * (pennies * .01))) / 100;
    printf(", balance owing $%.2f\n", penreq);

    return 0;

}

When I run this code on Visual Studio, I get the correct outputs for certain inputs like 8.68 but when I submit this file through PuTTY for my professor, it tests it for me and I get an incorrect balance (a penny off) after dimes deducted. When I put in a value like 76.54, I'm a penny off after the deduction of loonies. Inputting a value like 76.76, I get the correct output. I don't exactly know what's happening. My professor looked at my code and said it's because of type changes? and recommended that I use this method instead, example

dimes = (balance / 100) / 0.1;
balance = (int)(balance) % 10;

I'm not sure how to use this method though.

Edit: I need to use the modulus operator and casting for this assignment. gcc is used when I submit on PuTTy.

asdfalala
  • 11
  • 1
  • 1
    This is a duplicate of the old issue of floating point inaccuracy. Non-integer values are not represented exactly in floating point. For instance, 0.01 cannot be exactly represented. So when converting back to a string, you will sometimes see it round up or down when you don't expect it to. The best solution is to keep everything in integers, i.e. pennies, so that you can perform exact arithmetic. – Tom Karzes Sep 27 '17 at 02:21
  • What? No toonies? And what are these pennies of which you speak? ;) – ikegami Sep 27 '17 at 18:00

3 Answers3

1

Don't use float/double at all for money. Computers are binary and use base 2 math. Not all decimal values (base 10) can be represented precisely in base 2 floating point, causing rounding errors. Do your math with int (or long long) representing pennies and it will be accurate up to the max value of a 32-bit signed int (millions) or 64-bit signed long long (quadrillions).

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • 1
    The actual reason is that many common values (e.g. 1/10, 2/10, 3/10, 4/10, 6/10, 7/10, 8/10, 9/10) are periodic in binary just like 1/3 is in decimal, so it would take infinite storage to store them accurately in a floating point number format, so a relatively close enough is used instead. – ikegami Sep 27 '17 at 05:13
  • @ilegami relatively close == rounding errors. – Mark Tolonen Sep 27 '17 at 06:06
  • Not all numbers can be represented precisely in base 10 floating point either, but that doesn't stop us from using it for money. Your post wasn't so much wrong as it really say much of anything. I just clarified. – ikegami Sep 27 '17 at 17:54
1

The rounding logic is not giving the correct answer. Say we have an input 123. The GST should be 15.99. However your code will give 16.00. To fix this try replacing gstCost = cost * .13 + .005; with gstCost = (round(cost * .13 * 100) / 100);

Mitch
  • 3,342
  • 2
  • 21
  • 31
0

(float)((int)(100 * quartreq) % (int)(100 * (dimes * .10))) / 100;

I'm sure that changing types like float to int and vise versa can lose a some data throught the parsing part.

example: you have a double variable set as 1.2 when you parse it to int it become 1 and not 1.2 and it won't become 1.2 when you parse it back to double.

Do your calculations with the float type and if you want to change data type only parse it in the final result.

xyaz
  • 139
  • 8