Jag snubblade på den här SO-frågan som frågade om ett sätt för att konvertera större hex-värden till ett positivt numeriskt värde:
?Val("&H8000") -32768 Val("&HFFFF") -1
Mitt svar handlade om att itera strängsiffrorna en efter en och beräkna deras respektive värde till resultatet:
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
Det fungerar, men jag kan ” t hjälp att tänka att jag ”har gjort något dumt för komplicerat för något som borde vara ganska enkelt.
Det finns ett bättre sätt, är det inte där?
Kommentarer
Svar
Från vad jag kan säga en sträng som börjar med &H
är en bokstavsordning.
Det finns ett antal konverteringsfunktioner som kan konvertera ett uttryck till önskad typ.
Så det borde helt enkelt vara, beroende på önskad typ:
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
Valuta kontra dubbel
Maximalt exakt representerbart positivt heltal:
-
&H0020000000000000
(9,007,199,254,740,992) för Dubbel ( IEEE 754 binary64 ) -
&H000346DC5D638865
(922,337,203,685,477) för Valuta
Så varför använda Currency
över Double
när den senare fungerar för ett större antal heltal?
Currency
är alltid korrekt. Om vi överskrider ett Currency
-värde får vi ett fel. Om vi överskrider det maximalt representerbara heltalsvärdet för en dubbel får vi ett ungefärligt heltalvärde:
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, "#")
Resultatet av detta exempel är:
Double before: 9007199254740990 after: 9007199254740990
Och sedan en run-time error "6": Overflow
vilket är bra om du vill undvika avrundningsfel. Nu hävdar MSDN att Double
är
lagrat som IEEE 64-bitars (8-byte) flytpunktsnummer
men om du har läst något om IEEE 754 binary64 borde du vara lite förvånad över resultatet från exemplet. Det faktiska maximumet är &H00038D7EA4C68000
(1.000.000.000.000.000).
Kommentarer
- Fantastiskt! Jag ' har länkat till det här svaret på en redigering av mitt SO-svar 🙂
- Jag utökade lite om
Currency
vsDouble
lite och upptäckte något intressant. - vad sägs om
doubleMax + doubleMax
att kasta en överflödesfel? - @Meehow Testade bara det och tydligen överflödar
Double
i VBA. Men det ' är fortfarande det går inte att överskrida om det analyserade resultatet är ett heltal. Jag ' är glad att jag inte ' t måste arbeta med det språket. - Det korrekta, idiomatiska sättet att göra detta är att använda en av de inbyggda konverteringsfunktionerna, som @Meehow kommenterade OP – Jag vet inte ' vad brainfart fick mig att ens skriva den här funktionen …
Short
,Integer
,Long
,UShort
,UInteger
,ULong
ellerDecimal
.Cdbl("&HFFFF")
?CDbl
är fel i det här scenariot.