Troubleshooting Data Types
This page lists some common problems that can occur when performing operations on intrinsic data types.
Floating-Point Expressions Do Not Compare as Equal
When you work with floating-point numbers (Single Data Type (Visual Basic) and Double Data Type (Visual Basic)), keep in mind that they are stored as binary fractions. This means they cannot hold an exact representation of any quantity that is not a binary fraction (of the form k / (2 ^ n) where k and n are integers). For example, 0.5 (= 1/2) and 0.3125 (= 5/16) can be held as precise values, while 0.2 (= 1/5) and 0.3 (= 3/10) can be only approximations.
Because of this imprecision, you cannot rely on exact results when you operate on floating-point values. In particular, two values that are theoretically equal might have slightly different representations.
To compare floating-point quantities
Calculate the absolute value of their difference, using the Abs method of the Math class in the System namespace.
Determine an acceptable maximum difference, such that you can consider the two quantities to be equal for practical purposes if their difference is no greater.
Compare the absolute value of the difference to the acceptable difference.
The following example demonstrates both improper and proper comparison of two Double values.
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))
The preceding example uses the ToString method of the Double structure so that it can specify greater precision than the CStr keyword uses. The default is 15 digits, but the "G17" format extends it to 17 digits.
Mod Operator Does Not Return Accurate Result
Because of the imprecision of floating-point storage, the Mod Operator (Visual Basic) can return an unexpected result when at least one of the operands is floating-point.
The Decimal Data Type (Visual Basic) does not use floating-point representation. Many numbers that are inexact in Single and Double are exact in Decimal (for example 0.2 and 0.3). Although arithmetic is slower in Decimal than in floating-point, it might be worth the performance decrease to achieve greater precision.
To find the integer remainder of floating-point quantities
Declare variables as Decimal.
Use the literal type character D to force literals to Decimal, in case their values are too large for the Long data type.
The following example demonstrates the potential imprecision of floating-point operands.
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))
The preceding example uses the ToString method of the Double structure so that it can specify greater precision than the CStr keyword uses. The default is 15 digits, but the "G17" format extends it to 17 digits.
Because zeroPointTwo
is Double, its value for 0.2 is an infinitely repeating binary fraction with a stored value of 0.20000000000000001. Dividing 2.0 by this quantity yields 9.9999999999999995 with a remainder of 0.19999999999999991.
In the expression for decimalRemainder
, the literal type character D
forces both operands to Decimal, and 0.2 has a precise representation. Therefore the Mod operator yields the expected remainder of 0.0.
Note that it is not sufficient to declare decimalRemainder
as Decimal. You must also force the literals to Decimal, or they default to Double and decimalRemainder
receives the same inaccurate value as doubleRemainder
.
Boolean Type Does Not Convert to Numeric Type Accurately
Boolean Data Type (Visual Basic) values are not stored as numbers, and the stored values are not intended to be equivalent to numbers. For compatibility with previous versions, Visual Basic provides conversion keywords (CType Function, CBool, CInt, and so on) to convert between Boolean and numeric types. However, other languages sometimes perform these conversions differently, as do the .NET Framework methods.
You should never write code that relies on equivalent numeric values for True and False. Whenever possible, you should restrict usage of Boolean variables to the logical values for which they are designed. If it is necessary to mix Boolean and numeric values, be sure that you understand the conversion method you choose.
Conversion in Visual Basic
When you use the CType or CBool conversion keywords to convert numeric data types to Boolean, 0 becomes False and all other values become True. When you convert Boolean values to numeric types using the conversion keywords, False becomes 0 and True becomes -1.
Conversion in the Framework
The ToInt32 method of the Convert class in the System namespace converts True to +1.
If you need to convert a Boolean value to a numeric data type, take care which conversion method you use.
Character Literal Generates Compiler Error
In the absence of any type characters, Visual Basic assumes default data types for literals. The default type for a character literal — enclosed within double quotes (" ") — is String.
The String data type does not widen to the Char Data Type (Visual Basic). This means that if you want to assign a literal to a Char variable, you must either make a narrowing conversion or force the literal to the Char type.
To create a Char literal to assign to a variable or constant
Declare the variable or constant as Char.
Enclose the character value within double quotation marks (" ").
Follow the closing double quotation mark with the literal type character C to force the literal to Char. This is necessary if the type checking switch (Option Strict Statement) is On, and it is desirable in any case.
The following example demonstrates both unsuccessful and successful assignments of a literal to a Char variable.
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")
There is always a risk in using narrowing conversions, because they can fail at run time. For example, a conversion from String to Char can fail if the String value contains more than one character. Therefore, it is better programming to use the C type character.
String Conversion Fails at Run Time
The String Data Type (Visual Basic) participates in very few widening conversions. String widens only to itself and Object, and only Char and Char() (a Char array) widen to String. This is because String variables and constants can contain values that other data types cannot accommodate.
When the type checking switch (Option Strict Statement) is On, the compiler disallows all implicit narrowing conversions, including those involving String. Your code can still use conversion keywords such as CStr and CType Function, which direct the .NET Framework to attempt the conversion.
Narrowing Conversion Protection
The disadvantage of narrowing conversions is that they can fail at run time. For example, if a String variable contains anything other than "True" or "False", it cannot be converted to Boolean. If it contains punctuation characters, conversion to any numeric type fails. Unless you know that your String variable always holds values that the destination type can accept, you should not attempt a conversion.
If you need to convert from String to another data type, the safest procedure is to enclose the attempted conversion in the Try...Catch...Finally Statement (Visual Basic). This allows you to deal with a run-time failure.
Character Arrays
A single Char and an array of Char elements both widen to String. However, String does not widen to Char(). To convert a String value to a Char array, you can use the ToCharArray method of the System.String class.
Meaningless Values
In general, String values are not meaningful in other data types, and conversion is highly artificial and dangerous. Whenever possible, you should restrict usage of String variables to the character sequences for which they are designed. You should never write code that relies on equivalent values in other types.
See Also
Reference
Data Type Summary (Visual Basic)
Type Conversion Functions
Concepts
Data Types in Visual Basic
Type Characters
Typeless Programming in Visual Basic
Data Types Are .NET Framework Types
Efficient Use of Data Types