C++ の float のround()
-
20-08-2019 - |
質問
したがって、単純な浮動小数点丸め関数が必要です。
double round(double);
round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1
私は見つけることができます ceil()
そして floor()
math.h では - しかしそうではありません round()
.
標準 C++ ライブラリに別の名前で存在しますか、それとも存在しませんか??
解決
C ++ 98標準ライブラリにはラウンド()ありません。あなたはしかし1を自分で書くことができます。以下は、ラウンドハーフアップするの実装です。
double round(double d)
{
return floor(d + 0.5);
}
C ++ 98標準ライブラリには、ラウンド関数が存在しない可能性の高い理由は、それが実際には様々な方法で実施することができるということです。上記一つの共通の方法であるが、このようなラウンドツーものような他のものがあり、これは少ないバイアスされ、あなたが丸めの多くをやろうとしている場合、一般的に優れています。それはしかし実装に少し複雑です。
他のヒント
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
詳細については、「 ブーストのドキュメント.
編集: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に記載されています。1)
に行ったら cppreference のround、lround、llroundに関するCドキュメント それがわかります ラウンド および関連する機能は、 C99 したがって、C++03 以前では使用できません。
C++11 では、C++11 が C99 ドラフト標準に依存しているため、これが変更されます。 C標準ライブラリ したがって、提供します 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 からのもう 1 つのオプションは次のとおりです。 std::trunc どれの:
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 以外のアプリケーションをサポートする必要がある場合は、次の方法を使用するのが最善の策です。 ブーストラウンド、iround、lround、llround または ブーストトラン.
独自バージョンのラウンドをロールするのは難しい
自分でロールするのはおそらく努力の価値がありません。 見た目よりも難しい:浮動小数点数を最も近い整数に丸める、パート 1, float を最も近い整数に丸める、パート 2 そして float を最も近い整数に丸める、パート 3 説明する:
たとえば、次を使用して実装をロールロールするのが一般的です std::floor
そして追加 0.5
すべての入力に対して機能するわけではありません。
double myround(double d)
{
return std::floor(d + 0.5);
}
これが失敗する入力の 1 つは、 0.49999999999999994
, (ライブで見てください).
もう 1 つの一般的な実装には、浮動小数点型を整数型にキャストすることが含まれます。これにより、整数部分が宛先の型で表現できない場合に未定義の動作が呼び出される可能性があります。これは、ドラフト C++ 標準セクションから確認できます。 4.9
浮動積分変換 それは(私の強調):
フローティングポイントタイプの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()を実装する簡潔な方法は? どの参照 新しいライブラリ 単精度浮動小数点ラウンドのバージョン。単純そうに見える関数ですが、非常に長い関数です。浮動小数点実装に関する詳しい知識がない人がこの関数を正しく実装できるとは考えにくいです。
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;
}
一方、他の解決策がどれも使用できない場合は、 新しいライブラリ これは十分にテストされた実装であるため、オプションになる可能性があります。
それはあなたが丸めの整数結果を望んでいた場合は、切り上げや床のいずれかを通してそれを渡す必要がないことは注目に値するかもしれません。すなわち。、
int round_int( double r ) {
return (r > 0.0) ? (r + 0.5) : (r - 0.5);
}
これは HTTPによる(cmathでC ++ 11以降で利用可能です://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を最も近いfloor/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))
丸めの特定のタイプはまた、ブーストに実装されます:
#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;
}
これはあなたがに、整数変換を行う場合にのみ機能することに注意してください。
あなたがして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;
}
あなたは最終的にdouble
にごround()
機能の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;
}
パフォーマンス向上のための理由の中では、分岐スキップされます。
最近では、C99/C++11 数学ライブラリを含む C++11 コンパイラを使用しても問題はありません。しかし、そうすると次のような疑問が生じます。どの丸め関数を選択しますか?
C99/C++11 round()
実際には必要な丸め関数ではないことがよくあります. 。これは、途中の場合のタイブレークとして 0 から離れる方向に丸めるファンキーな丸めモードを使用します (+-xxx.5000
)。特にその丸めモードが必要な場合、または C++ 実装をターゲットにしている場合は、 round()
よりも速いです rint()
, 、それからそれを使用します(または、額面通りに受け取り、その特定の丸め動作を注意深く再現した、この質問に対する他の回答の1つでその動作をエミュレートします)。
round()
の丸めは IEEE754 のデフォルトとは異なります タイブレークとしても最も近いモードに丸めます. 。最近偶数は、数値の平均の大きさにおける統計的な偏りを回避しますが、偶数に偏ります。
現在のデフォルトの丸めモードを使用する数学ライブラリの丸め関数が 2 つあります。 std::nearbyint()
そして std::rint()
, 、どちらも C99/C++11 で追加されたため、いつでも利用できます std::round()
は。唯一の違いは、 nearbyint
FE_INEXACT を発生させることはありません。
好む rint()
パフォーマンス上の理由から:gcc と Clang はどちらもインライン化が容易ですが、gcc は決してインライン化しません nearbyint()
(でもで -ffast-math
)
x86-64 および AArch64 の gcc/clang
いくつか入れました Matt Godbolt の Compiler Explorer で関数をテストする, ここでは、ソース + asm 出力 (複数のコンパイラの場合) を確認できます。コンパイラ出力の読み取りの詳細については、を参照してください。 このQ&A, 、および Matt の CppCon2017 での講演: 「最近、コンパイラは何をしてくれたんですか?」コンパイラの蓋を外す」,
FP コードでは、通常、小さな関数をインライン化することが大きな効果をもたらします。特に Windows 以外では、標準の呼び出し規約に呼び出し保持レジスタがないため、コンパイラは、複数の期間にわたって XMM レジスタに FP 値を保持できません。 call
. 。そのため、asm についてよく知らなくても、それがライブラリ関数の単なる末尾呼び出しなのか、それとも 1 つまたは 2 つの数学命令にインライン展開されているのかを簡単に確認できます。1 つまたは 2 つの命令にインライン化するものは、関数呼び出しよりも優れています (x86 または ARM 上のこの特定のタスクの場合)。
x86 では、SSE4.1 にインライン化されるものすべて roundsd
SSE4.1で自動ベクトル化可能 roundpd
(またはAVX vroundpd
)。(FP->整数変換は、AVX512 を必要とする FP->64 ビット整数を除き、パック SIMD 形式でも利用できます。)
std::nearbyint()
:- x86 の鳴る音:単一のインにインラインで
-msse4.1
. - x86 gcc:単一のインのみにインライン化します
-msse4.1 -ffast-math
, 、gcc 5.4 のみ、および 以前. 。その後、gcc はそれをインライン化しません (おそらく、即値ビットの 1 つが不正確な例外を抑制できることを認識していなかったのでしょうか?)これは、clang が使用するものですが、古い gcc は、rint
インライン化する場合) - AArch64 gcc6.3:デフォルトでは単一の insn にインライン化されます。
- x86 の鳴る音:単一のインにインラインで
std::rint
:- x86 の鳴る音:単一のインにインラインで
-msse4.1
- x86 gcc7:単一のインにインラインで
-msse4.1
. 。(SSE4.1 を使用しない場合、複数の命令にインライン化されます) - x86 gcc6.x 以前:単一のインにインラインで
-ffast-math -msse4.1
. - AArch64 gcc:デフォルトで単一のインにインライン化されます
- x86 の鳴る音:単一のインにインラインで
std::round
:- x86 の鳴る音:インライン化しない
- x86 gcc:複数の命令をインライン化します。
-ffast-math -msse4.1
, 、2 つのベクトル定数が必要です。 - AArch64 gcc:単一の命令にインライン化します (この丸めモード、IEEE デフォルトおよびその他のほとんどのモードのハードウェア サポート)。
std::floor
/std::ceil
/std::trunc
- x86 の鳴る音:単一のインにインラインで
-msse4.1
- x86 gcc7.x:単一のインにインラインで
-msse4.1
- x86 gcc6.x 以前:単一のインにインラインで
-ffast-math -msse4.1
- AArch64 gcc:デフォルトでは単一の命令にインライン化されます
- x86 の鳴る音:単一のインにインラインで
四捨五入 int
/ long
/ long long
:
ここには 2 つのオプションがあります。使用 lrint
(のように rint
しかし戻ってきます long
, 、 または long long
のために llrint
)、または FP->FP 丸め関数を使用してから、通常の方法 (切り捨てあり) で整数型に変換します。一部のコンパイラは、一方の方法で他方の方法よりも優れた最適化を行います。
long l = lrint(x);
int i = (int)rint(x);
ご了承ください int i = lrint(x)
改宗する float
または double
-> long
まず、整数を切り捨てて、 int
. 。これにより、範囲外の整数の場合に違いが生じます。C++ では未定義の動作ですが、x86 FP -> int 命令については明確に定義されています (コンパイラは、コンパイル時に定数伝播中に UB を認識しない限り、この命令を発行します。その後、実行された場合に中断するコードを作成することが許可されます)。
x86 では、整数をオーバーフローさせる FP->整数変換により次のものが生成されます。 INT_MIN
または LLONG_MIN
(のビットパターン 0x8000000
または、符号ビットのみが設定された 64 ビット相当のもの)。Intel はこれを「不定の整数」値と呼んでいます。(見る の cvttsd2si
手動入力, 、スカラー double を符号付き整数に変換する SSE2 命令。32 ビットまたは 64 ビットの整数宛先で使用できます (64 ビット モードのみ)。もあります cvtsd2si
(現在の丸めモードで変換)、これはコンパイラに出力させたいものですが、残念ながら、gcc と Clang はそれを実行しません。 -ffast-math
.
FP との間のやり取りにも注意してください。 unsigned
int / long は、x86 (AVX512 なし) では効率が低くなります。64 ビット マシンでの署名なし 32 ビットへの変換は非常に安価です。64 ビット符号付きに変換して切り詰めるだけです。しかし、それ以外の場合は大幅に遅くなります。
x86 の鳴る音あり/なし
-ffast-math -msse4.1
:(int/long)rint
インラインでroundsd
/cvttsd2si
. 。(最適化を逃したcvtsd2si
).lrint
まったくインライン化しません。x86 gcc6.x 以前 (なし)
-ffast-math
:どちらにしてもインライン- x86 gcc7なし
-ffast-math
:(int/long)rint
丸めと変換を個別に実行します (SSE4.1 の合計 2 つの命令が有効になっている場合、それ以外の場合は、大量のコードがインライン化されます)rint
それなしroundsd
).lrint
インライン化しません。 x86 gcc と
-ffast-math
: すべての方法でインラインでcvtsd2si
(最適な), 、SSE4.1は必要ありません。AArch64 gcc6.3 なし
-ffast-math
:(int/long)rint
2 つの命令にインライン化します。lrint
インライン化しない- AArch64 gcc6.3 あり
-ffast-math
:(int/long)rint
への呼び出しにコンパイルしますlrint
.lrint
インライン化しません。2 つの命令がなければ、これは最適化が失われる可能性があります。-ffast-math
とても遅いです。
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)床を回避する別の理由はここにが与えられます>。
何も実装する必要がないため、なぜこれほど多くの回答に定義、関数、メソッドが含まれるのかわかりません。
C99にて
型汎用マクロには、次の 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の場合
#include <cmath> には、IEEE 倍精度浮動小数点に依存する次のオーバーロードと追加のオーバーロードがあります。
#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 Community ではエラーも警告も生成しません。
g++ -std=c++11 -Wall
序数除算あり
2 つの序数 (T は short、int、long、または別の序数) を除算する場合、丸め式は次のようになります。
T roundedQuotient = (2 * integerNumerator + 1)
/ (2 * integerDenominator);
正確さ
浮動小数点演算で奇妙に見える不正確さが現れることは間違いありませんが、これは数値が表示される場合のみであり、丸めとはほとんど関係ありません。
ソースは、浮動小数点数の IEEE 表現の仮数部の有効桁数だけではなく、人間としての 10 進数の考え方に関連しています。
10 は 5 と 2 の積であり、5 と 2 は互いに素です。したがって、IEEE 浮動小数点標準は、すべての 2 進デジタル表現を 10 進数として完全に表現することはできません。
これは丸めアルゴリズムの問題ではありません。数値のタイプの選択や計算、データ入力、表示の設計時に考慮すべきは数学的現実です。アプリケーションがこれらの 10 進数と 2 進数の変換の問題を示す数字を表示する場合、アプリケーションはデジタル現実には存在しない精度を視覚的に表現していることになるため、変更する必要があります。
関数 double round(double)
を使用して modf
関数:
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
あなたはC ++ 11標準をサポートする環境でコードをコンパイルできるようにする必要がありますが、また、あなたがする関数マクロを使用することができ、それをサポートしていない環境では、同じコードをコンパイルできるようにする必要がある場合std ::ラウンド()および各システムのためのカスタム関数の間で選択します。ただ、合格-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するます。
これは、-0.0のための符号ビットの保存を含むC ++ 11に準拠していない環境でのstd ::ラウンド()を近似します。しかし、わずかなパフォーマンスヒットを引き起こす可能性があり、そしておそらく、このような0.49999999999999994など特定の既知の「問題」の浮動小数点値または類似した値を丸めの問題を持つことになります。
また、あなたがC ++ 11準拠のコンパイラへのアクセス権を持っている場合は、あなただけの<cmath>
ヘッダからラウンド():: STDをつかむことができ、そしてそれはだ場合、関数を定義して、独自のヘッダを作るためにそれを使用しますすでに定義されていません。これはあなたが複数のプラットフォーム用にコンパイルする必要がある場合は特に、しかし、最適な解決策ではないかもしれないことに注意します。
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);
}
、ISO C ++標準ライブラリには追加しませんでしたround()
この機能は、ISO C99標準数学ライブラリを参照して引き込まれました。
UB が2 23 IEEEにマッピングされた場合round(x) == floor (x + 0.5)
するためのものである[1/2 UB ] float
における正オペランドについてbinary32
ため-754(2008)double
、および2 52 それはIEEE-754(2008)にマッピングされるbinary64
。番号23および52は、これら2つの浮動小数点形式で格納されたの仮数ビットの数に対応します。正のオペランドのための[0、1/2)round(x) == 0
、そしてにおける正のオペランドのため( UB 、+∞] round(x) == x
。関数は、x軸、負引数について対称であるように< => x
に従って処理することができる。
これは、以下のコンパクトなコードにつながります。これは、さまざまなプラットフォーム間でマシン命令の合理的な数にコンパイルされます。私はround(-x) == -round(x)
約ダース命令を必要とGPUを、上で最もコンパクトなコードを観察しました。プロセッサアーキテクチャとツールチェーンに応じて、この浮動小数点ベースのアプローチのいずれか速いか遅い異なる回答するます。
私は両方で、インテルコンパイラのバージョン13を使用してnewlibのmy_roundf()
実装に対して徹底的にroundf()
テスト/fp:strict
と/fp:fast
。私はまた、newlibのバージョンは、Intelコンパイラのmathimf
ライブラリに<=>と一致することを確認しました。徹底的な試験は、しかしコードが単精度の実装と構造的に同一であり、倍精度<=>ため不可能である。
#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;
}
私は、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
「n」は小数点以下の桁数によってフローティング値を四捨五入するための最良の方法は、O中で、以下の通りである(1)時間: -
我々は、3ヶ所によって値を四捨五入する必要があり、すなわち、N = 3.So、
float a=47.8732355;
printf("%.3f",a);
// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that's what you want anyway =P)
float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();
// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want
// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");
これは、変換の非効率的な汚い方法かもしれないが、一体、それは笑動作します。それは実際のフロートに適用されるため、それは、良いことです。ただ視覚的に出力に影響を与えません。
私はこれをしなかった。
#include <cmath.h>
using namespace std;
double roundh(double number, int place){
/* place = decimal point. Putting in 0 will make it round to whole
number. putting in 1 will round to the
tenths digit.
*/
number *= 10^place;
int istack = (int)floor(number);
int out = number-istack;
if (out < 0.5){
floor(number);
number /= 10^place;
return number;
}
if (out > 0.4) {
ceil(number);
number /= 10^place;
return number;
}
}