Mi sono imbattuto in questa domanda SO che chiedeva un modo per convertire valori esadecimali più grandi in un valore numerico positivo:
?Val("&H8000") -32768 Val("&HFFFF") -1
La mia risposta prevedeva literazione delle cifre della stringa una per una e il calcolo del rispettivo valore nel risultato:
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
Funziona, ma posso ” Non aiuta a pensare di aver fatto qualcosa di stupidamente troppo complicato per qualcosa che dovrebbe essere piuttosto semplice.
Esiste un modo migliore, no?
Commenti
Risposta
Da quello che posso dire una stringa che inizia con &H
è un letterale esadecimale.
Esiste un numero di funzioni di conversione che possono convertire unespressione al tipo desiderato.
Quindi dovrebbe essere semplicemente, a seconda del tipo desiderato:
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 vs doppia
Valore intero positivo massimo rappresentabile con precisione:
-
&H0020000000000000
(9,007,199,254,740,992) per Doppio ( IEEE 754 binary64 ) -
&H000346DC5D638865
(922.337.203.685.477) per Valuta
Allora perché utilizzare Currency
su Double
quando questultimo funziona per un intervallo più ampio di numeri interi?
Currency
è sempre accurato. Se eccediamo un valore Currency
otteniamo un errore. Se superiamo il valore intero massimo rappresentabile di un double, otteniamo un valore intero approssimativo:
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, "#")
Loutput di questo esempio è:
Double before: 9007199254740990 after: 9007199254740990
E poi un run-time error "6": Overflow
che è fantastico se tu desidera evitare errori di arrotondamento. Ora MSDN afferma che Double
è
memorizzato come numero a virgola mobile IEEE a 64 bit (8 byte)
ma se “hai letto qualcosa sul IEEE 754 binary64 dovresti essere un po sorpreso dalloutput dellesempio. Il massimo effettivo è &H00038D7EA4C68000
(1,000,000,000,000,000).
Commenti
- Fantastico! Ho ' collegato a questa risposta in una modifica alla mia risposta SO 🙂
- Ho ampliato la parte su
Currency
vsDouble
un po e ho scoperto qualcosa di interessante. - che ne dici di
doubleMax + doubleMax
lanciare un errore di overflow? - @Meehow Lho appena testato e apparentemente
Double
in VBA va in overflow. Ma ' è ancora non è possibile eseguire loverflow se il risultato analizzato è un numero intero. ' sono contento di averlo fatto ' t deve lavorare con quella lingua. - Il modo corretto e idiomatico per farlo è usare una delle funzioni di conversione integrate, come ha commentato @Meehow sullOP – Non ' non so cosa brainfart mi abbia fatto scrivere questa funzione …
Short
,Integer
,Long
,UShort
,UInteger
,ULong
oDecimal
.Cdbl("&HFFFF")
?CDbl
sia sbagliato per questo scenario.