トップ回答者
BigInteger の計算(四則演算など)結果を小数点型で欲しいのですが,どのような方法がありますか?

質問
回答
-
VB .NET には割り算の演算子が2種類あります。
1つは「 / 」でこれは演算するときに分子と分母をdoubleに変換してから割り算を行って、結果もdoubleになります。
もう一つは「 \ 」で、こちらは変換されずにそのまま演算するため、両方とも整数であれば端数切捨ての割り算になり、結果も整数になります。
これに対して、BigIntegerの割り算はVBの割り算のように2種類の方法が定義されているわけではないので、整数のままの割り算で端数切り捨てになり、結果も整数になります。
ですからVB.Netの「 / 」と同じような考えでBigIntegerの割り算を行うと異なる結果になってしまいます。あと、2^127-1とやってますが、これは2^127という大きな値の結果のdouble型から1という小さい値を引いているために、差の桁数がdouble型の有効桁数を超過しており、情報落ちが発生しまています。
そのため2^127から1を引いた値になっていません。これは1の位が奇数になっているかで簡単に判定できます。
正しく演算するにはBigInteger型から1を引くという計算をする必要があります。また、掛け算を使う分には整数のまま計算できるので、割り算を使う回数を減らした方が演算誤差は少なくなります。
ですから分子を2で割るよりも、分母に2を掛ける方が正しい値に近くなります。
精度良く計算するには計算順も考慮するようにしましょう。
Imports System.Numerics Imports System.Numerics.BigInteger Module Module1 Sub Main() Dim Int128MaxError As System.Numerics.BigInteger = 2 ^ 127 - 1 Dim Int128Max As System.Numerics.BigInteger = BigInteger.Pow(2, 127) - 1 Console.WriteLine(Int128MaxError) Console.WriteLine(Int128Max) Dim d As Double = CDbl(Int128Max) / (CDbl(Int128Max) * 2) Console.WriteLine("d={0}", d) '余りをつかって小数点以下を別に計算する方法 Dim 小数桁用 As BigInteger = 2 ^ 128 Dim 余り As BigInteger = 0 Dim dv As Double = CDbl(BigInteger.DivRem(Int128Max, Int128Max * 2, 余り)) Dim re As Double = CDbl(BigInteger.Divide(余り * 小数桁用, Int128Max * 2)) / CDbl(小数桁用) d = dv + re '整数部と小数部を加算して最終結果にする Console.WriteLine("d={0}", d) End Sub End Module
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
すべての返信
-
質問者さんは Integer の場合と同じ結果になることを期待したと思いますが、BigInteger は違う結果になるようです。
Dim intValue As Integer = 100 Dim d As Double = (intValue / 2) / intValue Console.WriteLine("d = {0}", d) Dim Int128Max As System.Numerics.BigInteger = 100 d = (Int128Max / 2) / Int128Max 'Option Strict On は外さないとここでエラー Console.WriteLine("d = {0}", d) '結果は: 'd = 0.5 'd = 0
何故違う結果になるかは VB.NET に詳しい方に説明をお願いしたいと思います。
#実は、最初の方の d が 0.5 になるのはつい最近知りました。C# を使っている自分としては驚き。【追伸】
肝心の質問の答えを忘れていました。
d = CType(Int128Max / 2, Double) / CType(Int128Max, Double) Console.WriteLine("d = {0}", d) '結果は: 'd = 0.5
詳しくは以下の記事を見てください。
BigInteger Narrowing 変換 (BigInteger to Double)
https://msdn.microsoft.com/ja-jp/library/dd268205(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2- 編集済み SurferOnWww 2016年1月20日 5:21 追伸追加
-
VB User1 さま よろしく。
暗黙的な型変換 を用いずに、明示的な型変換表記 を使った方が良いと思いますし、
個人的にはそう書きたいです。
3-5 データ型変換 (3-5-2 明示的な型変換表記)
https://msdn.microsoft.com/ja-jp/library/dd314347.aspx
つまり、左辺右辺の変数や定数を皆同じ型に変換して記述します。あれ、投稿したら、 これは、aviator__ さまと同じ事になりますね。 重複です。
- 編集済み ShiroYuki_Mot 2016年1月20日 7:00 追記 と ハイパーリンク化
-
VB .NET には割り算の演算子が2種類あります。
1つは「 / 」でこれは演算するときに分子と分母をdoubleに変換してから割り算を行って、結果もdoubleになります。
もう一つは「 \ 」で、こちらは変換されずにそのまま演算するため、両方とも整数であれば端数切捨ての割り算になり、結果も整数になります。
これに対して、BigIntegerの割り算はVBの割り算のように2種類の方法が定義されているわけではないので、整数のままの割り算で端数切り捨てになり、結果も整数になります。
ですからVB.Netの「 / 」と同じような考えでBigIntegerの割り算を行うと異なる結果になってしまいます。あと、2^127-1とやってますが、これは2^127という大きな値の結果のdouble型から1という小さい値を引いているために、差の桁数がdouble型の有効桁数を超過しており、情報落ちが発生しまています。
そのため2^127から1を引いた値になっていません。これは1の位が奇数になっているかで簡単に判定できます。
正しく演算するにはBigInteger型から1を引くという計算をする必要があります。また、掛け算を使う分には整数のまま計算できるので、割り算を使う回数を減らした方が演算誤差は少なくなります。
ですから分子を2で割るよりも、分母に2を掛ける方が正しい値に近くなります。
精度良く計算するには計算順も考慮するようにしましょう。
Imports System.Numerics Imports System.Numerics.BigInteger Module Module1 Sub Main() Dim Int128MaxError As System.Numerics.BigInteger = 2 ^ 127 - 1 Dim Int128Max As System.Numerics.BigInteger = BigInteger.Pow(2, 127) - 1 Console.WriteLine(Int128MaxError) Console.WriteLine(Int128Max) Dim d As Double = CDbl(Int128Max) / (CDbl(Int128Max) * 2) Console.WriteLine("d={0}", d) '余りをつかって小数点以下を別に計算する方法 Dim 小数桁用 As BigInteger = 2 ^ 128 Dim 余り As BigInteger = 0 Dim dv As Double = CDbl(BigInteger.DivRem(Int128Max, Int128Max * 2, 余り)) Dim re As Double = CDbl(BigInteger.Divide(余り * 小数桁用, Int128Max * 2)) / CDbl(小数桁用) d = dv + re '整数部と小数部を加算して最終結果にする Console.WriteLine("d={0}", d) End Sub End Module
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)