0
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int main(void)
{
    const double TAX = 0.13;
    double small = 17.96;

    double subTotal = small * 6;
    double taxes = subTotal * TAX;

    printf("j   SubTotal: %9.4lf Tax: %9.4lf \n", (double)subTotal, (double)taxes);

  return 0;
} 

The required output is

SubTotal: 107.7600 Tax: 14.0100 should come out. 

My output is:

SubTotal: 107.7600 Tax: 14.0088

What should I do?

Clifford
  • 88,407
  • 13
  • 85
  • 165
Chloe
  • 1
  • 4
  • 2
    Does this answer your question? [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – kaylum Jan 31 '22 at 03:05
  • 1
    Would `14.01` instead of `14.0100` be acceptable? (It usually makes more sense, and that makes it easier to do in code.) – Ry- Jan 31 '22 at 03:07
  • 1
    14.0100 must be printed out – Chloe Jan 31 '22 at 03:11
  • @kaylum: Where in that question and its answers do you think it answers the question here of how to round up? – Eric Postpischil Jan 31 '22 at 10:48
  • why should it output `14.0100` if the product is exactly `14.0088` ?? Don't ask for 4 decimals if you want only two, just `printf` using `%9.2lf` instead. – Luis Colorado Feb 02 '22 at 09:11

6 Answers6

3

The solution is to not ever use floating point types for currency.

If you have a decimal type available, use it.

If you don't, write one.

Or, at a minimum, use long integers to store cents.

Chris
  • 26,361
  • 5
  • 21
  • 42
Jeff Dege
  • 11,190
  • 22
  • 96
  • 165
  • 1
    Seen at the gas station: Regular 495 ⁹⁄₁₀. I am afraid cents do not provide fine enough granularity. Other than that, sound advice. – njuffa Jan 31 '22 at 03:31
  • The needed granularity varies across domains, true. – Jeff Dege Jan 31 '22 at 03:37
  • 2
    Re “The solution is to not ever use floating point types for currency”: Floating-point for currency should be used with Black-Scholes options evaluation and other financial modeling. – Eric Postpischil Jan 31 '22 at 10:50
  • i'm afraid the problem here is not of floating point precision or how floating point works in computers. The answer is _exactly_ `14.0088` and he has asked for four decimal figures. Why should the computer change it into only two? Anyway, you don't speak at all, about the rounding of cents (that you should do in the above case, working with `long`s) – Luis Colorado Feb 02 '22 at 09:14
1

If you want to round to two decimal places, multiply by one hundred add one half, truncate to an int and then divide by one hundred. Like,

double round(double v) {
    return ((int)(v * 100 + .5)) / 100.;
}

Then you can call that like

double taxes = round(subTotal * TAX);

And I get (with no other changes)

j   SubTotal:  107.7600 Tax:   14.0100
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • 1
    You could extend this to have `round` take an int indicating the number of places, and instead of hard-coding `100` use `10` raised to that exponent. – Chris Jan 31 '22 at 03:15
  • `(int)(v * 100 + .5)` can fail to round correctly when 1) `v *100` out of `int` range or not finite 2) `v *100` is not exact and product near xxx.5, 3) `v < 0`. Unclear why use the `+ 0.5` and cast when the standard library provides at least 8 round functions as reasonable alternatives. – chux - Reinstate Monica Feb 01 '22 at 13:48
1

C provides useful rounding functions rint(), round(), nearby(), llround() that well handle rounding corner cases.

To round to the nearest 100th, scale the value by /100.0, round and scale back.

#include <math.h>

double value = ...;
value *= 100.0;
value = round(value); // or rint(), nearby()
value /= 100.0;

At this point, value may not exactly be of the form ddd.dd but will be the closest double to that form. Printing with "%.4f" will then print to the closest 0.0001.

printf("%9.4lf\n", value);

Alternatively, take money and round to the smallest monetary unit - suppose OP wants to the nearest cent (0.01).

long long value_cents = llround(value * 100.0);

printf("%lld.%02d00", amount / 100, abs(amount % 100));

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

It’s a weird requirement to display four decimal places while rounding to two, but you can satisfy it by displaying two decimal places and hard-coding the characters 00 after it:

printf("j   SubTotal: %9.2f00 Tax: %9.2f00 \n", subTotal, taxes);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
Ry-
  • 218,210
  • 55
  • 464
  • 476
0

Do not use floating point numbers for currency because they are tricky and inexact in many cases. Here is a working solution that gives you the exact output you want, but you should know it is strange to print more than two digits after the decimal point if those extra digits are always zero.

The + 99 in the code below is the trick you need to make the calculation always round up, instead of rounding down which is the normal behavior of unsigned integer division.

#include <stdio.h>
#include <stdint.h>

void print_money_with_strange_format(uint64_t amount)
{
  printf("%llu.%02llu00", amount / 100, amount % 100);
}

int main(void)
{
  uint64_t tax_rate = 13;
  uint64_t small = 1796;
  uint64_t subtotal = small * 6;
  uint64_t tax = (subtotal * tax_rate + 99) / 100;

  printf("SubTotal: ");
  print_money_with_strange_format(subtotal);
  printf(" Tax: ");
  print_money_with_strange_format(tax);
  printf("\n");
}
David Grayson
  • 84,103
  • 24
  • 152
  • 189
0

If you want to round the output to two decimals, but still producing four on output, just use

    printf("%9.2f00", value);

and the value will be rounded to two decimals, and two zeros will be appended to it. But, why the computer should round 88 to 100 in a calculation, when you have not commanded it to round numbers at some place?

To round a number when the first decimal is over .5, you can just add 0.5 and then truncate the result. In case you want to do it at some position, just multiply the number by the required power of ten (in this case 100) and after the truncation, divide it again by that power(100), as proposed by other answers.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31