12

Is there any way to round numbers in C?

I do not want to use ceil and floor. Is there any other alternative?

I came across this code snippet when I Googled for the answer:

(int)(num < 0 ? (num - 0.5) : (num + 0.5))

The above line always prints the value as 4 even when float num =4.9.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
webgenius
  • 856
  • 3
  • 13
  • 30
  • There are many different types of rounding - which one(s) do you want to use? Please post examples of the desired behaviour. –  Apr 03 '10 at 10:24
  • 3
    I think the problem is somewhere else, that should definitely print 5 for an input of 4.9. – IVlad Apr 03 '10 at 10:41
  • Yes, the conversion of a floating point type to an integer type that can represent a number of the required signedness and magnitude should work simply by truncating the decimals; this code does the ±0.5 to cause this truncation to round the original value away from zero. – Arkku Apr 03 '10 at 10:50
  • 1
    What's wrong with `ceil` and `floor`? Also, see http://stackoverflow.com/questions/485525/round-for-float-in-c. – kennytm Apr 05 '10 at 16:05
  • see http://stackoverflow.com/questions/2205211/rounding-with-static-castint except for replace `static_cast` with `(int)` – Earlz Apr 08 '10 at 14:20
  • The above line does not print anything. There is nothing in that line that can possibly do any printing. Show us how you print things. The problem is likely there. – AnT stands with Russia May 13 '10 at 07:20
  • Subset: specific for integer division: http://stackoverflow.com/questions/2422712/rounding-integer-division-instead-of-truncating | C++ http://stackoverflow.com/questions/485525/round-for-float-in-c – Ciro Santilli OurBigBook.com Mar 18 '17 at 08:16

11 Answers11

14

4.9 + 0.5 is 5.4, which cannot possibly round to 4 unless your compiler is seriously broken.

I just confirmed that the Googled code gives the correct answer for 4.9.

marcelo@macbookpro-1:~$ cat round.c 
#include <stdio.h>

