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.