web-dev-qa-db-ja.com

C ++でのfloatの場合はround()

単純な浮動小数点丸め関数が必要です。

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

ceil()floor()はmath.hにあります - しかしround()は見つかりません。

標準のC++ライブラリに別の名前で存在していますか。それとも存在していませんか。

225
Roddy

C++ 98標準ライブラリにはround()はありません。自分で書くこともできます。以下は round-half-up の実装です。

double round(double d)
{
  return floor(d + 0.5);
}

C++ 98標準ライブラリにラウンド関数がないのは、実際にはさまざまな方法で実装できるからです。上記は一般的な方法の1つですが、 round-to-even のような他の方法もあります。実装は少し複雑です。

139

Boostは簡単な丸め機能を提供します。

#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

詳しくは、 Boostの資料 を参照してください。

編集:C++ 11以降、 std::roundstd::lround、およびstd::llround があります。

94
Daniel Wolf

C++ 03標準は、C90標準に基づいています標準Cライブラリは、C++ 03標準ドラフトでカバーされています(最も近い公開された利用可能C++ 03のドラフト標準はN1804)セクション1.2規範的参照

ISO/IEC 9899:1990の7節およびISO/IEC 9899/AMD.1:1995の7節で説明されているライブラリは、以後標準Cライブラリと呼ばれます。1)

cppreferenceのround、lround、llroundのCドキュメント に移動すると、roundおよび関連する関数がC99の一部であることがわかります。 C++ 03以前では使用できません。

C++ 11では、C++ 11がC標準ライブラリのC99ドラフト標準に依存しているため、この変更が行われるため、 std :: roundおよび整数戻り型std :: lroundが提供されます。 std :: llround

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

C99の別のオプションは std :: trunc which:

Argよりも大きさが大きくない最も近い整数を計算します。

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;

}

C++ 11以外のアプリケーションをサポートする必要がある場合は、 boost round、iround、lround、llround または boost trunc を使用するのが最善の策です。

独自のラウンドを展開するのは難しい

見た目よりも難しい:最も近い整数への浮動小数点の丸め、パート1最も近い整数への浮動小数点の丸め、パート2 および 最も近い整数への浮動小数点の丸め、パート 説明:

たとえば、std::floorを使用して実装し、0.5を追加する一般的なロールは、すべての入力に対して機能しません。

double myround(double d)
{
  return std::floor(d + 0.5);
}

これが失敗する入力の1つは0.49999999999999994、(ライブを参照)です。

別の一般的な実装では、浮動小数点型を整数型にキャストします。これにより、宛先部分で整数部分を表現できない場合に未定義の動作を呼び出すことができます。これは、ドラフトC++標準セクション4.9Floating-integral conversionsからわかる(emphasis mine):

浮動小数点型のprvalueは、整数型のprvalueに変換できます。変換は切り捨てられます。つまり、小数部分は破棄されます。 切り捨てられた値を宛先タイプで表現できない場合の動作は未定義です。[...]

例えば:

float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

std::numeric_limits<unsigned int>::max()4294967295の場合、次の呼び出し:

myround( 4294967296.5f ) 

オーバーフローが発生します(ライブ表示)。

Cでround()を実装する簡潔な方法? を参照するnewlibsの単精度浮動小数点ラウンドのこの答えを見ると、これが本当に難しいことがわかります。シンプルに思える非常に長い関数です。浮動小数点実装の詳細な知識がなければ、この関数を正しく実装できる可能性は低いと思われます。

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant Word, least significant Word. */
  int exponent_less_127;

  GET_FLOAT_Word(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_Word(x, w);
  return x;
}

一方、他のソリューションが使用できない場合は、十分にテストされた実装であるため、潜在的にオプションになる可能性がありますnewlib

79
Shafik Yaghmour

四捨五入の結果を整数にしたい場合は、それをceilまたはfloorに渡す必要はありません。すなわち、

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}
71
kalaxy

C++ 11以降cmathで利用可能です( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdfによる )。

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

出力:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2
41
schibum

