calgorithmfloorceil

Alternative to ceil() and floor() to get the closest integer values, above and below of a floating point value?


I´m looking for an alternative for the ceil() and floor() functions in C, due to I am not allowed to use these in a project.

What I have build so far is a tricky back and forth way by the use of the cast operator and with that the conversion from a floating-point value (in my case a double) into an int and later as I need the closest integers, above and below the given floating-point value, to be also double values, back to double:

#include <stdio.h>

int main(void) {
   double original = 124.576;
   double floorint;
   double ceilint;
   int f;
   int c;

   f = (int)original;            //Truncation to closest floor integer value
   c = f + 1;
   floorint = (double)f;
   ceilint = (double)c;

   printf("Original Value: %lf, Floor Int: %lf , Ceil Int: %lf", original, floorint, ceilint);
}

Output:

Original Value: 124.576000, Floor Int: 124.000000 , Ceil Int: 125.000000 

For this example normally I would not need the ceil and floor integer values of c and f to be converted back to double but I need them in double in my real program. Consider that as a requirement for the task.


Although the output is giving the desired values and seems right so far, I´m still in concern if this method is really that right and appropriate or, to say it more clearly, if this method does bring any bad behavior or issue into the program or gives me a performance-loss in comparison to other alternatives, if there are any other possible alternatives.


Do you know a better alternative? And if so, why this one should be better?

Thank you very much.


Solution

  • Do you know a better alternative? And if so, why this one should be better?

    OP'code fails:


    Alternative construction

    double my_ceil(double x)

    Using the cast to some integer type trick is a problem when x is outsize the integer range. So check first if x is inside range of a wide enough integer (one whose precision exceeds double). x values outside that are already whole numbers. Recommend to go for the widest integer (u)intmax_t.

    Remember that a cast to an integer is a round toward 0 and not a floor. Different handling needed if x is negative/positive when code is ceil() or floor(). OP's code missed this.

    I'd avoid if (x >= INTMAX_MAX) { as that involves (double) INTMAX_MAX whose rounding and then precise value is "chosen in an implementation-defined manner". Instead, I'd compare against INTMAX_MAX_P1. some_integer_MAX is a Mersenne Number and with 2's complement, ...MIN is a negated "power of 2".

    #include <inttypes.h>
    
    #define INTMAX_MAX_P1 ((INTMAX_MAX/2 + 1)*2.0)
    
    double my_ceil(double x) {
      if (x >= INTMAX_MAX_P1) {
        return x;
      }
      if (x < INTMAX_MIN) {
        return x;
      }
    
      intmax_t i = (intmax_t) x;      // this rounds towards 0
      if (i < 0 || x == i) return i;  // negative x is already rounded up.
      return i + 1.0;
    }
    

    As x may be a not-a-number, it is more useful to reverse the compare as relational compare of a NaN is false.

    double my_ceil(double x) {
      if (x >= INTMAX_MIN && x < INTMAX_MAX_P1) {
        intmax_t i = (intmax_t) x;      // this rounds towards 0
        if (i < 0 || x == i) return i;  // negative x is already rounded up.
        return i + 1.0;
      }
      return x;
    }
    
    double my_floor(double x) {
      if (x >= INTMAX_MIN && x < INTMAX_MAX_P1) {
        intmax_t i = (intmax_t) x;      // this rounds towards 0
        if (i > 0 || x == i) return i;  // positive x is already rounded down.
        return i - 1.0;
      }
      return x;
    }