Функция 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;
}