-
数据类型疑难解答 (Visual Basic)
本页列出一些在对内部数据类型执行操作时可能出现的常见问题。
比较浮点表达式时不能获得等值结果
在使用浮点数(Single 数据类型 (Visual Basic) 和 Double 数据类型 (Visual Basic))时,请记住,它们以二进制分数的形式存储。 这就意味着,对于任何非二进制分数(二进制分数形式为 k / (2 ^ n),其中 k 和 n 均为整数)的数值,浮点数无法保存它们的精确表示形式。 例如,0.5 (= 1/2) 和 0.3125 (= 5/16) 可保存为精确值,而 0.2 (= 1/5) 和 0.3 (= 3/10) 只能是近似值。
由于这一不精确性,因此,在处理浮点值时,不能指望会获得精确的结果。 具体而言,理论上相等的两个值可能会具有略微不同的表示形式。
比较浮点数量 |
|
下面的示例演示两个 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 运算符 (Visual Basic) 可能会返回意外的结果。
Decimal 数据类型 (Visual Basic) 不使用浮点表示形式。 在 Single 和 Double 中不精确的许多数字在 Decimal 中是精确的数字(例如 0.2 和 0.3)。 虽然浮点表示形式在算术运算速度上比 Decimal 表示形式快,但可能值得用性能的下降换回精度的提高。
找出浮点数量的整数余数 |
|
下面的示例演示了浮点操作数潜在的不精确性。
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 一样均采用不精确的值。
Boolean 类型不能准确地转换为数值类型
Boolean 数据类型 (Visual Basic) 值不作为数字进行存储,存储的值也不用来与数字等效。 为了与早期版本兼容,Visual Basic 提供了用于在 Boolean 和数值类型之间进行转换的转换关键字(CType 函数 (Visual Basic)、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 文本 |
|
下面的示例演示了将文本赋予 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 值包含多个字符,则从 String 到 Char 的转换可能会失败。 因此,在编程时最好使用 C 类型字符。
字符串转换在运行时失败
String 数据类型 (Visual Basic) 只参与非常少的扩大转换。 String 只扩大到自身和 Object,并且只有 Char 和 Char()(Char 数组)会扩大到 String。 这是因为 String 变量和常量可以包含其他数据类型无法包含的值。
在类型检查开关(Option Strict 语句)为 On 时,编译器不允许任何隐式收缩转换。 这包括涉及 String 的转换。 代码仍可以使用转换关键字(如 CStr 和 CType 函数 (Visual Basic)),以指示 .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 变量作为字符序列使用,这也是设计这些变量的目的所在。 绝不应编写依赖其他类型中的等效值的代码。