忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

金額計算(会計処理など)は Decimal 型で、科学計算には Double 型 を使用する

通常プログラムを組んでいて計算処理を行う場合にはそこまで気にすることは無いのですが、 小数点以下の計算で誤差が生じない様にするため金額計算では Decimal 型変数を使い、 科学計算的な場合には Double 型変数を使用する様にしています。
また、ループの指標等、少ない桁の整数の場合には Integer 型変数を使い、 桁数が多い整数の計算では Long 型変数を使います。

尚、各データ型の内容ですが以下の様になっています。

■各データ型について

データ型型説明サイズ有効桁数値の範囲
Integer 整数型 4バイト -2147483648 ~ 2147483647
Long 長整数型 8バイト -9223372036854775808 ~ 9223372036854775807
Single 単精度浮動小数点型 4バイト 7桁 -3.40282347E+38 ~ 3.40282347E+38
Double 倍精度浮動小数点型 8バイト 15桁 -1.7976931348623157E+308 ~ 1.7976931348623157E+308
Decimal 10進型 16バイト 28桁 -7.9228162514264337594E+28 ~ 7.9228162514264337594E+28


■Single 型と Double 型について

浮動小数点型は内部的に符号部、指数部、仮数部に分けてデータを持っているそうです。 Single 型は符号部(1ビット)、指数部(8ビット)、仮数部(23ビット) で Double 型は符号部(1ビット)、指数部(11ビット)、仮数部(52ビット) で構成されているらしいです。 (詳しくは IEEE 754 の説明にあたって下さい。)

SingleDouble 型の計算を行わせてどのくらいの精度があるのかを見てみます。
以下のソースでは小数「0.1」を10回加算して結果を有効桁20桁までで表示させています。 「0.1」と言う数値は2進数にすると「0.00011001100110011…」の循環小数になることはよく知られています。 これを10回加算して結果が数学的に「1」になるでしょうか?

    Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
        '単精度数値変数
        Dim sngVal As Single = 0
        '倍精度数値変数
        Dim dblVal As Double = 0
        'それぞれ「0.1」を10回加算
        For i As Integer = 1 To 10
            sngVal += 0.1F  'リテラルで「F」指定は単精度数値
            dblVal += 0.1D  'リテラルで「D」指定は倍精度数値
        Next
        '結果が「1」になることを期待?
        Console.WriteLine("Single Data = {0}", sngVal.ToString("G20"))
        Console.WriteLine("Double Data = {0}", dblVal.ToString("G20"))
    End Sub

実行結果がコンソールには以下様に表示されます。 やはり「1」にはなりません。 リテラルで加算される「0.1」は循環小数なので内部的にはどこかで丸めが発生しているからです。

Single Data = 1.00000012
Double Data = 0.99999999999999989

Single 型では有効桁数が少ないので、小数以下4桁どうしの乗算を行うと小数以下8桁まで必要になりますが、 小数以下8桁以降は信頼できない値になります。これでは科学計算には向かないので、 Double 型を使うことになります。

■Decimal 型について

上記の処理を Decimal 型に変えて処理します。

    Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
        '10新型数値変数
        Dim decVal As Decimal = 0
        '「0.1」を10回加算
        For i As Integer = 1 To 10
            decVal += 0.1D  'リテラルで「D」指定は10新型数値
        Next
        '結果が「1」になることを期待?
        Console.WriteLine("Decimal Data = {0}", decVal.ToString("G20"))
        decVal = 0.1D
        Console.WriteLine("Decimal Data = {0}", decVal.ToString("G30"))
    End Sub

実行結果がコンソールには以下様に表示されます。 確かにこの程度の小数以下の精度では問題無い様です。

Decimal Data = 1
Decimal Data = 0.1

Decimal 型はデータサイズも大きく内部での計算は他の型よりも時間が掛かりますが、 会計処理などでは小数点以下の精度に正確性が求められますので、この型を使います。
私が組むプログラムは OA 系の処理が多いので数値は全て Decimal 型を使っています。 まれに、工場のライン監視などで計測データが必要なときは Double 型も使いますが、 そこまで速度が要求されなければ Decimal 型を使います。

■リテラル(直値)宣言の注意

プログラム上で直接値(リテラル)を宣言する場合に注意が必要です。 値を正しく宣言しないと期せずして異なるデータ型で処理されてることがあります。
以下のソースは小数点付きで宣言した場合と、小数点無し(整数)で宣言した場合に、数値の後ろにデータ型を指示する文字を 付加する場合としない場合の例を示します。

    Private Sub btnTest_Click(sender As Object, e As EventArgs) Handles btnTest.Click
        '各種データ型を入れるため「Object」型
        Dim objTest As Object = Nothing

        'Decimal型のリテラル
        objTest = 0.1
        Console.WriteLine("Type = {0}", objTest.GetType.ToString)

        'Single型のリテラル
        objTest = 0.1F
        Console.WriteLine("Type = {0}", objTest.GetType.ToString)

        'Integer型のリテラル
        objTest = 12345
        Console.WriteLine("Type = {0}", objTest.GetType.ToString)

        'Long型のリテラル
        objTest = 12345L
        Console.WriteLine("Type = {0}", objTest.GetType.ToString)
    End Sub

実行結果がコンソールには以下様に表示されます。

Type = System.Double
Type = System.Single
Type = System.Int32
Type = System.Int64

■各データ型のリテラル指定について

データ型型説明リテラル指定
Integer 整数型 I
Long 長整数型 L
Single 単精度浮動小数点型 F
Double 倍精度浮動小数点型 R
Decimal 10進型 D

関連する記事

指定した精度の桁数に数値を切り上げ    :[Math.Ceiling,Math.Floor]
文字列から数値型への変換(parse - tryparse)
オブジェクト型から数値型への変換(TryParse)











PR

コメント

コメントを書く