数据类型疑难解答 (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 运算符可能会返回意外的结果。

十进制数据类型不使用浮点表示形式。 在 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。

请注意,将 decimalRemainder 声明为 Decimal 是不够的。 还必须将文本强制为 Decimal,或者它们在默认情况下使用 Double 并且 decimalRemainder 接收同一不准确值作为 doubleRemainder

布尔类型无法准确转换为数字类型

布尔数据类型的值不存储为数字,并且存储的值不等效于数字。 为了与早期版本兼容,Visual Basic 提供了转换关键字(CType 函数CBoolCInt 等)在 Boolean 和数值类型之间转换。 但是,其他语言有时会以不同方式执行这些转换,比如说 .NET Framework 方法。

切勿编写依赖于 TrueFalse 的等效数字值的代码。 应尽可能将 Boolean 变量的使用限制为设计时适用的逻辑值。 如果必须混合使用 Boolean 和数字值,请确保了解所选择的转换方法。

Visual Basic 中的转换

使用 CTypeCBool 转换关键字将数字数据类型转换为 Boolean 时,0 变为 False,所有其他值变为 True。 使用转换关键字将 Boolean 值转换为数字类型时,False 变为 0,True 变为 -1。

Framework 中的转换

命名空间 SystemConvert 类的 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 值包含多个字符,则从 StringChar 的转换可能会失败。 因此,最好使用 C 类型字符进行编程。

字符串转换在运行时失败

字符串数据类型参与的扩大转换很少。 String 仅向自身和 Object 扩大,并且只有 CharChar()Char 数组)扩大为 String。 这是因为 String 变量和常量可以包含其他数据类型不能包含的值。

当类型检查开关 (Option Strict Statement) 为 On 时,编译器将禁止所有隐式收缩转换。 这包括涉及 String 的转换。 代码仍可使用转换关键字(如 CStrCType 函数)来指示 .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 变量的使用限制在设计时适用的字符序列。 切勿编写依赖于其他类型中的等效值的代码。

另请参阅