Rounding float to nearest integer in C

I may have a noob question here, but searching the site has stuck nothing together. I am learning to program in C and I am trying to build a function from scratch that the rounds float to the nearest integer without using math.h. Here's my code:

void main()
{
    float b;
    for(b = 0; b <= 2; b = b + 0.1)
    {
        printf("%f    ", b);
        printf("%i    ", (int)b);
        printf("%f    ", b - (int)b);
        printf("Nearest: ");
        if((b - (int)b)<0.5)
            printf("%i    ", (int)b);
        else
            printf("%i    ", (int)b + 1);
        printf("Function: %i    ", round_near(b));
        printf("\n");
    }
    getchar();
}

int round_near(float b)
{
    if((b - (int)b)<0.5)
        return(int)b;
    else
        return (int)b + 1;
}

      

My results look like this:

enter image description here

Some of the code is superfluous and is only intended to show you some of the individual steps in my function. What gives? Is there some kind of float manipulation that I am not aware of?

+3


source to share


5 answers


You don't have a prototype for int round_near(float b)

, so you rely on implicit declarations.

Try adding this to your code.

int round_near (float b); // Prototype

int main(void) // Nitpick: main returns an int!

      



The use of implicit declarations for round_near(b)

is b

promoted to double. But the definition assumes it is a float which has a different binary layout, so you get crazy random results.

You need to make sure your code compiles without any warnings to avoid this kind of thing. The only reason implicit declaration is in the language is for backward compatibility, but every compiler in the last decade or two has warned you that this is bad at compilation.

+6


source


When I tried to compile this under gcc I got the following error:

/tmp/x1.c:23: error: conflicting types for ‘round_near’
/tmp/x1.c:23: note: an argument type that has a default promotion can’t match an empty parameter name list declaration
/tmp/x1.c:16: error: previous implicit declaration of ‘round_near’ was here

      



The funny results you get are that your compiler didn't know the definition round_near

at the time of its first encounter and assumed it was int round_near()

. So this resulted in undefined behavior.

If you move round_near

above the main or place your ad above the main, you should get the expected results.

+2


source


@QuestionC answered well the OP's immediate operation problem: the implied function signature is int round_near(...)

incompatible with int round_near(float b)

and invoking round_near(b)

that passes b

as a double

.

Easy solution: function prototype.

Some questions about round_near()

  • Casting to int

    severely narrows the legal range. Better to use long long

    .

  • Common wrong functions with negative numbers. @Eugene Sh. The code should be checked for the presence of a sign.

Below is a solution that uses a range long long

as it is usually larger than a continuous range of integers a float

. Alternatively OP can replace my_roundf()

with round_near()

and use this code for testing. round_near()

doesn't work about 40% of the time.

#include <limits.h>
#include <stdio.h>

float my_roundf(float x) {
  // Large `float`s typically have no fractional portion to round
  if (x > LLONG_MAX / 2) return x;
  if (x < LLONG_MIN / 2) return x;
  return x > 0 ? (long long) (x + 0.5f) : (long long) (x - 0.5f);
}

float rand_float(void) {
  union {
    unsigned char uc[sizeof(float)];
    float f;
  } u;
  do {
    unsigned i;
    for (i = 0; i < sizeof(float); i++) {
      u.uc[i] = rand();
    }
  } while (u.f != u.f);  // re-do if NaN encountered
  return u.f;
}

void my_roundf_test(void) {
  unsigned n = 100000;
  while (n-- > 0) {
    float x = rand_float();
    float ymath = roundf(x);
    // float ymy = round_near(x);
    float ymy = my_roundf(x);
    // Exact half-way cases may fail
    if (ymath != ymy) {
      printf("x:% .9e math:% .9e my:% .9e\n", x, ymath, ymy);
    }
  }
}

      

Note. For a complete answer, there are exact half cycle cases for different floating point rounding modes, negative zero, etc. But leave it for another day.

+2


source


Simple ( float

won't match the type int

, so long long

)

long long round(float a) {
    long long b = a;
    if (a >= 0)
        return a - b < 0.5 ? b : b + 1;
    else
        return b - a < 0.5 ? b : b - 1;
}

      

0


source


Output like -241 ... instead of 1 or 2 usually denotes uninitialized integers ... However, your code is compiled with the GNU C compiler (gcc) on Linux only after moving a function round_near

before, int main()

or just inserting an empty definition of that function (like int round_near(float b);

) before int main()

- this is "prototyping".

Otherwise your function will be "seen" as int round_near()

(see no argument definition) and therefore uninitialized integers printed by the program.

On the other hand, this practice will not create portable code, so without the changes below, your (actually C) code can compile in Visual Studio ... but not with other compilers.

Another non-topic: don't use floats in loops for

. Fel floats!

-2


source







All Articles