読者です 読者をやめる 読者になる 読者になる

tmori3y2のブログ

主にWindowsのプログラムなど

LinqPadでCultureInfoをDumpして見た2 ~ 各地域と言語の数値フォーマットの詳細化

PCの周りにゴキブリ出現。3年ぶり。

tmori3y2.hatenablog.com

前回は、取り敢えずCultureInfoを列挙して、特にNumberFormatプロパティの小数点と桁数区切りをクエリしてLinqPadで結果を見た。

しかし、前回は、C++/Cのランタイムをいじった経験がある者にとって、注意に値する項目をスルーしていた。

  • NumberDecimalDigits: 書式N、および、Fのデフォルトの小数点以下の桁数。既定値2
  • NumberNegativePattern: 負数の書式

NumberFormatInfo クラス (System.Globalization)

小数点以下の桁数

C++/Cのprintfでは、既定で小数点以下6桁まで表示され、0でない数値がある場合はその値も表示することが出来たので、float/doubleの近似値を表示することが出来ていたが、.NETの書式文字列F/Nの既定動作ではNumberDecimalDigitsで丸めた結果が表示される。

丸めが、四捨五入なのか?銀行丸めなのかも気になるところ・・・

標準の数値書式指定文字列

書式文字列Fの場合は、G、もしくは、G0を使用することで、Decimalの有効な小数点以下の桁数を出力することが出来るが、精度桁数1以上の場合は、整数部と小数部の合計の桁数を表すので、Fとは違う結果になるので注意が必要である他、書式文字列Nの代替はない。

書式文字列Nで、C++/Cと同じように表示しようと思ったら、適切な桁数を指定するか、decimalのバイナリ表現から、小数点以下の桁数を取得するしかない。

qiita.com

バイナリ表現から取得した結果と書式文字列N/Fを組み合わせたときの小数点以下の桁数は、書式文字列Gの結果と一致する。

decimalの特徴としては、定数リテラル、および、文字列をParseした結果の小数点以下の桁数では、末尾の連続する0の桁数も保持されるという点だ。

これから、末尾の連続する0の桁数を取り除くには、Math.Round()などで丸める必要があることを意味している。

符号の位置

符号が後ろに付くパターンもあるが、これはNumberNegativePatternが3、もしくは、4の場合の地域で採用されている。

どういうことかというと、その地域の人は、自然に後ろに符号を付けて入力するケースがありうる。

当然だが、他所が対応しているなら対応した方が潜在的な競争力は上がる。

.NETで影響するのは、Decimal.TryParse()で指定するNumberStylesに何を許容するのか?という点だ。

Decimal.TryParse メソッド (String, NumberStyles, IFormatProvider, Decimal) (System)

Integerだと後ろの符号はParse出来ず、Numberだと常に受け付ける。

桁数区切り文字の間隔

これ以外にも、あまり見慣れぬNumberGroupSizesというのがある。

これは、古い書式で桁の位置によって、区切り文字の間隔が変わるので、配列になっている。

漢数字を常用している国は、4桁区切りの方が分かり易いという話もある。

しかし、builtinのCultureでは、現在は3桁で統一されていて、Parseする時は整数の途中で任意の間隔で挿入されても影響がないという代物なので、今回は特に扱わない。

クエリ用のヘルパークラス

前回はセパレータを文字と10進数の文字コードで表示していて分かりにくかった。

数値が具体的にどのように文字列化されるかも分かりにくいという問題もあった。

セパレータの文字コードの列挙型と拡張メソッド

セパレータの文字コードは列挙型で定義して、それから16進数文字コード+文字名からなる文字列を生成する拡張メソッドを作った。

CultureInfoの識別子とNumberFormatInfoのメンバーをまとめたクエリ用クラス

  • CultureInfoを識別するプロパティ
  • NumberInfoで重要そうなプロパティ
  • セパレータは16進数文字コード+文字名で表示
  • 整数4桁+小数3桁で丸めの仕様とデフォルトの小数点以下の桁数、符号の書式を判別するためのサンプル文字列

をメンバーに持つヘルパークラスを作成してクエリに使用する。

フィルタリング用の拡張メソッド

NumberFormatInfoのメンバーの組み合わせで、クエリをフィルタリングするための拡張メソッド。

  • Arabicスタイル?とアフガニスタンスタイル?は、数が少ないので複数の組み合わせを含む。
  • SIスタイルのEnglishバージョンは数が少ないが、Frenchバージョンの数が多いので、別にした。

クエリ用のLinqPadスクリプト

  • ヘルパーのNuGetPackageを作ったが、LinqPadはFree版を使用しているので、アセンブリを直接参照している
  • Neutral Cultureが200以上あったので除外した
  • Arabicスタイル?とアフガニスタンスタイル?、ペルシャスタイル?は、リファレンスにある通り、書式文字列Nのみが後ろに符号がつく
  • Arabicスタイル?の中には、丸め桁数が3桁のものが混ざっている
  • 丸めは、銀行丸目ではなく、四捨五入

といったことがわかる。

スクリプトは以下からどうぞ。

BlogSamples/LinqPadsamples at Article_20160804 · tmori3y2/BlogSamples · GitHub

NuGet Packageのプロジェクトは、以前作成したMSBuild用のTargetと一緒に置いています。

tmori3y2.hatenablog.com

BlogSamples/NuGetPackages at Article_20160804 · tmori3y2/BlogSamples · GitHub