数据类型疑难解答 (Visual Basic)
此页列出了对固有数据类型执行操作时可能会发生的一些常见问题。
浮点表达式不比较为等于
使用浮点数(单数据类型和双数据类型)时,请记住,它们存储为二进制分数。 这意味着对于任何不是二进制分数的数量,它们无法提供准确的表示形式(采用 k / (2 ^ n) 的形式,其中 k 和 n 是整数)。 例如,0.5 (= 1/2) 和 0.3125 (= 5/16) 可以作为精确的值,而 0.2 (= 1/5) 和 0.3 (= 3/10) 只能是近似值。
由于这种不精确性,在使用浮点值时,不要指望获得准确的结果。 具体而言,理论上相等的两个值的表示形式可能略有不同。
比较浮点数量 |
---|
1. 使用 System 命名空间中 Math 类的 Abs 方法计算其差值的绝对值。 2. 确定可接受的最大差值,这样,如果两个数量的差值不大于该值,则出于实用目的,你可以认为这两个数量相等。 3. 将差值的绝对值与可接受的差值进行比较。 |
下面的示例演示了两个 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 运算符不返回准确的结果
由于浮点存储的不精确性,当至少有一个操作数为浮点时,Mod 运算符可能会返回意外的结果。
十进制数据类型不使用浮点表示形式。 在 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
会将两个操作数都强制为 Decimal
,0.2 将具有精确的表示形式。 因此,Mod
运算符将得到预期余数 0.0。
请注意,将 decimalRemainder
声明为 Decimal
是不够的。 还必须将文本强制为 Decimal
,或者它们在默认情况下使用 Double
并且 decimalRemainder
接收同一不准确值作为 doubleRemainder
。
布尔类型无法准确转换为数字类型
布尔数据类型的值不存储为数字,并且存储的值不等效于数字。 为了与早期版本兼容,Visual Basic 提供了转换关键字(CType 函数、CBool
、CInt
等)在 Boolean
和数值类型之间转换。 但是,其他语言有时会以不同方式执行这些转换,比如说 .NET Framework 方法。
切勿编写依赖于 True
和 False
的等效数字值的代码。 应尽可能将 Boolean
变量的使用限制为设计时适用的逻辑值。 如果必须混合使用 Boolean
和数字值,请确保了解所选择的转换方法。
Visual Basic 中的转换
使用 CType
或 CBool
转换关键字将数字数据类型转换为 Boolean
时,0 变为 False
,所有其他值变为 True
。 使用转换关键字将 Boolean
值转换为数字类型时,False
变为 0,True
变为 -1。
Framework 中的转换
命名空间 System 中 Convert 类的 ToInt32 方法将 True
转换为 +1。
如果必须将 Boolean
值转换为数值数据类型,请谨慎决定使用哪种转换方法。
字符文本生成编译器错误
在没有任何字符类型的情况下,Visual Basic 假定使用文本的默认数据类型。 字符型文本的默认类型(包含在引号内 (" "
))为 String
。
String
数据类型不会扩大为字符型数据类型。 这意味着,如果要将文本赋值给 Char
变量,则必须进行收缩转换或将文本强制为 Char
类型。
创建要赋值给变量或常量的字符型文本 |
---|
1. 将变量或常量声明为 Char 。2. 将字符值包含在引号中 ( " " )。3. 将文本类型字符 C 放在右双引号的后面,以将文本强制为 Char 。 如果类型检查开关 (Option Strict Statement) 为 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
值包含多个字符,则从 String
到 Char
的转换可能会失败。 因此,最好使用 C
类型字符进行编程。
字符串转换在运行时失败
字符串数据类型参与的扩大转换很少。 String
仅向自身和 Object
扩大,并且只有 Char
和 Char()
(Char
数组)扩大为 String
。 这是因为 String
变量和常量可以包含其他数据类型不能包含的值。
当类型检查开关 (Option Strict Statement) 为 On
时,编译器将禁止所有隐式收缩转换。 这包括涉及 String
的转换。 代码仍可使用转换关键字(如 CStr
和 CType 函数)来指示 .NET Framework 尝试转换。
注意
对于从 For Each…Next
集合中的元素到循环控制变量的转换,将抑制收缩转换错误。 有关详细信息和示例,请参阅关于 Each...Next 语句的“收缩转换”一节。
收缩转换保护
收缩转换的缺点是它们可能会在运行时失败。 例如,如果 String
变量包含除“True”或“False”之外的任何内容,则不能将其转换为 Boolean
。 如果它包含标点字符,则无法转换为任何数字类型。 除非知道 String
变量始终包含目标类型可以接受的值,否则不应尝试转换。
如果必须从 String
转换为另一种数据类型,则最安全的过程是将尝试的转换包含在 Try...Catch...Finally 语句中。 这样,你可以处理运行时故障。
字符数组
单个 Char
元素和 Char
元素数组均扩大为 String
。 但是,String
不会扩大为 Char()
。 若要将 String
值转换为 Char
数组,可以使用 System.String 类的 ToCharArray 方法。
无意义的值
一般情况下,String
值在其他数据类型中没有意义,转换过于人为且非常危险。 应尽可能将 String
变量的使用限制在设计时适用的字符序列。 切勿编写依赖于其他类型中的等效值的代码。