Cの浮動小数点値を小数点以下2桁に制限するにはどうすればよいですか?


215

Cで浮動小数点値(37.777779など)を小数点以下2桁(37.78)に丸めるにはどうすればよいですか?


15
float(およびdouble)は10進浮動小数点ではないため、数値自体を正しく丸めることはできません。これらは2進浮動小数点であるため、小数点以下の位に丸めることは無意味です。ただし、出力を丸めることができます。
Pavel Minaev 2009

63
無意味ではありません。それは不正確です。かなり違いがあります。
Brooks Moses、

2
どのような丸めを期待していますか?ハーフアップまたは最も近い偶数への丸め?
Truthseeker Rangwan 2014

回答:


407

出力目的で数値を丸めるだけの場合は、"%.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進数値ではない可能性がありますが、非常に近いものになります。

丸め、および特に最も近い値に丸めるためのタイブレークルールの詳細については、ウィキペディアの丸めに関する記事を参照してください。


4
任意の精度への丸めをサポートするように変更できますか?

1
@slater「任意の精度」と言うとき、たとえば小数点以下2桁ではなく3桁に丸めることについて質問しますか、または制限のない精度の10進数値を実装するライブラリを使用しますか?前者の場合は、定数100への明らかな調整を期待してください。それ以外の場合は、使用している多精度ライブラリを使用して、上記とまったく同じ計算を行います。
デールハグルンド、2014年

2
@DaleHagglung前者、ありがとうございます。100をpow(10、(int)desiredPrecision)に置き換える調整ですか?

3
うん。小数点以下k桁で丸めるには、10 ^ kの倍率を使用します。これは、10進数の値を手動で書き出して10の倍数で操作すると簡単に確認できるはずです。値1.23456789で作業していて、小数点以下3桁に四捨五入したいとします。使用可能な操作は整数に丸められます。では、小数点の最初の3桁を移動して小数点の左側に移動するにはどうすればよいでしょうか。10 ^ 3を掛けていることは明らかです。これで、その値を整数に丸めることができます。次に、3つの下位桁を10 ^ 3で除算して戻します。
デールハグルンド、2014年

1
doublesどういうわけかこれで動作させることはできますか?私が望む仕事をしていないようです:((とを使用floorしてceil
Ms. Nobody

87

printfで%.2fを使用しています。小数点2桁のみを出力します。

例:

printf("%.2f", 37.777779);

出力:

37.77

精度が失われないため、この方法の方が適しています。
albert 2014

2
@albertこれには、オーバーフローの可能性があるため、float範囲の損失がないという利点もありval * 100ます。
chux-モニカを2014年

42

あなたが印刷の価値について話していると仮定すると、Andrew ColesonAraKの答えは正しいです:

printf("%.2f", 37.777779);

ただし、内部で使用するために数値を正確に37.78に丸めることを目的としている場合(たとえば、別の値と比較する場合)、浮動小数点数が機能する方法のため、これは良いアイデアではありません。通常、浮動小数点の等値比較を行いたい場合は、代わりにターゲット値+/-シグマ値を使用します。または、数値を既知の精度を持つ文字列としてエンコードし、それを比較します。

関連する質問に対するGreg Hewgillの回答のリンクを参照してください。これには、財務計算に浮動小数点を使用してはならない理由も含まれています。


1
質問の背後にある疑問(または疑問の背後にあるはずの疑問)に対処することに賛成。それはかなり重要なポイントです。
Brooks Moses、

実際、37.78は浮動小数点で正確に表すことができます。浮動小数点は、精度のために11から12桁です。これは、3778 377.8またはあらゆる種類の4桁の10進数を処理するのに十分なはずです。
匿名のホワイト

@HaryantoCiuええ、まあまあ、私は私の答えを少し編集しました。
John Carter、

動的精度:printf("%.*f", (int)precision, (double)number);
Minhas Kamal

24

これはどう:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);

4
-1:a)これは負の数に対しては機能しません(わかりました、例は正ですが、まだです)。b)フロートに正確な10進数値を格納することは不可能であることは言及していません
John Carter

32
@therefromhere:(a)あなたは正しい(b)これは何ですか?高校のテスト?
ダニール、

1
なぜ0.5を追加したのですか?
muhammad tayyab 2016年

1
端数処理規則に従う必要があります。
Daniil

1
@Daniilコメントのコンテキストでの丸めルールは、最も近い
Shmil The Cat

20
printf("%.2f", 37.777779);

C-stringに書き込みたい場合:

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);

@シナン:なぜ編集?@AraK:いいえ、サイズに注意する必要があります :)。snprintf()を使用してください。
aib 2009

1
@aib:/ ** /はCスタイルのコメントであり、質問にはCのタグが付けられているので、私は推測します
Michael Haren

5
C89は/ ** /スタイルのみを許可し、C99は//スタイルのサポートを導入しました。lame / oldコンパイラー(または強制C89モード)を使用すると、//スタイルを使用できなくなります。そうは言っても、2009年なので、CスタイルとC ++スタイルの両方を考えてみましょう。
Andrew Coleson

11

丸められたものが表現できない場合があるため(浮動小数点数の制限)、a floatを別floatの丸めに設定する方法はありませんfloat。たとえば、37.777779を37.78に丸めるが、最も近い表現可能な数は37.781であるとします。

ただし、フォーマット文字列関数を使用してを「丸める」ことができfloatます。


3
これは、「2つの浮動小数点数を分割して浮動小数点数を取得する方法はありません。分割された結果は表現できない可能性があるため」と言っても違いはありません。フロートは、加算のような基本的なものであっても、常に不正確です。常に、実際に得られるのは「正確な丸みを帯びた回答に最も近い浮動小数点数」であるという仮定です。
Brooks Moses、

