Функция Ceil: как мы можем реализовать это сами? - PullRequest
15 голосов
/ 04 декабря 2011

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

Пожалуйста, предоставьте некоторую информацию.

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

Ответы [ 8 ]

25 голосов
/ 04 декабря 2011

Вы можете разобрать компоненты числа с плавающей точкой 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;
}

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

10 голосов
/ 04 декабря 2011

Вот наивная реализация для положительных чисел (используется тот факт, что приведение к (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.

7 голосов
/ 04 декабря 2011

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

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


* Не десятичная точка, потому что мыв базе-2, а не в базе-10.
3 голосов
/ 16 января 2019

Мои 5 центов:

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

  return t + (t < f);
}
0 голосов
/ 09 ноября 2018

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

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

не компилируется: получает сообщение об ошибке «C2296:«% »: недопустимо, левый операнд имеет тип« float »« в строке 4 »if (val% 10)msgstr "потому что вы не можете использовать оператор мод (%) на float или double.См .: Почему мы не можем использовать оператор% для операндов с плавающей запятой и двойного типа? Он также не работает для десятичных значений, точность которых не превышает 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, сохраненные двоичные представления могут быть неточными.Например:

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

int ma_ceil(float num)
{   int a = num;
    if ((float)a != num)
        return a+1;
    return a;
}
0 голосов
/ 02 января 2018

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

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

    return num;
}
0 голосов
/ 04 декабря 2011

Примерно так:

  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, чтобы дробная часть была больше, чем до увеличения целочисленной части.Другие вещи, которые следует учитывать, это знак (то есть, если значение отрицательное и т. Д.)

0 голосов
/ 04 декабря 2011

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

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