int main() {
    float num = 4.9;
    int n = (int)(num < 0 ? (num - 0.5) : (num + 0.5));
    printf("%d\n", n);
}
marcelo@macbookpro-1:~$ make round && ./round
cc     round.c   -o round
5
marcelo@macbookpro-1:~$
Marcelo Cantos
  • 181,030
  • 38
  • 327
  • 365
  • Hmmm....You're right....The statement can be further simplified to: int n = (num < 0) ? (num - 0.5) : (num + 0.5); I've checked this and it works flawless. Can you please explain how the (num<0) comparison works? I inserted a breakpoint in my IDE and saw that the condition check (num<0) always points to FALSE, which should execute (num + 0.5) always – webgenius Apr 03 '10 at 10:48
  • Not sure what you are asking, but an expression `a ? b : c` evaluates to `b` if `a` is true and to `c` otherwise. In this case the idea is to move the value of `num` away from zero by `0.5` before converting it to int by truncating the decimals. This way the truncation will round to the nearest int (e.g. 0.5 + 0.5 = 1.0 and 0.99 + 0.5 = 1.49 both truncate to 1). – Arkku Apr 03 '10 at 12:11
  • 1
    The comparison is done because the statement is for a general-use-case (where num could be positive or negative), not for the specific case in his example program. If the whole point of the question was, "how do you round 4.9 to 5?" then we could just put `float num = 5.0` and be done with it. – Dan Story Apr 03 '10 at 22:01
  • 2
    This produces 4 with ./gcc -frip-fabric-of-space-time. There's a plugin for emacs that turns this on by default. – Tim Post May 13 '10 at 07:23
  • This methods fails when `num` is outside the range of `int` 2) It would fail for many other `float` except that it uses `double` math. e.g. `(int)(num < 0 ? (num - 0.5f) : (num + 0.5f))` has many failing values when `num + 0.5f` _rounds_ to a `float` result. – chux - Reinstate Monica Aug 05 '16 at 14:39
  • @chux when it's outside `int` range, `float` can't distinguish between whole numbers and fractions anyway. With `double`, you can cast to `int64_t`. – Marcelo Cantos Aug 05 '16 at 23:54
  • You [comment](http://stackoverflow.com/questions/2570934/c-code-to-round-numbers/2570949?noredirect=1#comment64968541_2570949) may apply with on some platforms, but not in C in general. Consider 16-bit `int`. Quite common in embedded predecessors in 2016. – chux - Reinstate Monica Aug 08 '16 at 02:00
  • @chux sure, but I assume people working on weird hardware are already aware enough to figure this out for themselves. – Marcelo Cantos Aug 08 '16 at 06:05
  • Embedded processors deployment in 2016 far exceed other platforms - Billions per years. Being so common is hardly weird. C is very popular there and many entering that field have trouble due to assumptions like `int` is 32-bits. – chux - Reinstate Monica Aug 08 '16 at 13:53
  • @chux then use int32_t. – Marcelo Cantos Aug 08 '16 at 23:16
  • Yes code could use `int32_t`. OP's code though is about `int`, not `int32_t` and friends. Hence my comments relating to `int` and a limitation with `(int)(num < 0 ? (num - 0.5) : (num + 0.5));`. – chux - Reinstate Monica Aug 09 '16 at 01:06
12

To round a float in C, there are 3 <math.h> functions to meet the need. Recommend rintf().

float nearbyintf(float x);

The nearbyint functions round their argument to an integer value in floating-point format, using the current rounding direction and without raising the ‘‘inexact’’ floating point exception. C11dr §7.12.9.3 2

or

float rintf(float x);

The rint functions differ from the nearbyint functions (7.12.9.3) only in that the rint functions may raise the ‘‘inexact’’ floating-point exception if the result differs in value from the argument. C11dr §7.12.9.4 2

or

float roundf(float x);

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 2


Example

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

void rtest(const char *fname, double (*f)(double x), double x) {
  printf("Clear inexact flag       :%s\n", feclearexcept(FE_INEXACT) ? "Fail" : "Success");
  printf("Set round to nearest mode:%s\n", fesetround(FE_TONEAREST)  ? "Fail" : "Success");

  double y = (*f)(x);
  printf("%s(%f) -->  %f\n", fname,x,y);

  printf("Inexact flag             :%s\n", fetestexcept(FE_INEXACT) ? "Inexact" : "Exact");
  puts("");
}

int main(void) {
  double x = 8.5;
  rtest("nearbyint", nearbyint, x);
  rtest("rint", rint, x);
  rtest("round", round, x);
  return 0;
}

Output

Clear inexact flag       :Success
Set round to nearest mode:Success
nearbyint(8.500000) -->  8.000000
Inexact flag             :Exact

Clear inexact flag       :Success
Set round to nearest mode:Success
rint(8.500000) -->  8.000000
Inexact flag             :Inexact

Clear inexact flag       :Success
Set round to nearest mode:Success
round(8.500000) -->  9.000000
Inexact flag             :Exact

What is weak about OP's code?

(int)(num < 0 ? (num - 0.5) : (num + 0.5))
  1. Should num have a value not near the int range, the cast (int) results in undefined behavior.

  2. When num +/- 0.5 results in an inexact answer. This is unlikely here as 0.5 is a double causing the addition to occur at a higher precision than float. When num and 0.5 have the same precision, adding 0.5 to a number may result in numerical rounded answer. (This is not the whole number rounding of OP's post.) Example: the number just less than 0.5 should round to 0 per OP's goal, yet num + 0.5 results in an exact answer between 1.0 and the smallest double just less than 1.0. Since the exact answer is not representable, that sum rounds, typically to 1.0 leading to an incorrect answer. A similar situation occurs with large numbers.


OP's dilemma about "The above line always prints the value as 4 even when float num =4.9." is not explainable as stated. Additional code/information is needed. I suspect OP may have used int num = 4.9;.


// avoid all library calls
// Relies on UINTMAX_MAX >= FLT_MAX_CONTINUOUS_INTEGER - 1
float my_roundf(float x) {
  // Test for large values of x 
  // All of the x values are whole numbers and need no rounding
  #define FLT_MAX_CONTINUOUS_INTEGER  (FLT_RADIX/FLT_EPSILON)
  if (x >= FLT_MAX_CONTINUOUS_INTEGER) return x;
  if (x <= -FLT_MAX_CONTINUOUS_INTEGER) return x;

  // Positive numbers
  // Important: _no_ precision lost in the subtraction
  // This is the key improvement over OP's method
  if (x > 0) {
    float floor_x = (float)(uintmax_t) x;
    if (x - floor_x >= 0.5) floor_x += 1.0f;
    return floor_x;
  }

  if (x < 0) return -my_roundf(-x);
  return x; //  x is 0.0, -0.0 or NaN
}

Tested little - will do so later when I have time.

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

I'm not sure that's such a good idea. That code depends on casts, and I'm fairly sure that the exact truncation is undefined.

float result = (num - floor(num) > 0.5) ? ceil(num) : floor(num);

I'd say that this is a much better way (which is basically what Shiroko posted) since it doesn't depend on any casts.

Puppy
  • 144,682
  • 38
  • 256
  • 465
4

A general solution is to use rint() and set the FLT_ROUNDS rounding mode as appropriate.

Dan Story
  • 9,985
  • 1
  • 23
  • 27
2

I think what you're looking for is: int n = (d - floor(d) > 0.5) ? ceil(d) : floor(d);

Shiroko
  • 1,437
  • 8
  • 12
  • 3
    If the questioner genuinely doesn't want to use ceil and floor, but genuinely is happy to use the built-in conversion to int, then the question can be filed under "quirky interview-style questions which involve an unnatural restriction to illustrate some point fully understood only by the interviewer". Unfortunately that doesn't fit in a tag. – Steve Jessop Apr 03 '10 at 11:43
  • Steve, you are right. This is an question often asked in interviews. – webgenius Apr 03 '10 at 12:14
2

the googled code works correctly. The idea behind it is that you round down when the decimal is less than .5 and round up otherwise. (int) casts the float into a int type which just drops the decimal. If you add .5 to a positive num, you get drop to the next int. If you subtract .5 from a negative it does the same thing.

datdo
  • 143
  • 7
0

You may be able to use fesetround() in fenv.h (introduced in C99). The possible arguments are the macros FE_DOWNWARD, FE_TONEAREST, FE_TOWARDZERO, and FE_UPWARD but note that not all of them are necessarily defined - only the ones supported by the platform/implementation are. Then you can use the various round, rint and nearbyint functions in math.h (also C99). This way you can set the desired rounding behaviour once and call the same function regardless of whether or not the value is positive or negative.

(With e.g. lround you usually need not even set the rounding direction for normal use to usually get what you want.)

Arkku
  • 41,011
  • 10
  • 62
  • 84
0
int round(double x)
{
return x >= 0.0 ? int(x + 0.5) : int(x - int(x-1) + 0.5) + int(x-1);
}

It will be faster, than a version with ceil and floor.

0

Round value x to precision p, where 0 < p < infinite. (f.ex. 0.25, 0.5, 1, 2,…)

float RoundTo(float x, float p)
{
  float y = 1/p;
  return int((x+(1/(y+y)))*y)/y;
}

float RoundUp(float x, float p)
{
  float y = 1/p;
  return int((x+(1/y))*y)/y;
}

float RoundDown(float x, float p)
{
  float y = 1/p;
  return int(x*y)/y;
}
JJussi
  • 1,540
  • 12
  • 12
-1

just add 0.5 to the number and typecast it.. and print it by type casting it in integer.. otherwise you can go with round() inside which just pass the argument as the respective number.

-4

Try moving the brackets on your solution above, so that it reads:

(int)(num < 0) ? (num - 0.5) : (num + 0.5)

Using num as 4.9 it rounds to 5 on my machine.

Ali Lown
  • 2,323
  • 1
  • 18
  • 22
  • 1
    This has no effect other than to possibly raise a warning when you assign this expression to an int. Also, the `(int)` cast is redundant, since `num < 0` already has type `int`. – Marcelo Cantos Apr 03 '10 at 10:34
  • What do you mean this is can't possibly work. This is the same as the answer that has 4 up votes. – Ali Lown Apr 03 '10 at 11:07
  • 2
    I didn't say it can't possible work, I said it has no effect, by which I meant that removing the parentheses as you did doesn't change anything. – Marcelo Cantos Apr 04 '10 at 01:19
  • 1
    Worse than that, by removing the brackets you have changed the type. The original expression had type int, yours has type float. – Recurse Jun 08 '12 at 06:10