通常はfloor(value + 0.5)として実装されています。

私が知っている少なくとも3つの丸めアルゴリズムがあるのでそれはおそらく丸めと呼ばれません:ゼロへの丸め、最も近い整数への丸め、そして銀行家の丸め。あなたは最も近い整数への丸めを求めています。

27
MSN

私たちが見ている2つの問題があります。

  1. 丸め変換
  2. 型変換.

四捨五入による変換とは、±float/doubleから最も近い階数/ ceil float/doubleへの四捨五入を意味します。あなたの問題はここで終わるかもしれません。しかし、Int/Longを返すことが予想される場合は、型変換を実行する必要があるため、「オーバーフロー」問題が解決策に当たる可能性があります。そう、あなたの機能のエラーのチェックをしなさい

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

から: http://www.cs.tut.fi/~jkorpela/round.html

13
Sangeet

ある種の丸めもBoostに実装されています。

#include <iostream>

#include <boost/numeric/conversion/converter.hpp>

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

これがうまくいくのはあなたがto-integer変換をした場合だけであることに注意してください。

11
Philipp

あなたはn桁の精度に丸めることができます:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}
6
Carl

round()関数のdouble出力を最終的にintに変換したい場合は、この質問に対する解決策は次のようになります。

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

一様にランダムな値で渡されると、これは私のマシンではおよそ8.88 nsになります。

以下は、私が言うことができる限り機能的には同等ですが、パフォーマンス上の大きな利点のために、私のマシンでは2.48 nsでクロックインします。

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

パフォーマンスが向上した理由の1つに、分岐のスキップがあります。

5
dshin

floor(x+0.5)に注意してください。これは範囲[2 ^ 52,2 ^ 53]の奇数に対して起こり得ることです:

-bash-3.2$ cat >test-round.c <<END

#include <math.h>
#include <stdio.h>

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

これは http://bugs.squeak.org/view.php?id=7134 です。 @konikのような解決策を使用してください。

私自身の堅牢なバージョンは次のようになります。

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

下限(x + 0.5)を回避するもう1つの理由は ここで です。

4
aka.nice

何も実装する必要がないので、なぜ多くの答えが定義、関数、またはメソッドを含むのか、よくわかりません。

C99では

型総称マクロの場合、次のようなand andヘッダー<tgmath.h>があります。

#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);

これをコンパイルできない場合は、おそらく数学ライブラリを省略しています。これに似たコマンドは、私が持っているすべてのCコンパイラ(いくつか)で動作します。

gcc -lm -std=c99 ...

C++ 11の場合

IEEE倍精度浮動小数点に依存する#include <cmath>には、次のような追加のオーバーロードがあります。

#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);

std名前空間の同等物 もあります。

これをコンパイルできない場合は、C++ではなくCコンパイルを使用している可能性があります。次の基本コマンドは、g ++ 6.3.1、x86_64-w64-mingw32-g ++ 6.3.0、clang-x86_64 ++ 3.8.0、およびVisual C++ 2015コミュニティではエラーも警告も生成しません。

g++ -std=c++11 -Wall

序数区分あり

Tがshort、int、long、または別の序数である2つの序数を除算する場合、丸め式はこれになります。

T roundedQuotient = (2 * integerNumerator + 1)
    / (2 * integerDenominator);

精度

浮動小数点演算に奇妙な不正確さが現れるのは間違いありませんが、これは数値が現れるときだけであり、丸めとはほとんど関係がありません。

ソースは、浮動小数点数のIEEE表現の仮数の有効桁数だけではなく、人間としての10進数の考え方に関連しています。

10は5と2の積であり、5と2は比較的素数です。したがって、IEEE浮動小数点標準は、すべての2進デジタル表現の10進数として完全に表現することはできません。

これは丸めアルゴリズムの問​​題ではありません。型の選択や計算の設計、データ入力、数値の表示の際に考慮すべきは数学的現実です。アプリケーションがこれらの10進数 - バイナリ変換の問題を示す数字を表示する場合、そのアプリケーションはデジタルの現実には存在しないため変更する必要がある正確さを視覚的に表現しています。

