Solucionando problemas de tipos de dados (Visual Basic)

Esta página lista alguns problemas comuns que podem ocorrer quando você executa operações em tipos de dados intrínsecos.

Expressões de Ponto Flutuante não são Comparadas como Iguais

Quando você trabalha com números de ponto flutuante (Tipo de Dados Único e Tipo de Dados Duplo), lembre-se de que eles são armazenados como frações binárias. Isso significa que eles não podem conter uma representação exata de qualquer quantidade que não seja uma fração binária (da forma k / (2 ^ n) em que k e n são inteiros). Por exemplo, 0,5 (= 1/2) e 0,3125 (= 5/16) podem ser mantidos como valores precisos, enquanto 0,2 (= 1/5) e 0,3 (= 3/10) podem ser apenas aproximações.

Devido a essa imprecisão, você não pode confiar em resultados exatos quando opera em valores de ponto flutuante. Em particular, dois valores teoricamente iguais podem ter representações ligeiramente diferentes.

Para comparar quantidades de ponto flutuante
1. Calcule o valor absoluto da diferença deles usando o método Abs da classe Math no namespace System.
2. Determine uma diferença máxima aceitável, de modo que você possa considerar as duas quantidades iguais para fins práticos se a diferença não for maior.
3. Compare o valor absoluto da diferença com a diferença aceitável.

O exemplo a seguir demonstra a comparação incorreta e correta de dois 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))

O exemplo anterior usa o método ToString da estrutura Double para que possa especificar uma precisão melhor do que a palavra-chave CStr usa. O padrão é 15 dígitos, mas o formato "G17" estende-o para 17 dígitos.

O Operador Mod não Retorna Resultado Preciso

Devido à imprecisão do armazenamento de ponto flutuante, o Operador Mod pode retornar um resultado inesperado quando pelo menos um dos operandos é um ponto flutuante.

O Tipo de Dados Decimais não usa representação de ponto flutuante. Muitos números que são inexatos em Single e Double são exatos em Decimal (por exemplo, 0,2 e 0,3). Embora a aritmética seja mais lenta no Decimal do que no ponto flutuante, pode valer a pena diminuir o desempenho para obter melhor precisão.

Para localizar o restante inteiro de quantidades de ponto flutuante
1. Declare as variáveis como Decimal.
2. Use o caractere de tipo literal D para forçar literais para Decimal, caso os valores deles sejam muito grandes para o tipo de dados Long.

O exemplo a seguir demonstra a possível imprecisão dos operandos de ponto flutuante.

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

O exemplo anterior usa o método ToString da estrutura Double para que possa especificar uma precisão melhor do que a palavra-chave CStr usa. O padrão é 15 dígitos, mas o formato "G17" estende-o para 17 dígitos.

Porque zeroPointTwo é Double, o valor dele para 0,2 é uma fração binária infinitamente repetitiva com um valor armazenado de 0,2000000000000001. Dividir 2,0 por essa quantidade produz 9,9999999999999995 com um restante de 0,19999999999999999999999991.

Na expressão para decimalRemainder, o caractere de tipo literal D força ambos os operandos a Decimal e 0,2 tem uma representação precisa. Portanto, o operador Mod produz o restante esperado de 0,0.

Observe que não é suficiente declarar decimalRemainder como Decimal. Você também precisa forçar os literais para Decimal ou eles usam Double por padrão e decimalRemainder recebe o mesmo valor impreciso que doubleRemainder.

O Tipo Booliano não Converte em Tipo Numérico com Precisão

Os valores de Tipo de Dados Boolianos não são armazenados como números e os valores armazenados não devem ser equivalentes a números. Para compatibilidade com versões anteriores, o Visual Basic fornece palavras-chave de conversão (Função CType , CBool, CInt e assim por diante) para converter entre Boolean e tipos numéricos. No entanto, outras linguagens às vezes executam essas conversões de maneira diferente, assim como os métodos .NET Framework.

