Narazil jsem na tuto SO otázku , která se ptala na způsob převést větší hexadecimální hodnoty na kladnou číselnou hodnotu:
?Val("&H8000") -32768 Val("&HFFFF") -1
Moje odpověď zahrnovala iteraci číslic řetězce po jedné a výpočet jejich příslušné hodnoty do výsledku:
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
Funguje to, ale můžu “ Nemohu si myslet, že jsem udělal něco hloupě přehnaně komplikovaného pro něco, co by mělo být docela jednoduché.
Existuje lepší způsob, není tam?
Komentáře
Odpověď
Z toho, co mohu říct řetězci začínajícímu &H
je hexadecimální literál.
Existuje počet konverzních funkcí , které mohou převést výraz na požadovaný typ.
Takže by to mělo být jednoduše, v závislosti na požadovaném typu:
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
Měna vs. dvojnásobek
Maximum přesně reprezentovatelné kladné celé číslo:
-
&H0020000000000000
(9 007 199 254 740 992) pro Double ( IEEE 754 binary64 ) -
&H000346DC5D638865
(922 337 203 685 477) pro měna
Proč tedy používat Currency
nad Double
, když tato funkce funguje pro větší rozsah celých čísel?
Currency
je vždy přesný. Pokud přetečeme Currency
hodnotu, dostaneme chybu. Pokud přetečeme maximální reprezentovatelnou celočíselnou hodnotu dvojitého, dostaneme přibližnou celočíselnou hodnotu:
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, "#")
Výstup tohoto příkladu je:
Double before: 9007199254740990 after: 9007199254740990
A pak run-time error "6": Overflow
což je skvělé, pokud se chcete vyhnout chybám zaokrouhlování. Nyní je tvrzení MSDN Double
uloženo jako 64bitové (8bajtové) číslo IEEE s plovoucí desetinnou čárkou
ale pokud jste si přečetli něco o IEEE 754 binary64 , měli byste být trochu překvapen výstupem z příkladu. Skutečné maximum je &H00038D7EA4C68000
(1 000 000 000 000 000).
Komentáře
- Úžasné! S touto odpovědí jsem ' propojil úpravu své SO odpovědi 🙂
- Rozšířil jsem část o
Currency
vsDouble
trochu a objevil něco zajímavého. - co tak
doubleMax + doubleMax
hodit chyba přetečení? - @Meehow Právě jste to otestovali a zjevně
Double
ve VBA přeteče. Ale ' stále není možné přetečení, pokud je analyzovaným výsledkem celé číslo. Jsem ' rád, že ned ' s tímto jazykem pracovat nemusíme. - Správným idiomatickým způsobem je použití jedné z integrovaných funkcí převodu, jak @Meehow komentoval OP – Nevím ' nevím, co mi brainfart způsobil, že jsem tuto funkci dokonce napsal …
Short
,Integer
,Long
,UShort
,UInteger
,ULong
, neboDecimal
.Cdbl("&HFFFF")
?CDbl
je pro tento scénář špatný.