6

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.

  • `x = round(100.0*x) / 100.;` – BLUEPIXY Aug 03 '14 at 13:18
  • 1
    Use some imagination. Rounding to the hundreths place is the same as rounding to an integer 100 times larger. – Hot Licks Aug 03 '14 at 13:28
  • 5
    Note that the `double` you wrote as `2.555` is not “half” (its value is 2.555000000000000159872115546022541821002960205078125), so the rule “half-up” will not apply to it. As long as you are rounding to the nearest value-with-two-decimal-places, the result will be “2.56”. It does not matter what rule you apply to midpoint values. – Pascal Cuoq Aug 03 '14 at 13:28
  • @alk Do not agree this is a duplicate of [Rounding Number to 2 Decimal Places in C](http://stackoverflow.com/questions/1343890/rounding-number-to-2-decimal-places-in-c). In that post, OP wants to round using "rounding to nearest, rounding ties is unspecified". Here, OP explicitly want to "round to nearest, rounding ties away from 0". The are subtle differences both mathematically and programmability, thus recommend re-opening. – chux - Reinstate Monica Aug 03 '14 at 15:47
  • 2
    You do realize that if you're using C doubles then there are few exactly-representable decimal halfway cases? For example, you say that you want `2.565` to round up, but on a typical machine, if you write `double x = 2.565;` then since `2.565` can't be represented exactly in a binary floating-point format, the actual value stored for `x` is `2.564999999999999946709294817992486059665679931640625`. So a correct solution to your problem should round down for this case, not up. If you want it to round up, you need to explain why, and specify the precise behaviour that you want in general. – Mark Dickinson Aug 03 '14 at 16:46
  • 1
    Your integer "rounding half-up", may not do what you want for -ve values. If `b` = -2.5; `a` = 2 rather than 3. You need to specify rounding in terms of up, down, toward zero or away from zero. – Clifford Aug 03 '14 at 19:47
  • Could simple change the rounding mode and then call `printf()`. – chux - Reinstate Monica Aug 04 '14 at 04:08
  • @chux That is what I think I should go after, but it beats me how to do that in C. – Truthseeker Rangwan Aug 04 '14 at 04:10
  • @TruthseekerRangwan: Can you clarify what you want to happen for `2.565`, and why? – Mark Dickinson Aug 04 '14 at 09:06
  • @MarkDickinson I've been practicing the programming contest and one of the problem tell me that I have to round the number to 2 decimal point "half-up". – Truthseeker Rangwan Aug 04 '14 at 09:46
  • 1
    @TruthseekerRangwan: Right, but your question as it stands is inconsistent - you can't have round-ties-away-from-zero *and* have the double closest to `2.565` round up. One of those has to change - either you want do-what-i-mean-round-ties-kinda-sorta-away-from-zero, or you accept that `2.565` will round down. – Mark Dickinson Aug 04 '14 at 09:59
  • @MarkDickinson I edit the problem, since making it works only with positive value is just fine. – Truthseeker Rangwan Aug 04 '14 at 10:03
  • Try `printf("%.2lf", nextafter(variable,variable*2));` – chux - Reinstate Monica Aug 04 '14 at 14:50
  • @chux : Post that as an answer and I'd vote for it - even if it is perhaps a more sophisticated and succinct development of my own answer. All other answers so far seem absurdly complex - including your own ;-) – Clifford Aug 04 '14 at 17:00
  • @Clifford [Done](http://stackoverflow.com/questions/25104883/round-positive-value-half-up-to-2-decimal-places-in-c/25107727#25107727). I concur the other http://stackoverflow.com/questions/19991623/why-is-malloc-not-using-up-the-memory-on-my-computer/19991656#19991656 is complex, but is a correct way to round a `double` into another `double` closest to a multiple of 0.01, as best as possible. Here OP is slightly cheating, wanting "2.565", which is really `2.564999999999999947`, to round to "2.57". So we have supplied a cheat, by nudging in some fashion. – chux - Reinstate Monica Aug 04 '14 at 18:20
  • could you just bias everything up by the FLT_EPSILON or FLT_MIN – Grady Player Aug 04 '14 at 18:27
  • @Grady Player, IMO, no, cannot "bias everything up by the FLT_EPSILON or FLT_MIN". Adding `FLT_MIN` to `float x` would still result in `x` for OP's sample numbers and most `float` numbers. Adding `FLT_EPSILON` to numbers larger than 2 (or maybe 4) would also have no effect. – chux - Reinstate Monica Aug 04 '14 at 18:39
  • @chux you are right, that would be too small if the exponent is anything, what about adding .001 – Grady Player Aug 04 '14 at 19:00
  • @Grady Player Adding `0.001` to a value like `2.004` would have a sum of `2.005` and that may print "2.01" rather than "2.00". IMO doing a simply `printf("%.2lf",variable);` should suffice. – chux - Reinstate Monica Aug 04 '14 at 19:10
  • @GradyPlayer : If you look at the edit history to my answer you will see that is what I had originally, but chux pointed out the flaw in that. – Clifford Aug 04 '14 at 20:42

3 Answers3

2

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
Clifford
  • 88,407
  • 13
  • 85
  • 165
  • 1
    It may be "conventional" to round toward zero, but the OP is correct in observing that the normal behaviour for C's `*printf` functions is to round ties to even. You can easily test this on your own machine by testing actual exactly-representable halfway cases like `0.125`, `1.375`, etc. I don't think it's actually enforced by the language specification: the closest the spec gets is (for C99) Annex F, section 5, para 2, which says that the current rounding mode (which will usually be round-ties-to-even) should be honoured, but it's certainly common in practice. – Mark Dickinson Aug 03 '14 at 19:48
  • @MarkDickinson : The point you refer to was an aside to the actual answer; it seems I may be on shaky ground. To avoid discussion irrelevant to the question, I have removed the paragraph. – Clifford Aug 03 '14 at 20:17
  • With FP values near a multiple of 0.001 this works, but fails with values like `2.5449` with prints "2.55". Perhaps a nudge of `double x2 = x*(1+DBL_EPSILON);`? – chux - Reinstate Monica Aug 03 '14 at 23:12
  • @chux : Your suggestion is better - it works for `2.54499999999999` which has 15 significant figures which is the guranteed decimal precision of `double`. It is also a neater way of dealing with -ve values. – Clifford Aug 04 '14 at 12:11
  • 1
    Another thought: A new-ish C way to nudge `x` to the next largest floating-point number would be to use the `nextafter(x, 2*x)`. – chux - Reinstate Monica Aug 04 '14 at 14:44
  • @chux : Nice. It is available in VC++ since VS2012. Visual C++ generally only supports C99 features that have been adopted by C++. I'd recommend that where C99 or C++11 is supported. – Clifford Aug 04 '14 at 16:53
1

[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).

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Aside from the floating-point issues, one problem with the `round` solution is that MSVC doesn't provide the `round` function. (Or at least, it didn't last time I looked.) It's typically not a problem on most Unix-y platforms, though. – Mark Dickinson Aug 03 '14 at 19:26
  • Ah, I take that back. It looks like it *is* there in Visual Studio 2013: http://msdn.microsoft.com/en-us/library/dn353646.aspx. Not sure which version added it; it looks as though it might be new in VS 2013. – Mark Dickinson Aug 03 '14 at 19:29
  • @MarkDickinson : VC++ [round()](http://msdn.microsoft.com/en-us/library/hh537992.aspx) - since VC++ 2012 as it is now defined by C++11 as well as C99. My VS2013 link has an "other versions" link - not sure why yours does not - that's MSDN for you - generally unnavigable! – Clifford Aug 04 '14 at 20:53
1

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;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256