Me encontré con esta pregunta SO que preguntaba sobre una forma para convertir valores hexadecimales más grandes en un valor numérico positivo:
?Val("&H8000") -32768 Val("&HFFFF") -1
Mi respuesta implicó iterar los dígitos de la cadena uno por uno y calcular su valor respectivo en el resultado:
Function ConvertHex(ByVal value As String) As Double If Left(value, 2) = "&H" Then value = Right(value, Len(value) - 2) End If Dim result As Double Dim i As Integer, j As Integer For i = Len(value) To 1 Step -1 Dim digit As String digit = Mid$(value, i, 1) result = result + (16 ^ j) * Val("&H" & digit) j = j + 1 Next ConvertHex = result End Function
Funciona, pero puedo » Ayude a pensar que he hecho algo estúpidamente demasiado complicado por algo que debería ser bastante simple.
Hay una mejor manera, ¿no es así?
Comentarios
Responder
Por lo que puedo decir, una cadena que comienza con &H
es un literal hexadecimal.
Existe un número de funciones de conversión que pueden convertir una expresión al tipo deseado.
Por lo tanto, debería ser simplemente, según el tipo deseado:
Function ConvertHex(ByVal value As String) As Currency Dim result As Currency result = CCur(value) If result < 0 Then "Add two times Int32.MaxValue and another 2 for the overflow "Because the hex value is apparently parsed as a signed Int64/Int32 result = result + &H7FFFFFFF + &H7FFFFFFF + 2 End If ConvertHex = result End Function
Moneda vs Doble
Valor entero positivo máximo representable con precisión:
-
&H0020000000000000
(9,007,199,254,740,992) para Doble ( IEEE 754 binary64 ) -
&H000346DC5D638865
(922,337,203,685,477) para moneda
Entonces, ¿por qué usar Currency
sobre Double
cuando este último funciona para un rango mayor de números enteros?
Currency
es siempre precisa. Si desbordamos un valor Currency
obtenemos un error. Si sobrepasamos el valor entero máximo representable de un doble, obtenemos un valor entero aproximado:
Dim doubleMax As Double Dim doubleAfter As Double doubleMax = CDbl("&H0020000000000000") doubleAfter = doubleMax + 1 MsgBox "Double before: " & Format(doubleMax, "#") & vbNewLine & "after: " & Format(doubleAfter, "#") Dim currencyMax As Currency Dim currencyAfter As Currency currencyMax = CCur("&H000346DC5D638865") currencyAfter = currencyMax + 1 MsgBox "Currency before: " & Format(currencyMax, "#") & vbNewLine & "after: " & Format(currencyAfter, "#")
La salida de este ejemplo es:
Double before: 9007199254740990 after: 9007199254740990
Y luego un run-time error "6": Overflow
que es genial si quiere evitar errores de redondeo. Ahora MSDN afirma que Double
está
almacenado como número de punto flotante IEEE de 64 bits (8 bytes)
pero si «ha leído algo sobre el IEEE 754 binary64 debería estar un poco sorprendido por el resultado del ejemplo. El máximo real es &H00038D7EA4C68000
(1,000,000,000,000,000).
Comentarios
- ¡Genial! ' he vinculado esta respuesta en una edición de mi respuesta SO 🙂
- Amplié el bit sobre
Currency
vsDouble
un poco y descubrió algo interesante. - ¿Qué tal si
doubleMax + doubleMax
lanza un ¿Error de desbordamiento? - @Meehow Acabo de probarlo y aparentemente
Double
en VBA se desborda. Pero ' todavía no es posible desbordar si el resultado analizado es un número entero. Yo ' me alegro de no haberlo ' No tengo que trabajar con ese idioma. - La forma correcta e idiomática de hacer esto es usar una de las funciones de conversión integradas, como @Meehow comentó en el OP – No ' no sé qué pedo cerebral me hizo escribir esta función …
Short
,Integer
,Long
,UShort
,UInteger
,ULong
, oDecimal
.Cdbl("&HFFFF")
?CDbl
es incorrecto para este escenario.