Cで浮動小数点値(37.777779など)を小数点以下2桁(37.78)に丸めるにはどうすればよいですか?
Cで浮動小数点値(37.777779など)を小数点以下2桁(37.78)に丸めるにはどうすればよいですか?
回答:
出力目的で数値を丸めるだけの場合は、"%.2f"
形式文字列が正解です。ただし、さらに計算するために浮動小数点値を実際に丸めたい場合は、次のようなものが機能します。
#include <math.h>
float val = 37.777779;
float rounded_down = floorf(val * 100) / 100; /* Result: 37.77 */
float nearest = roundf(val * 100) / 100; /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100; /* Result: 37.78 */
選択する可能性のある3つの異なる丸め規則があることに注意してください。切り捨て(つまり、小数点以下2桁で切り捨て)、最も近い値に丸め、および切り上げです。通常、最も近い値に丸めます。
他のいくつかが指摘しているように、浮動小数点表現の癖のため、これらの丸められた値は正確に「明白な」10進数値ではない可能性がありますが、非常に近いものになります。
丸め、および特に最も近い値に丸めるためのタイブレークルールの詳細については、ウィキペディアの丸めに関する記事を参照してください。
doubles
どういうわけかこれで動作させることはできますか?私が望む仕事をしていないようです:((とを使用floor
してceil
)
printfで%.2fを使用しています。小数点2桁のみを出力します。
例:
printf("%.2f", 37.777779);
出力:
37.77
float
範囲の損失がないという利点もありval * 100
ます。
あなたが印刷の価値について話していると仮定すると、Andrew ColesonとAraKの答えは正しいです:
printf("%.2f", 37.777779);
ただし、内部で使用するために数値を正確に37.78に丸めることを目的としている場合(たとえば、別の値と比較する場合)、浮動小数点数が機能する方法のため、これは良いアイデアではありません。通常、浮動小数点の等値比較を行いたい場合は、代わりにターゲット値+/-シグマ値を使用します。または、数値を既知の精度を持つ文字列としてエンコードし、それを比較します。
関連する質問に対するGreg Hewgillの回答のリンクを参照してください。これには、財務計算に浮動小数点を使用してはならない理由も含まれています。
printf("%.*f", (int)precision, (double)number);
これはどう:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
C-stringに書き込みたい場合:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
丸められたものが表現できない場合があるため(浮動小数点数の制限)、a float
を別float
の丸めに設定する方法はありませんfloat
。たとえば、37.777779を37.78に丸めるが、最も近い表現可能な数は37.781であるとします。
ただし、フォーマット文字列関数を使用してを「丸める」ことができfloat
ます。
float
小数点以下の桁数をnに丸めることができず、結果が常に小数点以下の桁数になることを期待することです。あなたはまだあなたがfloat
期待したものではなく、を取得します。
あなたはまだ使うことができます:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
例:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
C ++(またはCスタイルのキャストを使用したC)では、関数を作成できます。
/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
int y=x;
double z=x-y;
double m=pow(10,numDecimals);
double q=z*m;
double r=round(q);
return static_cast<double>(y)+(1.0/m)*r;
}
次に、std::cout << showDecimals(37.777779,2);
37.78が生成されます。
もちろん、その関数で5つの変数をすべて作成する必要はありませんが、ロジックを確認できるように、変数はそのままにしておきます。おそらくもっと簡単な解決策がありますが、これは私にとってはうまく機能します。特に、必要に応じて小数点以下の桁数を調整できるためです。
これには常にprintf
関数ファミリーを使用してください。値を浮動小数点数として取得したい場合でもsnprintf
、丸められた値を文字列として取得してから、次のように解析して戻すのが最善ですatof
。
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
私がこれを言っているのは、現在上位投票されている回答や他のいくつかの方法(100を掛け、最も近い整数に丸め、次に100で除算する)が示すアプローチには2つの点で欠陥があるためです。
最初の種類のエラーを説明するために-丸め方向が時々間違っている-このプログラムを実行してみてください:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
次の出力が表示されます。
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
最初の値は0.015未満だったので、小数点以下2桁に四捨五入すると数学的に正解は0.01になることに注意してください。もちろん、0.01はdoubleとして正確に表すことはできませんが、結果は0.01に最も近いdoubleになると期待しています。使用snprintf
するとその結果がround(100 * x) / 100
得られますが、使用すると0.02になりますが、これは誤りです。どうして?100 * x
結果として正確に1.5を与えるからです。したがって、100を掛けると、正しい方向が丸められます。
2番目の種類のエラーを説明するために* 100
、/ 100
実際には互いの逆ではないために結果が間違っていることがあります- 非常に大きな数で同様の演習を行うことができます。
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
現在、私たちの数は小数部分すらありません。これは整数値で、typeで保存されdouble
ます。したがって、丸めた後の結果は、最初の数と同じになるはずですよね?
上記のプログラムを実行すると、次のように表示されます。
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
おっとっと。私たちのsnprintf
メソッドは正しい結果を再び返しますが、乗算してから四捨五入して除算するアプローチは失敗します。数学的に正しい値があるためだという8631192423766613.0 * 100
、863119242376661300.0
二重のように正確に表現ではありません。最も近い値は863119242376661248.0
です。これを100で割ると8631192423766612.0
、最初の数とは異なる数になります。
うまくいけばroundf
、小数点以下の桁数を四捨五入するためのの使用が無効になり、snprintf
代わりに使用する必要があることを示す十分なデモンストレーションです。それがあなたにとって恐ろしいハックのように思えるなら、おそらくそれは基本的にCPythonが行うことであるという知識に安心するでしょう。
使用する float roundf(float x)
ます。
「丸め関数は、現在の丸め方向に関係なく、引数を浮動小数点形式の最も近い整数値に丸め、ゼロから半分の場合を丸めます。」C11dr§7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
float
実装によっては、半分のように見える可能性のある数値はそうではありません。浮動小数点は通常、base-2指向であるためです。さらに、0.01
すべての「途中」のケースで最も近い値に正確に丸めることは最も困難です。
void r100(const char *s) {
float x, y;
sscanf(s, "%f", &x);
y = round(x*100.0)/100.0;
printf("%6s %.12e %.12e\n", s, x, y);
}
int main(void) {
r100("1.115");
r100("1.125");
r100("1.135");
return 0;
}
1.115 1.115000009537e+00 1.120000004768e+00
1.125 1.125000000000e+00 1.129999995232e+00
1.135 1.134999990463e+00 1.139999985695e+00
「1.115」は1.11と1.12の間の「中間」ですが、に変換するfloat
と、値は1.115000009537...
「中間」であり、「中間」ではなくなり、1.12に近づき、最も近い値float
に丸められます。1.120000004768...
「1.125」は1.12と1.13の間の「中間」であり、に変換するfloat
と、値は正確に1.125
「中間」になります。偶数の支配との関係により1.13に向かって丸め、最も近いものに丸めますfloat
に1.129999995232...
「1.135」は1.13と1.14の間の「中間」ですが、に変換するfloat
と、値は1.134999990463...
「中間」ではなくなり、1.13に近づき、最も近い値に丸められますfloat
に1.129999995232...
コードを使用する場合
y = roundf(x*100.0f)/100.0f;
「1.135」はに変換1.13と1.14の間で「ハーフウェイ」、ですがfloat
、値がされていない1.134999990463...
と「ハーフウェイ」はもはやですが、近い1.13が、間違ってのラウンドfloat
の1.139999985695...
の、より限定された精度のためにfloat
対double
。この誤った値は、コーディングの目的によっては正しいと見なされる場合があります。
浮動小数点数を丸めるためにこのマクロを作成しました。ヘッダーに追加する/ファイルであること
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
次に例を示します。
float x = ROUNDF(3.141592, 100)
xは3.14に等しい:)
double f_round(double dval, int n)
{
char l_fmtp[32], l_buf[64];
char *p_str;
sprintf (l_fmtp, "%%.%df", n);
if (dval>=0)
sprintf (l_buf, l_fmtp, dval);
else
sprintf (l_buf, l_fmtp, dval);
return ((double)strtod(l_buf, &p_str));
}
ここに n
は小数の数です
例:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
3)各ブランチでまったく同じことをする奇妙なif
/ else
ブロック、および4)の過度に複雑な使用によるsprintf
2番目のsprintf
呼び出しのフォーマット指定子の作成。.*
同じsprintf
呼び出しの引数としてdouble値と小数点以下の桁数を使用して渡す方が簡単です。
最初に、この質問に別の回答を追加する理由を正当化しようと思います。理想的な世界では、丸めはそれほど重要ではありません。ただし、実際のシステムでは、予期しない丸めが発生する可能性のあるいくつかの問題に対処する必要がある場合があります。たとえば、最終的な結果が四捨五入されて小数点以下2桁としてユーザーに表示される財務計算を実行している場合があります。これらの同じ値は、固定精度で小数点以下2桁を超える可能性があるデータベースに格納されます(さまざまな理由により、保持するのに最適な数はありません...各システムがサポートする必要がある特定の状況に依存します。単位あたりのペニーの端数です); そして、結果がプラス/マイナスのイプシロンである値に対して実行される浮動小数点計算。私はこれらの問題に直面し、長年にわたって自分の戦略を進化させてきました。私がすべてのシナリオに直面した、または最良の回答を得たと主張するつもりはありませんが、以下はこれらの問題を克服するこれまでの私のアプローチの例です。
次の丸め関数/メソッドを使用して、小数点以下6桁が浮動小数点数/倍精度浮動小数点数(特定のアプリケーションでは任意の決定)の計算に十分な精度であると見なされるとします。
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
結果の表示のために小数点以下2桁に丸めることは、次のように実行できます。
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
のval = 6.825
結果は6.83
期待どおりです。
のval = 6.824999
結果は6.82
です。ここでは、計算が正確に行われ6.824999
、小数点第7位がゼロであると仮定しています。
のval = 6.8249999
結果は6.83
です。9
この場合、小数点以下第7位にすると、Round(val,6)
関数は期待どおりの結果をもたらします。この場合、末尾にがいくつあってもかまいません9
。
のval = 6.824999499999
結果は6.83
です。最初のステップとして小数点以下8桁に丸めRound(val,8)
ます。つまり、計算された浮動小数点の結果がに計算さ6.8249995
れ6.824999499999...
ますが、内部的にはと表されます。
最後に、質問の例... val = 37.777779
はになり37.78
ます。
このアプローチは、次のようにさらに一般化できます。
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
ここで、Nは、float / doubleのすべての中間計算で維持される精度です。これは負の値でも機能します。このアプローチがすべての可能性に対して数学的に正しいかどうかはわかりません。
数値を丸める単純なCコード:
float n = 3.56;
printf("%.f", n);
これは出力します:
4
この関数は数値と精度を取り、丸められた数値を返します
float roundoff(float num,int precision)
{
int temp=(int )(num*pow(10,precision));
int num1=num*pow(10,precision+1);
temp*=10;
temp+=5;
if(num1>=temp)
num1+=10;
num1/=10;
num1*=10;
num=num1/pow(10,precision+1);
return num;
}
浮動小数点数をintに変換するには、ポイントを左シフトし、5より大きい条件をチェックします。
float
(およびdouble
)は10進浮動小数点ではないため、数値自体を正しく丸めることはできません。これらは2進浮動小数点であるため、小数点以下の位に丸めることは無意味です。ただし、出力を丸めることができます。