Integer Vs Long Confusion

Ho visto molti credono nel seguente

VBA converte tutti i valori interi in type Long

Infatti, anche l’ articolo MSDN dice

“Nelle versioni recenti, tuttavia, VBA converte tutti i valori interi in tipo Long, anche se sono dichiarati come tipo Integer.”

inserisci la descrizione dell'immagine qui

    Com’è ansible? Considera questo semplice esempio.

    Sub Sample() Dim I As Integer I = 123456789 End Sub 

    Se VBA converte tutti i valori Integer in tipo Long anche se sono dichiarati come tipo Integer , il precedente non dovrebbe mai darti l’errore di Overflow !

    Cosa mi manca qui? O dovrei prendere che la dichiarazione non è corretta e prestare attenzione a ciò che il link dice all’inizio

    “Questo contenuto non viene più mantenuto triggersmente. Viene fornito così com’è, per chiunque possa ancora utilizzare queste tecnologie, senza garanzie o richieste di accuratezza in relazione alla versione più recente del prodotto o alla versione del servizio.”

    NOTA: Aggiungo i tag Excel-VBA/Powerpoint-VBA/Word-VBA/Access-VBA sperando che un esperto VBA possa dare un’occhiata a questo nel caso in cui l’esperto non controlli solo il tag VBA . Rimuoverò questi tag non necessari una volta che una soluzione è stata cercata.

    Un intero dichiarato come numero Integer è ancora controllato come numero Integer . La documentazione di msdn fa riferimento a come la variabile viene archiviata internamente. Su un sistema a 32 bit, un intero verrà memorizzato in 32 bit non byte , mentre su un sistema a 16 bit il valore viene memorizzato in uno spazio o registro BIT 16, sarebbe stato memorizzato in 16. Quindi la dimensione massima.

    Non vi è alcuna conversione di tipo in corso per quanto riguarda VBA. Un int è un int e un long è lungo, anche se ora occupano altrettanto spazio .

    Ho trascorso molto tempo a lavorare nell’ambiente VBA e ho tutte le ragioni per ritenere che l’affermazione contenuta in questo articolo sia nella migliore delle ipotesi, fuorviante.

    Non mi sono mai imbattuto in una situazione in cui viene effettuata una conversione automatica non prevista. Naturalmente, l’assegnazione per valore a un tipo più grande (come Double o Long ) sarebbe implicita.

    Un caso specifico in cui la conversione automatica sarebbe un cambio di rottura sarebbe un’assegnazione a un tipo Variant . Senza una conversione, il tipo sarebbe VT_I2, con conversione VT_I4.

    Passando un numero intero di tipo ByRef a una funzione in attesa di un messaggio Long emette una mancata corrispondenza di tipo in Office 2013.

    Sospetto che si stiano riferendo alla memoria interna di Integer : è molto probabile che non siano allineati su parole a 16 bit in memoria (vedere un membro di una struttura short in C / C ++). Probabilmente ne stanno parlando.

    La conversione è solo per l’ottimizzazione della memoria, non per il codice utente. Per il programmatore, non c’è praticamente alcun cambiamento dato che i limiti min / max dei tipi di dati rimangono gli stessi.

    Se prendi quel para nel suo complesso, ti renderai conto che questa affermazione è nel contesto della sola esibizione, e non altrimenti. Questo perché la dimensione predefinita dei numeri è Int32 o Int64 (a seconda che si tratti di un sistema a 32 o 64 bit). Il processore può elaborare fino a quel grande numero in un colpo solo. Se dichiari un’unità più piccola di questa, il compilatore deve ridimensionarla, e ciò richiede più sforzi che semplicemente usando il tipo predefinito. E il processore non ha nemmeno alcun guadagno. Quindi, anche se dichiari la tua variabile come Integer , il compilatore la assegna a Long memory, perché sa che deve fare più lavoro senza alcun guadagno.

    Come programmatore VBA, ciò che è importante per te è: Declare your variables as LONG instead of INTEGER even if you want to store small numbers in them.

    “Nelle versioni recenti, tuttavia, VBA converte tutti i valori interi in tipo Long, anche se sono dichiarati come tipo Integer.”

    Non credo che la documentazione. Considera il seguente semplice esempio (esegui in Excel 2010):

     Sub checkIntegerVsLong() 'Check the total memory allocation for an array Dim bits As Integer 'or long? :) Dim arrInteger() As Integer ReDim arrInteger(1 To 5) arrInteger(1) = 12 arrInteger(2) = 456 'get total memory allocation for integer in array bits = VarPtr(arrInteger(2)) - VarPtr(arrInteger(1)) Debug.Print "For integer: " & bits & " bits and " & bits * 8 & " bytes." Dim arrLong() As Long ReDim arrLong(1 To 5) arrLong(1) = 12 arrLong(2) = 456 'get memory allocation for long bits = VarPtr(arrLong(2)) - VarPtr(arrLong(1)) Debug.Print "For long: " & bits & " bits and " & bits * 8 & " bytes." End Sub 

    Questo stampa:

    Per numero intero: 2 bit e 16 byte.

    Per lungo tempo: 4 bit e 32 byte.

    Puoi anche testarlo su singole variabili usando il seguente:

     Sub testIndividualValues() Dim j As Long Dim i As Integer Dim bits As Integer bits = LenB(i) Debug.Print "Length of integer: " & bits & " bits and " & bits * 8 & " bytes." bits = LenB(j) Debug.Print "Length of long: " & bits & " bits and " & bits * 8 & " bytes." End Sub 

    che stampa

    Lunghezza del numero intero: 2 bit e 16 byte.

    Lunghezza di lunghezza: 4 bit e 32 byte.

    Infine, puoi usare un confronto di tipi qui:

     Public Type myIntegerType a As Integer b As Integer End Type Public Type myLongType a As Long b As Long End Type Public Sub testWithTypes() Dim testInt As myIntegerType Dim testLong As myLongType Dim bits As Integer bits = VarPtr(testInt.b) - VarPtr(testInt.a) Debug.Print "For integer in types: " & bits & " bits and " & bits * 8 & " bytes." bits = VarPtr(testLong.b) - VarPtr(testLong.a) Debug.Print "For long in types: " & bits & " bits and " & bits * 8 & " bytes." End Sub 

    che stampa anche :

    Per numero intero nei tipi: 2 bit e 16 byte.

    Per lunghi periodi: 4 bit e 32 byte.

    Questa è una prova abbastanza convincente per me che VBA tratta effettivamente Integer e Long diverso.

    Se VBA viene convertito automaticamente dietro le quinte, ci si aspetterebbe che restituiscano lo stesso numero di bit / byte per ciascuna posizione delle allocazioni del puntatore. Ma nel primo caso, con Intero, assegna solo 16 bit, mentre per le variabili Long assegna 32 bit.

    E allora?

    Quindi alla tua domanda

    Se VBA converte tutti i valori Integer in tipo Long anche se sono dichiarati come tipo Integer, il precedente non dovrebbe mai darti l’errore di Overflow!

    È assolutamente logico ottenere un errore di overflow, poiché VBA non ha effettivamente assegnato la memoria per una dichiarazione Long to the Integer .

    Sarei curioso anche se questo ritorna lo stesso su tutte le versioni di Office. Posso solo provare su Office 2010 su Windows 7 a 64 bit.

    Per quanto riguarda il test, un numero intero VBA richiede ancora due byte (testato su Access 2016, build 8201).

    Il casting implicito a lungo (e il ritorno, se si tratta di un’operazione di scrittura) si verifica per le operazioni , non per l’ archiviazione , per quanto posso trovare. Ad esempio, se faccio myInt + 1 , myInt viene castato a lungo, poi uno viene aggiunto a quel long, e quindi il risultato viene ricondotto a un int, con una conseguente perdita di prestazioni rispetto all’utilizzo di Long . Quindi, mentre costa meno memoria per usare un numero intero, tutte le operazioni ne risentiranno.

    Come Mathieu Guindon ha notato con la risposta di Enderland / Elysian Fields, testare la memoria VBA con le funzioni VBA non può provare nulla, quindi andiamo più a basso livello e guardiamo direttamente ciò che è immagazzinato nella memoria e manipoliamo quella memoria.

    Innanzitutto, dichiarazioni:

     Declare PtrSafe Sub CopyMemory Lib "Kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As Long) Public Function ToBits(b As Byte) As String Dim i As Integer For i = 7 To 0 Step -1 ToBits = ToBits & IIf((b And 2 ^ i) = (2 ^ i), 1, 0) Next End Function 

    Ora, ho intenzione di provare due cose:

    1. La memoria VarPtr punta a contenere numeri interi a 16 bit
    2. Manipolando questa memoria si manipolano gli interi utilizzati da VBA, anche se lo si manipola al di fuori di VBA

    Il codice:

     Dim i(0 To 1) As Integer 'Using negatives to prove things aren't longs, because of the sign bit i(0) = -2 ^ 15 + (2 ^ 0) '10000000 00000001 i(1) = -2 ^ 15 + (2 ^ 1) '10000000 00000010 Dim bytes(0 To 3) As Byte CopyMemory VarPtr(bytes(0)), VarPtr(i(0)), 4 Dim l As Long For l = 3 To 0 Step -1 Debug.Print ToBits(bytes(l)) & " "; 'Prints 10000000 00000010 10000000 00000001 Next 'Now, let's write something back bytes(0) = &HFF '0xFFFF = signed -1 bytes(1) = &HFF CopyMemory VarPtr(i(0)), VarPtr(bytes(0)), 2 Debug.Print i(0) '-1 

    Quindi, possiamo essere certi che VBA in effetti scrive entrambi in memoria 2 byte interi e li legge dalla memoria, quando dichiariamo le cose come un intero.

    Finora, non ho visto nessuno menzionare il problema dell’allineamento dei byte. Per manipolare i numeri, è necessario caricarli nel registro e, di regola, un registro non può contenere più di una variabile. Credo che anche i registri debbano essere cancellati dall’istruzione precedente, per garantire che la variabile sia caricata correttamente deve essere riallineata, il che può anche comportare l’ estensione del segno o l’azzeramento del registro .

    È inoltre ansible osservare l’allineamento dei byte utilizzando il codice VBA:

     Public Type x a As Integer b As Integer l As Long End Type Public Type y a As Integer l As Long b As Integer End Type Public Sub test() Dim x As x Dim y As y Debug.Print LenB(x) Debug.Print LenB(xa), LenB(xb), LenB(xl) Debug.Print LenB(y) Debug.Print LenB(ya), LenB(yl), LenB(yb) End Sub 

    Anche se l’UDT y contiene lo stesso numero di membri e ogni membro è dello stesso tipo di dati; con l’unica differenza che è l’ordine dei membri, LenB() darà risultati diversi; su una piattaforma a 32 bit, x consuma solo 8 byte mentre y richiede 12 byte. La parola alta tra xa e xl e dopo xb viene semplicemente ignorata.

    L’altro punto è che il problema non è esclusivo di VBA. Ad esempio, C ++ ha le stesse considerazioni illustrate qui e qui . Quindi questo è in realtà molto più basso livello e quindi perché non è ansible “vedere” il comportamento di estendere / estendere il segno quando si caricano le variabili nei registri per eseguire l’operazione. Per vederlo, hai bisogno dello sassembly.