22

我知道 C++ 为我们提供了一个 ceil 函数。为了练习,我想知道如何在 C++ 中实现 ceil 函数。该方法的签名是 public static int ceil(float num)

请提供一些见解。

我想到了一个简单的方法:将num转换为字符串,找到小数点的索引,检查小数部分是否大于0。如果是,则返回num+1,否则返回num。但我想避免使用字符串转换

4

10 回答 10

32

您可以拆开 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;
}

(在此处插入通常的“不可移植”免责声明。)

于 2011-12-04T19:34:40.283 回答
15

(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,上述函数将失败。

于 2011-12-04T18:23:39.880 回答
7

这本质上是您必须做的,但无需转换为string.

浮点数表示为(+/-) M * 2^E。指数 ,E告诉您距离二进制点*有多远。如果E足够大,则没有小数部分,因此无事可做。如果E足够小,则没有整数部分,因此答案为 1(假设M为非零,并且数字为正数)。否则,E告诉您二进制点出现在尾数中的位置,您可以使用它进行检查,然后执行舍入。


* 不是小数点,因为我们是 base-2,而不是 base-10。

于 2011-12-04T18:08:18.350 回答
5

我的 5 美分:

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

  return t + (t < f);
}
于 2019-01-16T08:38:43.533 回答
1

之前的代码推荐:

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

无法编译:在第 4 行“if(val%10)”上收到“错误 C2296: '%': 非法,左操作数的类型为 'float'”,因为您不能在浮点数上使用 mod 运算符 (%) 或双倍的。请参阅:为什么我们不能将 operator % 用于 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 除非您使用双精度数,否则将不起作用。

此外,由于浮点数和双精度数以 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;
}
于 2018-11-09T18:38:59.830 回答
0

像这样的东西:

  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值定义为您希望小数部分大于整数部分增加之前的任何值。其他要考虑的事情是符号(即,如果值为负等)

于 2011-12-04T18:19:12.953 回答
0

这是一个适用于负数的方法:

int ceil(float num) {
    int inum = (int)num;
    if (num < 0 || num == (float)inum) {
        return inum;
    }
    return inum + 1;
}
于 2020-07-30T17:24:54.533 回答
0

像这样(纯粹来自第一原则):

#include <iostream>
#include <type_traits>


template<typename T>
constexpr int truncate( const T val )
{
    return static_cast<int>( val );
}

template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
constexpr T modulusFloat( const T divident,
    const T divisor )
{
    return divident - truncate( divident / divisor ) * divisor;
}

template<typename T>
constexpr T modulus( const T divident,
    const T divisor ) noexcept
{
    if constexpr ( std::is_floating_point_v<T> )
    {
        return modulusFloat( divident,
            divisor );
    }
    else
    {
        return divident % divisor;
    }
}

template<typename T>
constexpr T ceil( const T val ) noexcept
{
    return val + (T)1 - modulus( val, (T)1 );
}

int main()
{
    std::cout << ceil( 5.4 ) << '\n';
    std::cout << ceil( -5.4 ) << '\n';

    return 0;
}
于 2021-11-03T14:30:18.243 回答
-1

它也适用于负值

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

    return num;
}
于 2018-01-02T13:58:53.303 回答
-5

试试这个...

int ceil(float val)
{
    int temp  = val * 10;
    if(val%10)
    return (temp+1);
    else
    return temp;
}
于 2011-12-04T18:11:18.770 回答