単純な浮動小数点丸め関数が必要です。
double round(double);
round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1
ceil()
とfloor()
はmath.hにあります - しかしround()
は見つかりません。
標準のC++ライブラリに別の名前で存在していますか。それとも存在していませんか。
C++ 98標準ライブラリにはround()はありません。自分で書くこともできます。以下は round-half-up の実装です。
double round(double d)
{
return floor(d + 0.5);
}
C++ 98標準ライブラリにラウンド関数がないのは、実際にはさまざまな方法で実装できるからです。上記は一般的な方法の1つですが、 round-to-even のような他の方法もあります。実装は少し複雑です。
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::round
、std::lround
、およびstd::llround
があります。
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.9
Floating-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。
四捨五入の結果を整数にしたい場合は、それをceilまたはfloorに渡す必要はありません。すなわち、
int round_int( double r ) {
return (r > 0.0) ? (r + 0.5) : (r - 0.5);
}
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
通常はfloor(value + 0.5)
として実装されています。
私が知っている少なくとも3つの丸めアルゴリズムがあるのでそれはおそらく丸めと呼ばれません:ゼロへの丸め、最も近い整数への丸め、そして銀行家の丸め。あなたは最も近い整数への丸めを求めています。
私たちが見ている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))
ある種の丸めも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変換をした場合だけであることに注意してください。
あなたは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;
}
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つに、分岐のスキップがあります。
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つの理由は ここで です。
何も実装する必要がないので、なぜ多くの答えが定義、関数、またはメソッドを含むのか、よくわかりません。
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進数 - バイナリ変換の問題を示す数字を表示する場合、そのアプリケーションはデジタルの現実には存在しないため変更する必要がある正確さを視覚的に表現しています。
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"をインクルードする必要があります。この関数は次の丸めスキーマに従って機能します。
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);
}
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()を取得し、まだ定義されていない場合はそれを使用して関数を定義する独自のヘッダーを作成できます。ただし、これが最適な解決策にはならない可能性があることに注意してください。特に複数のプラットフォーム用にコンパイルする必要がある場合は特にそうです。
コメントやその他の回答で指摘されているように、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軸、負の引数x
はround(-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;
}
浮動小数点値を小数点以下「n」桁で丸める最良の方法は、O(1) timeで次のようになります。
値を3桁、つまりn = 3で四捨五入する必要があります。
float a=47.8732355;
printf("%.3f",a);
私は、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