2
FauChristian

modf関数を使用した関数double round(double)

double round(double x)
{
    using namespace std;

    if ((numeric_limits<double>::max() - 0.5) <= x)
        return numeric_limits<double>::max();

    if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
        return (-1*std::numeric_limits<double>::max());

    double intpart;
    double fractpart = modf(x, &intpart);

    if (fractpart >= 0.5)
        return (intpart + 1);
    else if (fractpart >= -0.5)
        return intpart;
    else
        return (intpart - 1);
    }

クリーンにコンパイルするには、 "math.h"と "limits"をインクルードする必要があります。この関数は次の丸めスキーマに従って機能します。

  • 5.0のラウンドは5.0
  • 3.8のラウンドは4.0です
  • 2.3のラウンドは2.0です
  • 1.5のラウンドは2.0
  • 0.501のラウンドは1.0です
  • 0.5のラウンドは1.0
  • 0.499のラウンドは0.0
  • 0.01のラウンドは0.0
  • 0.0の丸めは0.0です
  • -0.01のラウンドは-0.0です
  • -0.499のラウンドは-0.0です
  • -0.5のラウンドは-0.0
  • -0.501のラウンドは-1.0
  • -1.5のラウンドは-1.0です
  • -2.3のラウンドは-2.0
  • -3.8のラウンドは-4.0
  • -5.0のラウンドは-5.0です
2
konik

