Round positive value half-up to 2 decimal places in C

扶醉桌前 提交于 2019-12-22 05:19:09

问题


Typically, Rounding to 2 decimal places is very easy with

printf("%.2lf",<variable>);

However, the rounding system will usually rounds to the nearest even. For example,

2.554 -> 2.55
2.555 -> 2.56
2.565 -> 2.56
2.566 -> 2.57

And what I want to achieve is that

2.555 -> 2.56
2.565 -> 2.57

In fact, rounding half-up is doable in C, but for Integer only;

int a = (int)(b+0.5)

So, I'm asking for how to do the same thing as above with 2 decimal places on positive values instead of Integer to achieve what I said earlier for printing.


回答1:


It is not clear whether you actually want to "round half-up", or rather "round half away from zero", which requires different treatment for negative values.

Single precision binary float is precise to at least 6 decimal places, and 20 for double, so nudging a FP value by DBL_EPSILON (defined in float.h) will cause a round-up to the next 100th by printf( "%.2lf", x ) for n.nn5 values. without affecting the displayed value for values not n.nn5

double x2  = x * (1 + DBL_EPSILON) ; // round half-away from zero
printf( "%.2lf", x2 ) ;

For different rounding behaviours:

double x2  = x * (1 - DBL_EPSILON) ;  // round half-toward zero
double x2  = x + DBL_EPSILON ;        // round half-up
double x2  = x - DBL_EPSILON ;        // round half-down



回答2:


Following is precise code to round a double to the nearest 0.01 double.

The code functions like x = round(100.0*x)/100.0; except it handles uses manipulations to insure scaling by 100.0 is done exactly without precision loss.

Likely this is more code than OP is interested, but it does work.

It works for the entire double range -DBL_MAX to DBL_MAX. (still should do more unit testing).
It depends on FLT_RADIX == 2, which is common.

#include <float.h>
#include <math.h>

void r100_best(const char *s) {
  double x;
  sscanf(s, "%lf", &x);

  // Break x into whole number and fractional parts.  
  // Code only needs to round the fractional part.  
  // This preserves the entire `double` range.
  double xi, xf;
  xf = modf(x, &xi);

  // Multiply the fractional part by N (256). 
  // Break into whole and fractional parts.
  // This provides the needed extended precision.
  // N should be >= 100 and a power of 2.
  // The multiplication by a power of 2 will not introduce any rounding.
  double xfi, xff;
  xff = modf(xf * 256, &xfi);

  // Multiply both parts by 100.  
  // *100 incurs 7 more bits of precision of which the preceding code
  //   insures the 8 LSbit of xfi, xff are zero.
  int xfi100, xff100;
  xfi100 = (int) (xfi * 100.0);
  xff100 = (int) (xff * 100.0); // Cast here will truncate (towards 0)

  // sum the 2 parts.  
  // sum is the exact truncate-toward-0 version of xf*256*100
  int sum = xfi100 + xff100;
  // add in half N
  if (sum < 0)
    sum -= 128;
  else
    sum += 128;

  xf = sum / 256;
  xf /= 100;
  double y = xi + xf;
  printf("%6s %25.22f ", "x", x);
  printf("%6s %25.22f %.2f\n", "y", y, y);
}

int main(void) {
  r100_best("1.105");
  r100_best("1.115");
  r100_best("1.125");
  r100_best("1.135");
  r100_best("1.145");
  r100_best("1.155");
  r100_best("1.165");
  return 0;
}



回答3:


[Edit] OP clarified that only the printed value needs rounding to 2 decimal places.

OP's observation that rounding of numbers "half-way" per a "round to even" or "round away from zero" is misleading. Of 100 "half-way" numbers like 0.005, 0.015, 0.025, ... 0.995, only 4 are typically exactly "half-way": 0.125, 0.375, 0.625, 0.875. This is because floating-point number format use base-2 and numbers like 2.565 cannot be exactly represented.

Instead, sample numbers like 2.565 have as the closest double value of 2.564999999999999947... assuming binary64. Rounding that number to nearest 0.01 should be 2.56 rather than 2.57 as desired by OP.

Thus only numbers ending with 0.125 and 0.625 area exactly half-way and round down rather than up as desired by OP. Suggest to accept that and use:

printf("%.2lf",variable);  // This should be sufficient

To get close to OP's goal, numbers could be A) tested against ending with 0.125 or 0.625 or B) increased slightly. The smallest increase would be

#include <math.h>
printf("%.2f", nextafter(x, 2*x));

Another nudge method is found with @Clifford.


[Former answer that rounds a double to the nearest double multiple of 0.01]

Typical floating-point uses formats like binary64 which employs base-2. "Rounding to nearest mathmatical 0.01 and ties away from 0.0" is challenging.

As @Pascal Cuoq mentions, floating point numbers like 2.555 typically are only near 2.555 and have a more precise value like 2.555000000000000159872... which is not half way.

@BLUEPIXY solution below is best and practical.

x = round(100.0*x)/100.0;

"The round functions round their argument to the nearest integer value in floating-point format, rounding halfway cases away from zero, regardless of the current rounding direction." C11dr §7.12.9.6.

The ((int)(100 * (x + 0.005)) / 100.0) approach has 2 problems: it may round in the wrong direction for negative numbers (OP did not specify) and integers typically have a much smaller range (INT_MIN to INT_MAX) that double.


There are still some cases when like when double x = atof("1.115"); which end up near 1.12 when it really should be 1.11 because 1.115, as a double is really closer to 1.11 and not "half-way".

string   x                         rounded x 
1.115 1.1149999999999999911182e+00 1.1200000000000001065814e+00

OP has not specified rounding of negative numbers, assuming y = -f(-x).



来源:https://stackoverflow.com/questions/25104883/round-positive-value-half-up-to-2-decimal-places-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!