私が意味したことは、float小数点以下の桁数をnに丸めることができず、結果が常に小数点以下の桁数になることを期待することです。あなたはまだあなたがfloat期待したものではなく、を取得します。
Andrew Keeton、

9

また、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();
}

次に、次のようなコードを使用して、小数点以下の桁数のある任意のdouble myDoubleを出力できますn

std::cout << prd(myDouble,n);

7

あなたはまだ使うことができます:

float ceilf(float x); // don't forget #include <math.h> and link with -lm.

例:

float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;

小数点でこの切り捨て(すなわち37が生成されます)、そして彼が2ヶ所へのラウンドに必要とした後、小数点。
Pavel Minaev 2009

ただし、小数点以下2桁に丸めることは些細なバリエーションです(ただし、回答ではまだ言及する必要があります。ZeroCool、編集を追加しますか?):float roundedValue = ceilf(valueToRound * 100.0)/ 100.0;
Brooks Moses、

スリープ状態に入ります:)
ZeroCool 2009

このソリューションの人気が高まっているのはなぜですか?これは、最小限のコードで正確に機能します。何か注意点はありますか?
アンディ

7

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つの変数をすべて作成する必要はありませんが、ロジックを確認できるように、変数はそのままにしておきます。おそらくもっと簡単な解決策がありますが、これは私にとってはうまく機能します。特に、必要に応じて小数点以下の桁数を調整できるためです。


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つの点で欠陥があるためです。

  • 一部の値では、浮動小数点数が不正確であるため、100を乗算すると丸めの方向を決定する10進数が4から5に、またはその逆に決定されるため、間違った方向に丸められます。
  • 一部の値では、乗算してから100で除算してもラウンドトリップしません。つまり、丸めが行われなくても、最終結果は正しくありません。

最初の種類のエラーを説明するために-丸め方向が時々間違っている-このプログラムを実行してみてください:

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 * 100863119242376661300.0二重のように正確に表現ではありません。最も近い値は863119242376661248.0です。これを100で割ると8631192423766612.0、最初の数とは異なる数になります。

うまくいけばroundf、小数点以下の桁数を四捨五入するためのの使用が無効になり、snprintf代わりに使用する必要があることを示す十分なデモンストレーションです。それがあなたにとって恐ろしいハックのように思えるなら、おそらくそれは基本的にCPythonが行うことであるという知識に安心するでしょう。


IEEE浮動小数点の奇妙さのおかげで、私の答えとそれに似たもので何がうまくいかないかの具体例として+1し、簡単な代替手段を提供します。私は、かなり昔から、浮動小数点値をラウンドトリップしても安全なように、印刷物や友人に多大な労力を費やしていることを周辺に認識していました。そのときに行われた作業がここに表示される可能性があると思います。
デールハグルンド

Ahem ...終わり近くにあるsaladという単語で申し訳ありません。編集するには遅すぎます。私が言うつもりだったのは、「printfや友人たちを安全にするために多大な努力を払った」ということでした
Dale Hagglund

4

使用する 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に向かって丸め、最も近いものに丸めますfloat1.129999995232...

「1.135」は1.13と1.14の間の「中間」ですが、に変換するfloatと、値は1.134999990463...「中間」ではなくなり、1.13に近づき、最も近い値に丸められますfloat1.129999995232...

コードを使用する場合

y = roundf(x*100.0f)/100.0f;

「1.135」はに変換1.13と1.14の間で「ハーフウェイ」、ですがfloat、値がされていない1.134999990463...と「ハーフウェイ」はもはやですが、近い1.13が、間違ってのラウンドfloat1.139999985695...の、より限定された精度のためにfloatdouble。この誤った値は、コーディングの目的によっては正しいと見なされる場合があります。


4

浮動小数点数を丸めるためにこのマクロを作成しました。ヘッダーに追加する/ファイルであること

#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))

次に例を示します。

float x = ROUNDF(3.141592, 100)

xは3.14に等しい:)


これは切り捨てられますが、質問は丸めを要求します。さらに、浮動小数点演算で丸め誤差が発生する可能性があります。
Eric Postpischil 2018

3
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

-1 4つの理由:1)説明の欠如、2)バッファオーバーフローに対する脆弱性-これはオーバーフローするため、非常に大きな場合、クラッシュする可能性がありますdval3)各ブランチでまったく同じことをする奇妙なif/ elseブロック、および4)の過度に複雑な使用によるsprintf2番目のsprintf呼び出しのフォーマット指定子の作成。.*同じsprintf呼び出しの引数としてdouble値と小数点以下の桁数を使用して渡す方が簡単です。
Mark Amery

3

コード定義:

#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

0

最初に、この質問に別の回答を追加する理由を正当化しようと思います。理想的な世界では、丸めはそれほど重要ではありません。ただし、実際のシステムでは、予期しない丸めが発生する可能性のあるいくつかの問題に対処する必要がある場合があります。たとえば、最終的な結果が四捨五入されて小数点以下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.82499956.824999499999...ますが、内部的にはと表されます。

最後に、質問の例... val = 37.777779はになり37.78ます。

このアプローチは、次のようにさらに一般化できます。

double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));

ここで、Nは、float / doubleのすべての中間計算で維持される精度です。これは負の値でも機能します。このアプローチがすべての可能性に対して数学的に正しいかどうかはわかりません。



-1

...またはライブラリなしで昔ながらの方法でそれを行うことができます:

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;

もちろん、あなたが番号から余分な情報を削除したい場合は。


-2

この関数は数値と精度を取り、丸められた数値を返します

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より大きい条件をチェックします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.