Solucionar problemas de tipos de datos (Visual Basic)

En esta página se indican algunos problemas habituales que pueden producirse al realizar operaciones en tipos de datos intrínsecos.

No es posible comprobar la igualdad de las expresiones de punto flotante

Al trabajar con números de punto flotante (Single (Tipo de datos) y Double (Tipo de datos)), recuerde que se almacenan como fracciones binarias. Esto significa que no pueden contener una representación exacta de ninguna cantidad que no sea una fracción binaria (del formato k / (2 ^ n), donde k y n son enteros). Por ejemplo, 0,5 (= 1/2) y 0,3125 (= 5/16) se pueden mantener como valores precisos, mientras que 0,2 (= 1/5) y 0,3 (= 3/10) solo pueden ser aproximaciones.

Debido a esta imprecisión, no se pueden esperar resultados exactos al trabajar con valores de punto flotante. En concreto, dos valores teóricamente iguales podrían tener representaciones ligeramente diferentes.

Para comparar cantidades de punto flotante
1. Calcule el valor absoluto de su diferencia mediante el método Abs de la clase Math del espacio de nombres System.
2. Determine una diferencia máxima aceptable, de modo que pueda considerar que las dos cantidades son iguales para fines prácticos si su diferencia no es mayor.
3. Compare el valor absoluto de la diferencia con la diferencia aceptable.

En el ejemplo siguiente se muestra una comparación incorrecta y correcta de dos valores 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))

En el ejemplo anterior se usa el método ToString de la estructura Double para que pueda especificar una mejor precisión que la que usa la palabra clave CStr. El valor predeterminado es 15 dígitos, pero el formato "G17" lo extiende a 17 dígitos.

El operador Mod no devuelve un resultado preciso

Debido a la imprecisión del almacenamiento de punto flotante, el Operador Mod puede devolver un resultado inesperado si al menos uno de los operandos es de punto flotante.

Decimal (Tipo de datos) no usa la representación de punto flotante. Muchos números que son inexactos en Single y Double son exactos en Decimal (por ejemplo, 0,2 y 0,3). Aunque la aritmética es más lenta en Decimal que en punto flotante, la disminución de rendimiento puede valer la pena para lograr una mejor precisión.

Para buscar el resto entero de cantidades de punto flotante
1. Declare las variables como Decimal.
2. Use el carácter de tipo literal D para forzar a los literales a ser Decimal si sus valores son demasiado grandes para el tipo de datos Long.

En el ejemplo siguiente se muestra la posible imprecisión de los operandos de punto flotante.

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))

En el ejemplo anterior se usa el método ToString de la estructura Double para que pueda especificar una mejor precisión que la que usa la palabra clave CStr. El valor predeterminado es 15 dígitos, pero el formato "G17" lo extiende a 17 dígitos.

Dado que zeroPointTwo es Double, su valor para 0,2 es una fracción binaria repetida infinitamente con un valor almacenado de 0,2000000000000001. Al dividir 2,0 por esta cantidad,el resultado es 9,9999999999999999995, con un resto de 0,1999999999999999999999991.

En la expresión para decimalRemainder, el carácter de tipo literal D fuerza a ambos operandos a ser Decimal, y 0,2 tiene una representación precisa. Por lo tanto, el operador Mod produce el resto esperado de 0,0.

Observe que no basta con declarar decimalRemainder como Decimal. También debe forzar a los literales a ser Decimal, o usan Double de manera predeterminada y decimalRemainder recibe el mismo valor impreciso que doubleRemainder.

El tipo booleano no se convierte en tipo numérico con precisión

Los valores Boolean (Tipo de datos) no se almacenan como números, y los valores almacenados no están diseñados para ser equivalentes a los números. Para lograr la compatibilidad con versiones anteriores, Visual Basic proporciona palabras clave de conversión (CType (Función), CBool, CInt, etc.) para convertir entre Boolean y tipos numéricos. Pero otros lenguajes a veces realizan estas conversiones de forma diferente, al igual que los métodos de .NET Framework.

