Question

I know that C++ provides us with a ceil function. For practice, I was wondering how can we implement the ceil function in C++. The signature of the method is public static int ceil(float num)

Please provide some insight.

I thought of a simple way: Convert num to a string, find the index of the decimal point, check if the decimal part is greater than 0. If yes, return num+1 else return num. But I want to avoid using the string conversion

Was it helpful?

Solution

Here is a naive implementation for positive numbers (this uses the fact that casting to (int) truncates toward zero):

int ceil(float num) {
    int inum = (int)num;
    if (num == (float)inum) {
        return inum;
    }
    return inum + 1;
}

It is easy to extend this to work with negative numbers too.

Your question asked for a function returning int, but normally the ceil() function returns the same type as its argument so there are no problems with the range (that is, float ceil(float num)). For example, the above function will fail if num is 1e20.

OTHER TIPS

You can take apart the ingredients of an IEEE754 floating point number and implement the logic yourself:

#include <cstring>

float my_ceil(float f)
{
    unsigned input;
    memcpy(&input, &f, 4);
    int exponent = ((input >> 23) & 255) - 127;
    if (exponent < 0) return (f > 0);
    // small numbers get rounded to 0 or 1, depending on their sign

    int fractional_bits = 23 - exponent;
    if (fractional_bits <= 0) return f;
    // numbers without fractional bits are mapped to themselves

    unsigned integral_mask = 0xffffffff << fractional_bits;
    unsigned output = input & integral_mask;
    // round the number down by masking out the fractional bits

    memcpy(&f, &output, 4);
    if (f > 0 && output != input) ++f;
    // positive numbers need to be rounded up, not down

    return f;
}

(Insert the usual "not portable" disclaimer here.)

That is essentially what you have to do, but without converting to string.

A floating-point number is represented as (+/-) M * 2^E. The exponent, E, tells you how far away you are from the binary point*. If E is big enough, there is no fractional part, so there's nothing to do. If E is small enough, there is no integer part, so the answer is 1 (assuming M is non-zero, and the number is positive). Otherwise, E tells you where the binary point appears within your mantissa, which you can use to do a check, and then perform rounding.


* Not decimal point, because we're in base-2, not base-10.

My 5 cents:

template <typename F>
constexpr inline auto ceil(F const f) noexcept
{
  auto const t(std::trunc(f));

  return t + (t < f);
}

Something like this:

  double param, fractpart, intpart;

  param = 3.14159265;
  fractpart = modf (param , &intpart);

  int intv = static_cast<int>(intpart); // can overflow - so handle that.

  if (fractpart > some_epsilon)
    ++intv;

You just need to define some_epsilon value to whatever you want the fractional part to be bigger than before the integer part is incremented. Other things to consider are sign (i.e. if the value is negative etc.)

it works with negative value too

int ma_ceil(float num)
{   int a = num;
    if ((float)a != num)
        return num+1;

    return num;
}

The previous code recommendation:

int ceil(float val) 
{
    int temp  = val * 10;
    if(val%10)
    return (temp+1);
    else
    return temp;
}

does not compile: Receives "error C2296: '%': illegal, left operand has type 'float'" on line 4 "if(val%10)" because you can't use the mod operator (%) on a float or double. See: Why we can't use operator % for float and double type operands? It also does not work for decimal values whose precision is not greater than 1/10.

Whereas, the prior code recommendation:

int ma_ceil(float num)
{   int a = num;
    if ((float)a != num)
        return num+1;
    return num;
}

works well, as long as you don't go beyond the bounds of a floating point value. num = 555555555; or num = -5.000000001 are not going to work unless you use a double.

Also, because floats and doubles are stored in IEEE floating point format, the binary representations stored can be inexact. For, instance:

float num = 5; in some instances might not get the value 5.0000000 assigned, but rather 5.9999998 or 5.00000001. To correct the prior code version, I'd recommended changing the return value to use integer math rather than relying on the accuracy of the floating point value, as follows:

int ma_ceil(float num)
{   int a = num;
    if ((float)a != num)
        return a+1;
    return a;
}

Try this...

int ceil(float val)
{
    int temp  = val * 10;
    if(val%10)
    return (temp+1);
    else
    return temp;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top