web-dev-qa-db-ja.com

Ceil関数:どうすれば自分で実装できますか?

C++がceil関数を提供することを私は知っています。実際には、C++でceil関数をどのように実装できるのか疑問に思いました。メソッドのシグネチャはpublicstatic int ceil(float num)です。

いくつかの洞察を提供してください。

簡単な方法を考えました。numを文字列に変換し、小数点のインデックスを見つけ、小数部分が0より大きいかどうかを確認します。はいの場合はnum + 1を返し、そうでない場合はnumを返します。しかし、文字列変換の使用は避けたい

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の場合、上記の関数は失敗します。

10
Greg Hewgill

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

(ここに通常の「ポータブルではない」免責事項を挿入します。)

25
fredoverflow

それは本質的にあなたがしなければならないことですが、stringに変換することはありません。

浮動小数点数は(+/-) M * 2^Eとして表されます。指数Eは、2進小数点からどれだけ離れているかを示します*Eが十分に大きい場合、小数部分がないため、何もする必要はありません。 Eが十分に小さい場合、整数部分がないため、答えは1です(Mがゼロ以外で、数値が正であると仮定します)。それ以外の場合、Eは、仮数内のどこにバイナリポイントが表示されるかを示します。これを使用して、チェックを実行し、丸めを実行できます。


* 10進数ではなく、2進数であるため、小数点ではありません。
7

私の5セント:

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

  return t + (t < f);
}
3
user1095108

負の値でも機能します

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

    return num;
}
0
chris

このようなもの:

  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
Nim

以前の推奨コード:

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

コンパイルされません:「エラーC2296: '%':不正です。floatまたはダブル。参照: floatおよびdouble型のオペランドに演算子%を使用できないのはなぜですか? 精度が1/10以下の10進値に対しても機能しません。

一方、以前のコードの推奨事項:

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

浮動小数点値の範囲を超えない限り、うまく機能します。 num = 555555555;または、doubleを使用しない限り、num = -5.000000001は機能しません。

また、floatと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
David M