Cでfloat(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進値ではないかもしれませんが、それらは非常に近いでしょう。
四捨五入、特に四捨五入に関する最も近い四捨五入の規則については、 丸めに関する記事 を参照してください。
printf("%.2f", 37.777779);
印刷の価値を四捨五入することについて話しているとすると、 Andrew Coleson と AraK の答えは正しいです。
printf("%.2f", 37.777779);
ただし、内部使用のために数値を正確に37.78に丸めることを目的としている場合(たとえば、他の値と比較するため)、浮動小数点数の機能のため、これはお勧めできません。浮動小数点について等価比較をしたいのであれば、代わりに目標値+/-シグマ値を使用してください。あるいは、数値を既知の精度の文字列としてエンコードし、それを比較します。
Greg Hewgillの関連質問 への回答のリンクを参照してください。これは、財務計算に浮動小数点を使用しない理由も説明しています。
これはどう:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
C文字列に書きたい場合:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
丸められたfloat
は表現できない可能性があるため(浮動小数点数の制限)、float
を別のfloat
に丸める方法はありません。たとえば、37.777779を37.78に丸めるとしますが、最も近い表現可能な数値は37.781です。
ただし、フォーマット文字列関数を使用して、float
を「丸める」ことができます。
また、C++を使用している場合は、次のような関数を作成するだけで済みます。
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
このようなコードを使用して、小数点の後にmyDouble
の位置を持つ任意のダブルn
を出力できます。
std::cout << prd(myDouble,n);
あなたはまだ使用することができます:
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
ファミリーの関数を使用します。値をfloatとして取得したい場合でも、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, "%.2f", 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", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f", res1);
printf("Rounded with round, then divided: %.50f", res2);
}
次の出力が表示されます。
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
開始時の値は0.015未満であったため、小数点以下2桁に丸めた場合の数学的に正しい答えは0.01です。もちろん、0.01はdoubleとして表現できるexactlyではありませんが、結果は0.01に最も近いdoubleになると予想されます。 snprintf
を使用するとその結果が得られますが、round(100 * x) / 100
を使用すると0.02になりますが、これは間違っています。どうして? 100 * x
は結果として正確に1.5を与えるためです。したがって、100を乗算すると、正しい方向が丸められます。
second種類のエラーを示すために-* 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);
}
現在、私たちの数には小数部分すらありません。これは整数値であり、double
型で格納されています。ですから、丸めた後の結果は、元の数値と同じになるはずですよね?
上記のプログラムを実行すると、以下が表示されます。
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
おっとっと。 snprintf
メソッドは再び正しい結果を返しますが、乗算してから丸めて分割するアプローチは失敗します。これは、数学的に正しい8631192423766613.0 * 100
、863119242376661300.0
の値がdoubleとして正確に表現できないためです。最も近い値は863119242376661248.0
です。それを100で除算すると、8631192423766612.0
が得られます。これは、開始時とは異なる数値です。
うまくいけば、roundf
を使用して小数点以下の桁数に丸めることができないことを示す十分なデモンストレーションであり、代わりにsnprintf
を使用する必要があります。それがあなたにとって恐ろしいハックのように感じるなら、おそらくそれが 基本的にCPythonが行うこと であるという知識に安心するでしょう。
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
float roundf(float x)
を使用してください。
「ラウンド関数は、現在の丸め方向に関係なく、引数を浮動小数点形式の最も近い整数値に丸め、ゼロから半分の場合を四捨五入します。」 C11dr§7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
あなたのfloat
の実装によっては、途中であるように見えるかもしれない数はそうではありません。浮動小数点は通常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に近くなり、1.120000004768...
の最も近いfloat
に丸められます。
"1.125"は1.12と1.13の間の "中途半端"です。float
に変換すると、値は正確に1.125
になり、 "中途半端"になります。偶数規則との関連性のために1.13に丸められ、1.129999995232...
の最も近いfloat
に丸められます
"1.135"は1.13と1.14の間の "中間"ですが、float
に変換すると、値は1.134999990463...
になり、もはや "half-way"にはなりませんが、1.13に近くなり、1.129999995232...
の最も近いfloat
に丸められます。
コードが使用されている場合
y = roundf(x*100.0f)/100.0f;
"1.135"は1.13と1.14の間では "中途半端"ですが、float
に変換すると、値は1.134999990463...
になり、 "中途半端"ではなくなり、1.13に近づきますが誤っていますfloat
とfloat
の精度は限られているため、1.139999985695...
のdouble
に丸めます。コーディングの目的によっては、この誤った値が正しいと見なされることがあります。
浮動小数点数を丸めるためにこのマクロを作りました。あなたのヘッダー/ファイルの存在にそれを追加
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
これが一例です。
float x = ROUNDF(3.141592, 100)
xは3.14と同じです。
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
私が最初にこの質問にさらに別の答えを追加する私の理由を正当化しようとしましょう。理想的な世界では、丸めはそれほど重要ではありません。ただし、実際のシステムでは、四捨五入が発生する可能性のあるいくつかの問題に対処する必要があるかもしれません。たとえば、最終的な結果が四捨五入されて小数点第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
です。この場合、小数点第7位が9
であるため、Round(val,6)
関数は期待される結果をもたらします。この場合、末尾に9
sをいくつでも含めることができます。
val = 6.824999499999
の場合、結果は6.83
です。最初のステップとして小数点以下第8位に丸める、すなわちRound(val,8)
は、計算された浮動小数点の結果が6.8249995
に計算されるが、内部的には6.824999499999...
として表される1つの厄介なケースを扱います。
最後に、質問...val = 37.777779
の例は37.78
になります。
このアプローチは、さらに一般化することができます。
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
ここで、Nはfloat/doubleのすべての中間計算で維持される精度です。これは負の値にも作用します。このアプローチがすべての可能性に対して数学的に正しいかどうかはわかりません。
...あるいは、ライブラリを使わずに昔ながらの方法でそれを実行できます。
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
あなたが番号から余分な情報を削除したい場合はもちろんです。