データ型のトラブルシューティング

更新 : 2008 年 7 月

ここでは、組み込みデータ型の演算で起きる一般的な問題についていくつか説明します。

浮動小数点式での比較が等しくならない

浮動小数点数 (単精度浮動小数点型 (Single) (Visual Basic) および 倍精度浮動小数点数型 (Double) (Visual Basic)) を扱うときは、数値が 2 進数の小数として格納されることに注意してください。つまり、2 進数 (k と n が整数である k / (2 ^ n) の形式) ではない値は正確に格納できません。たとえば、0.5 (= 1/2) や 0.3125 (= 5/16) は正確な値を格納できますが、0.2 (= 1/5) や 0.3 (= 3/10) は近似値のみを格納できます。

このような不正確性があるため、浮動点演算の結果が常に正確であると見なすことはできません。特に、理論的には等しい 2 つの値が、わずかに異なる数値として表現される場合があります。

浮動小数点の値を比較するには

  1. System 名前空間にある Math クラスの Abs メソッドを使用して、2 つの数値の絶対的な差を計算します。

  2. 許容できる最大の差を決めます。2 つの数値の差がこれより小さい場合、2 つが等価であると見なしても実用上の問題がないような値にしてください。

  3. 差の絶対値と許容可能な差を比較します。

次のコード例は、2 つの Double 値を比較する誤った方法と正しい方法を示しています。

Dim oneThird As Double = 1.0 / 3.0
Dim pointThrees As Double = 0.333333333333333

' The following comparison does not indicate equality.
Dim exactlyEqual As Boolean = (oneThird = pointThrees)

' The following comparison indicates equality.
Dim closeEnough As Double = 0.000000000000001
Dim absoluteDifference As Double = Math.Abs(oneThird - pointThrees)
Dim practicallyEqual As Boolean = (absoluteDifference < closeEnough)

MsgBox("1.0 / 3.0 is represented as " & oneThird.ToString("G17") _
  & vbCrLf & "0.333333333333333 is represented as " _
  & pointThrees.ToString("G17") _
  & vbCrLf & "Exact comparison generates " & CStr(exactlyEqual) _
  & vbCrLf & "Acceptable difference comparison generates " _
  & CStr(practicallyEqual))

この例では、Double 構造体の ToString メソッドを使用しているので、CStr キーワードを使用する場合より高い精度が得られます。既定では 15 桁ですが、"G17" 形式を使うと 17 桁に拡張されます。

Mod 演算子が正確な結果を返さない

浮動小数点数が正確に格納されないことがあるため、オペランドの少なくとも 1 つが浮動小数点数である場合に、Mod 演算子 (Visual Basic) が予期しない結果を返す可能性があります。

10 進型 (Decimal) (Visual Basic) は、浮動小数点表現を使用しません。Single および Double に正確に格納されない多くの値が、Decimal には正確に格納されます (たとえば 0.2 や 0.3)。Decimal に格納すると浮動小数点に格納した場合より演算は遅くなりますが、それによって得られる精度はパフォーマンスを犠牲にする価値があります。

浮動小数点数の整数剰余を求めるには

  1. 変数を Decimal で宣言します。

  2. リテラルの型文字 D を使って、強制的にリテラルを Decimal にします。これは、値が Long データ型で扱えない大きさである場合に備えるためです。

浮動小数点数のオペランドによって誤差が生じる例を次に示します。

Dim two As Double = 2.0
Dim zeroPointTwo As Double = 0.2
Dim quotient As Double = two / zeroPointTwo
Dim doubleRemainder As Double = two Mod zeroPointTwo

MsgBox("2.0 is represented as " & two.ToString("G17") _
  & vbCrLf & "0.2 is represented as " & zeroPointTwo.ToString("G17") _
  & vbCrLf & "2.0 / 0.2 generates " & quotient.ToString("G17") _
  & vbCrLf & "2.0 Mod 0.2 generates " _
  & doubleRemainder.ToString("G17"))

Dim decimalRemainder As Decimal = 2D Mod 0.2D
MsgBox("2.0D Mod 0.2D generates " & CStr(decimalRemainder))

この例では、Double 構造体の ToString メソッドを使用しているので、CStr キーワードを使用する場合より高い精度が得られます。既定では 15 桁ですが、"G17" 形式を使うと 17 桁に拡張されます。

zeroPointTwo は Double 型なので、0.2 を格納すると、小数部分が永久に繰り返される 0.20000000000000001 という値になります。この値で 2.0 を除算すると、答えは 9.9999999999999995 となり、剰余が 0.19999999999999991 となります。

decimalRemainder の式では、リテラルの型文字 D が指定されているため、2 つのオペランドは 10 進型 (Decimal) になり、0.2 が正確に表現されます。したがって、Mod 演算子は、予期されるとおりの剰余 0.0 を生成します。

decimalRemainder を Decimal で宣言するだけでは十分ではないことに注意してください。強制的にリテラルを Decimal にすることも必要です。そうしなければ、リテラルは既定で Double を使用し、decimalRemainder は doubleRemainder と同じ不正確な値を受け取ります。

Boolean 型が数値型に正確に変換されない