Kalaxyの応答に基づいて、以下は自然な丸めに基づいて任意の浮動小数点数を最も近い整数型に丸めるテンプレート化された解決策です。値が整数型の範囲外である場合も、デバッグモードでエラーをスローし、それによって実行可能なライブラリ関数としておおまかに機能します。

    // round a floating point number to the nearest integer
    template <typename Arg>
    int Round(Arg arg)
    {
#ifndef NDEBUG
        // check that the argument can be rounded given the return type:
        if (
            (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
            (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
            )
        {
            throw std::overflow_error("out of bounds");
        }
#endif

        return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
    }
1
quant

C++ 11標準をサポートする環境でコードをコンパイルできる必要があるが、それをサポートしない環境でも同じコードをコンパイルできる必要がある場合は、関数マクロを使用してstdを選択できます。 :: round()と各システム用のカスタム関数。 -DCPP11または/DCPP11をC++ 11準拠のコンパイラに渡して(またはその組み込みバージョンのマクロを使用して)、次のようなヘッダーを作成するだけです。

// File: rounding.h
#include <cmath>

#ifdef CPP11
    #define ROUND(x) std::round(x)
#else    /* CPP11 */
    inline double myRound(double x) {
        return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
    }

    #define ROUND(x) myRound(x)
#endif   /* CPP11 */

簡単な例については、 http://ideone.com/zal709 を参照してください。

これは、C++ 11に準拠していない環境でのstd :: round()に似ています(-0.0の符号ビットの保存など)。ただし、わずかなパフォーマンスの低下を招く可能性があり、0.49999999999999994または同様の値など、特定の既知の "問題のある"浮動小数点値の丸めに問題がある可能性があります。

あるいは、C++ 11準拠のコンパイラにアクセスできる場合は、その<cmath>ヘッダーからstd :: round()を取得し、まだ定義されていない場合はそれを使用して関数を定義する独自のヘッダーを作成できます。ただし、これが最適な解決策にはならない可能性があることに注意してください。特に複数のプラットフォーム用にコンパイルする必要がある場合は特にそうです。

1
Justin Time

コメントやその他の回答で指摘されているように、ISO C++標準ライブラリは、この関数がISO C99標準数学ライブラリを参照して取り込まれるまで、ISO C++ 11までround()を追加しませんでした。

[½、ub] round(x) == floor (x + 0.5)の正のオペランドの場合、ubは223 IEEE-754(2008)binary32にマップされている場合のfloat、および252 IEEE-754(2008)binary64にマップされている場合のdoubleの場合。 23と52の数は、これら2つの浮動小数点形式の格納された仮数ビットの数に対応します。 [+0、½)round(x) == 0の正のオペランドの場合、および(ubの正のオペランドの場合、+∞] round(x) == x。 x軸、負の引数xround(-x) == -round(x)に従って処理することができます。

これは以下のコンパクトなコードにつながります。さまざまなプラットフォームにわたって、妥当な数の機械命令にコンパイルされます。 my_roundf()には十数個の命令が必要なGPU上で最もコンパクトなコードを観察しました。プロセッサのアーキテクチャとツールチェーンに応じて、この浮動小数点ベースのアプローチは、 別の回答 で参照されているnewlibの整数ベースの実装よりも速くなることも遅くなることもあります。

私は、/fp:strict/fp:fastの両方を使用して、Intelコンパイラバージョン13を使用してnewlibのmy_roundf()実装に対して徹底的にroundf()をテストしました。また、newlibのバージョンが、Intelコンパイラのmathimfライブラリのroundf()と一致することも確認しました。倍精度round()では徹底的なテストはできませんが、コードは単精度実装と構造的に同じです。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

float my_roundf (float x)
{
    const float half = 0.5f;
    const float one = 2 * half;
    const float lbound = half;
    const float ubound = 1L << 23;
    float a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floorf (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

double my_round (double x)
{
    const double half = 0.5;
    const double one = 2 * half;
    const double lbound = half;
    const double ubound = 1ULL << 52;
    double a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floor (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

uint32_t float_as_uint (float a)
{
    uint32_t r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float uint_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float newlib_roundf (float x)
{
    uint32_t w;
    int exponent_less_127;

    w = float_as_uint(x);
    /* Extract exponent field. */
    exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    if (exponent_less_127 < 23) {
        if (exponent_less_127 < 0) {
            /* Extract sign bit. */
            w &= 0x80000000;
            if (exponent_less_127 == -1) {
                /* Result is +1.0 or -1.0. */
                w |= ((uint32_t)127 << 23);
            }
        } else {
            uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
            if ((w & exponent_mask) == 0) {
                /* x has an integral value. */
                return x;
            }
            w += 0x00400000 >> exponent_less_127;
            w &= ~exponent_mask;
        }
    } else {
        if (exponent_less_127 == 128) {
            /* x is NaN or infinite so raise FE_INVALID by adding */
            return x + x;
        } else {
            return x;
        }
    }
    x = uint_as_float (w);
    return x;
}

int main (void)
{
    uint32_t argi, resi, refi;
    float arg, res, ref;

    argi = 0;
    do {
        arg = uint_as_float (argi);
        ref = newlib_roundf (arg);
        res = my_roundf (arg);
        resi = float_as_uint (res);
        refi = float_as_uint (ref);
        if (resi != refi) { // check for identical bit pattern
            printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}
1
njuffa

浮動小数点値を小数点以下「n」桁で丸める最良の方法は、O(1) timeで次のようになります。

値を3桁、つまりn = 3で四捨五入する必要があります。

float a=47.8732355;
printf("%.3f",a);
0
Parveen Kumar

私は、x86アーキテクチャとMS VS固有のC++のために、次のようなラウンドインasmを実装しています。

__forceinline int Round(const double v)
{
    int r;
    __asm
    {
        FLD     v
        FISTP   r
        FWAIT
    };
    return r;
}

UPD:double値を返す

__forceinline double dround(const double v)
{
    double r;
    __asm
    {
        FLD     v
        FRNDINT
        FSTP    r
        FWAIT
    };
    return r;
}

出力:

dround(0.1): 0.000000000000000
dround(-0.1): -0.000000000000000
dround(0.9): 1.000000000000000
dround(-0.9): -1.000000000000000
dround(1.1): 1.000000000000000
dround(-1.1): -1.000000000000000
dround(0.49999999999999994): 0.000000000000000
dround(-0.49999999999999994): -0.000000000000000
dround(0.5): 0.000000000000000
dround(-0.5): -0.000000000000000
0
Aleksey F.