Você nunca deve gravar um código que dependa de valores numéricos equivalentes para True e False. Sempre que possível, você deve restringir o uso de variáveis Boolean aos valores lógicos para os quais elas são projetadas. Se você precisar misturar Boolean e valores numéricos, verifique se entendeu o método de conversão selecionado.

Conversão no Visual Basic

Quando você usa as palavras-chave de conversão CType ou CBool para converter tipos de dados numéricos para Boolean, 0 se torna False e todos os outros valores se tornam True. Quando você converte valores Boolean em tipos numéricos usando as palavras-chave de conversão, False torna-se 0 e True torna-se -1.

Conversão no Framework

O método ToInt32 da classe Convert no namespace System converte True em +1.

Se você precisar converter um valor Boolean em um tipo de dados numérico, tenha cuidado com qual método de conversão você usa.

Literal de Caractere Gera Erro do Compilador

Na ausência de caracteres de tipo, o Visual Basic pressupõe tipos de dados padrão para literais. O tipo padrão para um literal de caractere – entre aspas (" ") – é String.

O tipo de dados String não é ampliado para o Tipo de Dados de Caractere. Isso significa que, se você quiser atribuir um literal a uma variável Char, deverá fazer uma conversão de restrição ou forçar o literal para o tipo Char.

Para criar um literal Char para atribuir a uma variável ou constante
1. Declare a variável ou a constante como Char.
2. Coloque o valor do caractere entre aspas (" ").
3. Siga as aspas duplas de fechamento com o caractere de tipo literal C para forçar o literal a Char. Isso será necessário se a opção de verificação de tipo (Instrução Option Strict) for On e for desejável em qualquer caso.

O exemplo a seguir demonstra atribuições malsucedidas e bem-sucedidas de um literal para uma variável 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")

Há sempre um risco em usar conversões de restrição, pois elas podem falhar em tempo de execução. Por exemplo, uma conversão de String para Char pode falhar se o valor String contiver mais de um caractere. Portanto, é melhor a programação usar o caractere de tipo C.

Falha na Conversão de Cadeia de Caracteres em Tempo de Execução

O Tipo de Dados String participa de pouquíssimas conversões de expansão. String amplia somente para si mesmo e Object e somente Char e Char() (uma matriz Char) ampliam para String. Isso ocorre porque variáveis e constantes String podem conter valores que outros tipos de dados não podem conter.

Quando a opção de verificação de tipo (Instrução Option Strict) é On, o compilador não permite todas as conversões de restrição implícitas. Isso inclui aquelas que envolvem String. Seu código ainda pode usar palavras-chave de conversão, como CStr e Função CType, que direcionam o .NET Framework para tentar a conversão.

Observação

O erro de conversão de restrição é suprimido para conversões dos elementos em uma coleção For Each…Next para a variável de controle de loop. Para obter mais informações e exemplos, consulte a seção "Conversões de restrição" em Para cada... Próxima instrução.

Proteção de Conversão de Restrição

A desvantagem das conversões de restrição é que elas podem falhar em tempo de execução. Por exemplo, se uma variável String contiver algo diferente de "True" ou "False", ela não poderá ser convertida em Boolean. Se ela contiver caracteres de pontuação, a conversão para qualquer tipo numérico falhará. A menos que você saiba que sua variável String sempre contém valores que o tipo de destino pode aceitar, você não deve tentar uma conversão.

Se você precisar converter de String para outro tipo de dados, o procedimento mais seguro é incluir a tentativa de conversão na Instrução Try...Catch...Finally. Isso permite que você lide com uma falha em tempo de execução.

Matrizes de Caracteres

Um único Char e uma matriz de elementos Char são ampliados para String. No entanto, String não é ampliado para Char(). Para converter um valor String em uma matriz Char, você pode usar o método ToCharArray da classe System.String.

Valores sem Sentido

Em geral, os valores String não são significativos em outros tipos de dados e a conversão é altamente artificial e perigosa. Sempre que possível, você deve restringir o uso de variáveis String para as sequências de caracteres para as quais elas são projetadas. Você nunca deve gravar um código que dependa de valores equivalentes em outros tipos.

Confira também