Nunca debe escribir código que se base en valores numéricos equivalentes para True y False. Siempre que sea posible, se debe restringir el uso de variables Boolean a los valores lógicos para los que están diseñadas. Si debe combinar Boolean y valores numéricos, asegúrese de comprender el método de conversión que seleccione.

Conversión en Visual Basic

Si usa las palabras clave de conversión CType o CBool para convertir tipos de datos numéricos en Boolean, 0 se convierte en False y todos los demás valores se convierten en True. Al convertir valores Boolean en tipos numéricos mediante las palabras clave de conversión, False se convierte en 0 y True se convierte en -1.

Conversión en .NET Framework

El método ToInt32 de la clase Convert del espacio de nombres System convierte True en +1.

Si debe convertir un valor Boolean en un tipo de datos numérico, tenga cuidado con el método de conversión que use.

El literal de carácter genera un error del compilador

En ausencia de cualquier tipo de caracteres, Visual Basic asume tipos de datos predeterminados para los literales. El tipo predeterminado para un literal de carácter, entre comillas (" "), es String.

El tipo de datos String no se amplía a Char (Tipo de datos). Esto significa que si quiere asignar un literal a una variable Char, debe realizar una conversión de restricción o forzar al literal a ser de tipo Char.

Para crear un literal de carácter a fin de asignarlo a una variable o constante
1. Declare la variable o constante como Char.
2. Incluya el valor de carácter entre comillas (" ").
3. Siga la comilla doble de cierre con el carácter de tipo literal C para forzar al literal a ser Char. Esto es necesario si el modificador de comprobación de tipos (Option Strict Statement) es On, y es deseable en cualquier caso.

En el ejemplo siguiente se muestran asignaciones incorrectas y correctas de un literal a una variable 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")

Siempre hay riesgo al usar conversiones de restricción, ya que pueden producir errores en tiempo de ejecución. Por ejemplo, una conversión de String a Char puede producir un error si el valor String contiene más de un carácter. Por lo tanto, la mejor programación es usar el carácter de tipo C.

Se produce un error en la conversión de cadenas en tiempo de ejecución

String (Tipo de datos) participa en muy pocas conversiones de ampliación. String solo se amplía a sí mismo y Object, y solo Char y Char() (una matriz Char) se amplían a String. Esto se debe a que las variables y constantes String pueden contener valores que otros tipos de datos no.

Cuando el modificador de comprobación de tipos (Option Strict Statement) es On, el compilador impide todas las conversiones de restricción implícitas. Esto incluye a las que implican a String. El código puede seguir usando palabras clave de conversión como CStr y CType (Función), que indican a .NET Framework que intente la conversión.

Nota

El error de conversión de restricción se suprime en las conversiones de los elementos de una colección For Each…Next a la variable de control de bucle. Para obtener más información y ejemplos, vea la sección "Conversiones de restricción" de Instrucción For Each...Next.

Protección de conversiones de restricción

La desventaja de las conversiones de restricción es que pueden producir errores en tiempo de ejecución. Por ejemplo, si una variable String contiene algo distinto a "True" o "False", no se puede convertir en Boolean. Si contiene caracteres de puntuación, se produce un error en la conversión a cualquier tipo numérico. A menos que sepa que la variable String siempre contiene valores que el tipo de destino puede aceptar, no debe intentar una conversión.

Si debe convertir de String a otro tipo de datos, el procedimiento más seguro es incluir la conversión intentada en la Instrucción Try...Catch...Finally. Esto le permite abordar un error en tiempo de ejecución.

Matrices de caracteres

Un solo valor Char y una matriz de elementos Char se amplían a String. Pero String no se amplía a Char(). Para convertir un valor String en una matriz Char, puede usar el método ToCharArray de la clase System.String.

Valores sin significado

En general, los valores String no son significativos en otros tipos de datos, y la conversión es altamente artificial y peligrosa. Siempre que sea posible, se debe restringir el uso de variables String a las secuencias técnicas para las que están diseñadas. Nunca debe escribir código que se base en valores equivalentes de otros tipos.

Consulte también