Функция Ceil: как мы можем реализовать ее самостоятельно?

Я знаю, что С++ предоставляет нам функцию ceil. Для практики мне было интересно, как мы можем реализовать функцию ceil в С++. Подпись метода public static int ceil (float num)

Просьба представить некоторые сведения.

Я подумал про простой способ: конвертировать num в строку, найти индекс десятичной точки, проверить, является ли десятичная часть более 0. Если да, верните num + 1 else return num. Но я хочу избежать использования преобразования строк

Ответ 1

Вот наивная реализация для положительных чисел (это использует тот факт, что отбрасывание до (int) обрезается до нуля):

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

Это легко распространить и на отрицательные числа.

В вашем вопросе задана функция, возвращающая int, но обычно функция ceil() возвращает тот же тип, что и аргумент, поэтому нет проблем с диапазоном (т.е. float ceil(float num)). Например, указанная выше функция будет терпеть неудачу, если num равно 1e20.

Ответ 2

Вы можете разделить ингредиенты числа с плавающей запятой IEEE754 и реализовать логику самостоятельно:

#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;
}

(Вставьте здесь обычный "не переносимый" отказ).

Ответ 3

Это по существу то, что вам нужно сделать, но без преобразования в string.

Число с плавающей запятой представлено как (+/-) M * 2^E. Показатель E сообщает вам, как далеко вы находитесь от двоичной точки *. Если E достаточно велико, то нет дробной части, так что делать нечего. Если E достаточно мало, то нет целочисленной части, поэтому ответ равен 1 (если M отличен от нуля, а число положительно). В противном случае E сообщает вам, где в вашей мантиссе появляется двоичная точка, которую вы можете использовать для проверки, а затем выполните округление.


* Не десятичная точка, потому что мы находимся в base-2, а не base-10.

Ответ 4

Что-то вроде этого:

  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;

Вам просто нужно определить значение some_epsilon для того, что вы хотите, чтобы дробная часть была больше, чем до того, как целая часть будет увеличена. Другие вещи для рассмотрения - это знак (т.е. Если значение отрицательное и т.д.)

Ответ 5

он также работает с отрицательным значением.

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

    return num;
}

Ответ 6

Предыдущая рекомендация кода:

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

не компилируется: получает сообщение об ошибке "C2296:"% ": недопустимо, левый операнд имеет тип" float "" в строке 4 if (val% 10) ", потому что вы не можете использовать оператор mod (%) для float или двойной. См.: Почему мы не можем использовать оператор% для операндов с плавающей точкой и двойного типа? Он также не работает для десятичных значений, точность которых не превышает 1/10.

Принимая во внимание, что предыдущая рекомендация кода:

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

работает хорошо, пока вы не выходите за пределы значения с плавающей запятой. число = 555555555; или num = -5.000000001 не будут работать, если вы не используете double.

Кроме того, поскольку числа с плавающей запятой и числа с двойными числами хранятся в формате IEEE, хранимые двоичные представления могут быть неточными. Например:

число с плавающей запятой = 5; в некоторых случаях может быть не присвоено значение 5.0000000, а 5.9999998 или 5.00000001. Чтобы исправить предыдущую версию кода, я бы рекомендовал изменить возвращаемое значение, чтобы использовать целочисленную математику, а не полагаться на точность значения с плавающей запятой, следующим образом:

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

Ответ 7

Мои 5 центов:

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

  return t + (t < f);
}

Ответ 8

Попробуйте это...

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