ブール型 (Boolean) (Visual Basic) 値は数字としては格納されず、格納された値は数字と等しくなりません。以前のバージョンとの互換性維持のため、Visual Basic には、Boolean 型と数値型を相互に変換するための変換用のキーワード (CType 関数、CBool、CInt など) が用意されています。ただし、他の言語では、この変換が別の方法で実行されることもあります。たとえば、.NET Framework のメソッドがそうです。

コードを作成する際、True および False に相当する数値を利用しないようにしてください。ブール型 (Boolean) の変数に対しては、できるだけ本来の論理値だけを使用してください。ブール型 (Boolean) と数値を混在させる必要がある場合は、選択した変換メソッドを十分理解してから行ってください。

Visual Basic における変換

変換用の CType キーワードまたは CBool キーワードを使って数値型を Boolean 型に変換すると、0 は False になり、それ以外のすべての値は True になります。変換用のキーワードを使って Boolean 値を数値型に変換すると、False は 0 になり、True は -1 になります。

フレームワークにおける変換

System 名前空間にある Convert クラスの ToInt32 メソッドを使うと、True は +1 に変換されます。

Boolean 値を数値データ型に変換する必要がある場合は、使用する変換メソッドに注意してください。

文字リテラルがコンパイル エラーを発生させる

型宣言文字がない場合、Visual Basic ではデータ型がリテラルであると見なされます。文字リテラル (二重引用符 (" ") で囲まれた文字) の既定の型は、String です。

String データ型は、文字型 (Char) (Visual Basic) に拡張されません。つまり、リテラルを Char 型の変数に代入する場合には、縮小変換を行うか、強制的にリテラルを Char 型にする必要があります。

Char リテラルを作成して変数または定数に代入するには

  1. 変数または定数を Char で宣言します。

  2. 文字値は二重引用符 (" ") で囲みます。

  3. 二重引用符の後にリテラルの型文字 C を付けて、強制的にリテラルを Char 型にします。これが必要なのは型チェック スイッチ (Option Strict ステートメント) が On の場合ですが、常にこのようにすることを推奨します。

次のコード例は、リテラルを Char 型の変数に代入する誤った方法と正しい方法を示しています。

Option Strict On
Dim charVar As Char
' The following statement attempts to convert a String literal to Char.
' Because Option Strict is On, it generates a compiler error.
charVar = "Z"
' The following statement succeeds because it specifies a Char literal.
charVar = "Z"c
' The following statement succeeds because it converts String to Char.
charVar = CChar("Z")

縮小変換は実行時にエラーになる可能性があるため、常にリスクを伴います。たとえば、String から Char への変換は、String 値が 1 文字より多い場合にエラーになります。したがって、C 型文字を使う方が推奨されるプログラミング方法です。

文字列変換が実行時にエラーになる

文字列型 (String) (Visual Basic) が拡張変換にかかわることはめったにありません。String が拡張されるのは同じ文字列型か Object だけであり、Char および Char() (Char 配列) だけが String に拡張されます。その理由は、String の変数および定数には、他のデータ型には格納できない値が格納できるためです。

型チェック スイッチ (Option Strict ステートメント) が On の場合、コンパイラは、すべての暗黙の縮小変換を認めません。これには、String がかかわる縮小変換も含まれます。その場合でも、CStr や CType 関数 などの変換キーワードをコードで使用できます。これらがあると、.NET Framework は変換を試みます。

メモ :

For Each…Next コレクション内の要素からループ制御変数への変換に対して縮小変換エラーが抑制されます。詳細および例については、「For Each...Next ステートメント (Visual Basic)」の「縮小変換」セクションを参照してください。

縮小変換の保護

縮小変換の欠点は、実行時にエラーになる可能性があることです。たとえば、String 変数に "True" または "False" 以外の値が格納されている場合、Boolean には変換できません。区切り記号が含まれている場合は、数値型への変換はエラーになります。変換先のデータ型で許容される値が常に String 変数に格納されるという確信がない限り、変換は行わないでください。

String から別のデータ型に変換する必要がある場合、最も安全な手順は、変換を試みる部分を Try...Catch...Finally ステートメント (Visual Basic) で囲むことです。このようにすると、ランタイム エラーを処理することができます。

文字配列

単一の Char と Char 要素の配列は、どちらも String に拡張されます。しかし、String は Char() に拡張されません。String 値を Char 配列に変換するには、System.String クラスの ToCharArray メソッドを使用します。

無意味な値

一般に、String 値は他のデータ型にとって無意味な値です。変換は不自然であり、危険です。String 変数は、できるだけ本来の文字の羅列だけに使うようにしてください。他の型でこれに相当する値に依存するコードは書かないようにしてください。

参照

概念

Visual Basic におけるデータ型

型文字

Visual Basic での型宣言を省略したプログラミング

.NET Framework 型のデータ型

データ型の有効な使用方法

参照

データ型の概要 (Visual Basic)

データ型変換関数

その他の技術情報

データ型の実装

Visual Basic における型変換

履歴の変更

日付

履歴

理由

2008 年 7 月

縮小変換および For Each…Next に関する説明を追加。

カスタマ フィードバック