C#

C# の string.Format() の丸め方式

More than 1 year has passed since last update.

※C# のstring.Format() に数値を与えた時の丸め方式について他の方と会話した際、まとまっているページが見当たらなかったので情報提供用にまとめました。(+ 会話時に誤った説明をしてしまっていたので、整理用)

string.Format() で数値を文字列化したときには、端数は四捨五入で丸められます。

var s = string.Format("{0:0.000}", 1.2345); // = "1.235"

これは「カスタム数値書式指定文字列」という、数値を文字列化する際の指定の挙動によるものです。上の例の「0.000」の部分が書式指定で、この場合「整数 + "." + 小数桁3桁の形式にする」という指定をしたことになり、「0」("0" カスタム指定子) で指定した桁をあふれた端数の部分が四捨五入で丸められます。

日本語版のMSDN だと


指定子が "00" の場合、値は一の位で丸められ、小数点以下のゼロは常に切り捨てられます。 たとえば、"00" を指定して 34.5 を書式設定すると、結果の値は 35 になります。


という切り捨てなのか四捨五入なのか判断しづらい説明になっていますが、英語版のMSDN だと


The "00" specifier causes the value to be rounded to the nearest digit preceding the decimal, where rounding away from zero is always used. For example, formatting 34.5 with "00" would result in the value 35.


となっており、「常に四捨五入される」(rounding away from zero is always used) と説明されています。

※ away from zero (round half away from zero) は「ゼロから遠い方に丸める」という意味で、0.5 を 整数に丸めると 1 に、-0.5 を整数に丸めると -1 になることを示しています。

Math.Round()MidpointRounding.AwayFromZero を指定しない場合や、Convert.ToInt32() では偶数丸め (最近接偶数への丸め / round half to even) が行われ、 0.5 や -0.5 を整数に丸めると 0 に、1.5 を整数に丸めると 2 になります (一番近い偶数に丸められる)。


四捨五入にならないケース

MSDNの説明では上記ですが、「String.Formatで四捨五入させないほう方」というMSDNのフォーラムのスレッドに、四捨五入にならないケースが紹介されています。


まず、四捨五入ではありません。

例えば↓は、"0.02"になります。

string str = string.Format("{0:#.00}", 0.01499999999999999);


(実際には 整数部に "#" を用いているので "0.02" ではなく ".02" になります)

精度の高い(桁数の多い)実数を文字列化する場合は予期しない値になることがありそうです。


  • 上記の例の「0.01499999999999999」は double型として扱われます。double型の有効桁数は15~16桁で、「0.01499999999999999」は小数桁が17桁あるので、そのせいでおかしくなっているように思います。小数桁を16桁にした「0.0149999999999999」だと、期待通り ".01" になります。

  • 実際にコードを動かしてstring.Format() 内をステップインで辿っていくと内部で Number.FormatDouble() というメソッドによって文字列化が行われていることを確認できますが、どのように文字列化しているかはわかりませんでした。(Number.FormatDouble() の中にステップインできないので詳細が確認できないのと、Number.FormatDouble() の公式の説明が見つけられない)

  • 0.01499999999999999 を Math.Round(0.01499999999999999, 2, MidpointRounding.AwayFromZero) で四捨五入した場合は、0.01 になります。


  • string.Format("{0:#.00}", 0.01499999999999999m) と decimal型(m) にすると ".01" になります。