Home > Blockchain >  discrepancies in rounding off in C
discrepancies in rounding off in C

Time:12-08

I'm having an issue where when I print a double value it is inconsistently either rounding up or down. I am using the format:

printf("%.2lf", double);

what could be the possible issue here? thanks!

CodePudding user response:

Any decimal numeral in source code or input is converted to the binary format your C implementation uses for double. This changes the value away from the xxx.xx5 value. When it is then printed using %.2lf, it is the changed value that is rounded, not the original. There is no way to avoid this using only the value in a double.

With additional information, such as known limits on how many decimal digits the original numerals had or what values they may take, it might be possible to reconstruct the original value and round that. However, such information is not present in the question.

CodePudding user response:

"fraction is exact at the 3rd decimal point"

As double uses a binary internal representation, most decimal fractions like 0.1, 0.01, 0.001 are not possible to represent exactly. Instead of the exact code value of x.xx5, a double near that is used, perhaps a tad lower or higher.

To recover that exact at the 3rd decimal point, scale the double by 1000.0 and round to nearest whole number.

double d1000 = round(d * 1000.0);

To print to 2 decimal places, round the d1000 to the nearest 10s, round and then divide by 100.0

double d100 = round(d1000 / 10.0) / 100.0;
printf("%.2lf", double);

In effect, code recovers by employing a double rounding.


Example:

#include <stdio.h>
#include <math.h>

void prt(double d) {
  double d1000 = round(d * 1000.0);
  double d100 = round(d1000 / 10.0) / 100.0;
  printf("%.16e %.2lf\n", d, d100);
}

void prt3(double d) {
  prt(nextafter(d, 0));
  prt(d);
  prt(nextafter(d, d*2));
}

int main() {
  prt3(1203.505000);
  prt3(1203.705000);
}

Output

1.2035049999999999e 03 1203.51
1.2035050000000001e 03 1203.51
1.2035050000000003e 03 1203.51
1.2037049999999997e 03 1203.71
1.2037049999999999e 03 1203.71
1.2037050000000002e 03 1203.71

CodePudding user response:

In a comment you wrote

the fraction is exact at the 3rd decimal point

Your original number might have been, but once it's converted to double, which uses binary fractions internally, there's no such thing as a "third decimal point", and in fact decimal numbers like 0.001 and 0.005 simply cannot be represented exactly.

Here are some sample numbers, how they're represented as a double, and how they print as "%.2lf, that is, converted back to decimal and rounded to two places.

(Note that when I say "how they're represented as a double", what I'm really showing you is the internal binary representation converted back to decimal again. It's also interesting to look at the actual internal representation, in binary or hexadecimal, but it's harder to display meaningfully and doesn't really bear on today's question, so I'm not going to bother.)

input value internal double value printed "%.2lf"
1203.480 1203.4800000000000181899 1203.48
1203.485 1203.4849999999998999556 1203.48
1203.490 1203.4900000000000090949 1203.49
1203.495 1203.4949999999998908606 1203.49
1203.500 1203.5000000000000000000 1203.50
1203.505 1203.5050000000001091394 1203.51
1203.510 1203.5099999999999909051 1203.51
1203.515 1203.5150000000001000444 1203.52
1203.520 1203.5199999999999818101 1203.52
1203.525 1203.5250000000000909495 1203.53
1203.530 1203.5299999999999727152 1203.53

You can see that sometimes .xx5 is represented as .xx50000000001 and so rounds up as you expect, but sometimes it's represented as .xx49999999999 and so rounds down.

You asked "what could be the possible issue here", and that's what I've explained. Other answers contain suggestions on how you could adjust your code to work around the issue.

  • Related