データ型のトラブルシューティング (Visual Basic)

このページでは、組み込みデータ型に対して操作を実行するときに発生する可能性がある一般的な問題をいくつか示します。

浮動小数点式を比較したときに等しくならない

浮動小数点数 (Single データ型および Double データ型) を扱うときは、それらがバイナリの分数として格納されることに注意してください。 つまり、これらは、バイナリの分数 (形式は k / (2 ^ n)、k と n は整数) ではない数量表現を保持できません。 たとえば、0.5 (= 1/2) と 0.3125 (= 5/16) は正確な値を保持できますが、0.2 (= 1/5) と 0.3 (= 3/10) は概算値しか保持できません。

この誤差のため、浮動小数点値の演算では、正確な結果かどうか信頼できません。 特に、理論的には等しい 2 つの値の表現が多少異なる場合があります。

浮動小数点数の数量を比較するには
1. System 名前空間の Math クラスの Abs メソッドを使用して、差の絶対値を計算します。
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 演算子は予期しない結果を返す可能性があります。

Decimal データ型では、浮動小数点表現は使用されません。 SingleDouble で正確ではない多くの数値は、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 桁に拡張されます。

zeroPointTwoDouble であるため、0.2 に対する値は、無限に連続するバイナリ分数値で、値 0.20000000000000001 が格納されます。 2\.0 をこの数で割ると 9.9999999999999995 になり、剰余が 0.19999999999999991 になります。

decimalRemainder の式では、リテラルの型文字 D によって両方のオペランドが強制的に Decimal になり、0.2 の表現は正確です。 したがって、Mod 演算子では、0.0 という予期される剰余が得られます。

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

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

Boolean データ型の値は数値として格納されず、格納された値は数値と等価であると見なされません。 以前のバージョンとの互換性のために、Visual Basic は変換キーワード (CType 関数CBoolCInt など) を使用して、Boolean と数値型の間で変換を行います。 ただし、その他の言語では、.NET Framework メソッドと同様に、これらの変換が異なる方法で実行されることがあります。

TrueFalse に対して等価の数値に依存するコードを記述することは避けてください。 可能な限り、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 データ型に拡大変換されません。 つまり、Char 変数にリテラルを代入する場合は、縮小変換を行うか、リテラルを強制的に Char 型にする必要があります。

変数または定数に代入する Char リテラルを作成するには
1. 変数または定数を Char として宣言します。
2. 文字値を二重引用符 (" ") で囲みます。
3. 閉じの二重引用符の後にリテラルの型文字 C を指定して、リテラルを強制的に Char にします。 これは型チェック スイッチ (Option Strict ステートメント) が On の場合に必須ですが、どのような場合でもお勧めします。

次の例では、Char 変数へのリテラルの代入の失敗と成功の両方を示します。

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 値に複数の文字が含まれている場合です。 そのため、C 型文字を使用する方が適切なプログラミングです。

実行時に文字列変換が失敗する

String データ型は、非常に多くの拡大変換に関連します。 String はそれ自体と Object に拡大変換され、CharChar() (Char 配列) のみが String に拡大変換されます。 これは、String の変数と定数には、他のデータ型に含めることができない値が含まれている可能性があるためです。

型チェック スイッチ (Option Strict ステートメント) が On のとき、コンパイラによって暗黙の縮小変換がすべて禁止されます。 これには、String に関係するものも含まれます。 .NET Framework が変換を試行するように指示する変換キーワード (CStrCType 関数など) をコードで引き続き使用できます。

Note

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

縮小変換の保護

縮小変換の欠点は、実行時にエラーが発生する可能性があることです。 たとえば、String 変数に "True" または "False" 以外が含まれる場合は、Boolean に変換できません。 区切り文字が含まれている場合は、どの数値型への変換も失敗します。 String 変数が、変換先の型で受け入れ可能な値を常に保持していることがわかっている場合を除き、変換を試行しないでください。

String から別のデータ型に変換する必要がある場合、最も安全な手順は、Try...Catch...Finally ステートメントに変換の試行を含めることです。 これにより、実行時エラーに対処できます。

文字配列

1 つの Char および Char 要素の配列 1 つは、どちらも String に拡大変換されます。 ただし、StringChar() に拡大変換されません。 String 値を Char 配列に変換するには、System.String クラスの ToCharArray メソッドを使用できます。

意味のない値

一般に、String の値は他のデータ型では意味がなく、変換は非常に人為的で危険です。 可能な限り、String 変数には、仕様で定められている文字シーケンス以外の値を使用しないようにしてください。 他の型の等価の値に依存するコードを記述することは避けてください。

関連項目