2009-08-18 7 views
7

Ho due cortocircuiti a 16 bit (s1 e s2) e sto cercando di unirli in un unico numero intero a 32 bit (i1). Secondo le specifiche che sto trattando, s1 è la parola più significativa, e s2 è la parola meno significativa, e la parola combinata sembra essere firmata. (cioè il bit più in alto di s1 è il segno.)Il modo più semplice per combinare due cortocircuiti con un numero

Qual è il modo più pulito di combinare s1 e s2?

ho pensato qualcosa di simile

const utils::int32 i1 = ((s1<<16) | (s2)); 

avrebbe fatto, e sembra funzionare, ma sono preoccupato per sinistra-shifting breve da 16.

Inoltre, mi interessa la idea di usare un sindacato per fare il lavoro, qualche idea se questa è una buona o cattiva idea?

risposta

1

Prova la proiezione esplicita data.second di tipo breve, come:

const utils::int32 combineddata = ((data.first<<16) | ((short)data.second)); 

edit: Sono C# dev, probabilmente il casting nella tua lingua codice di un aspetto diverso, ma l'idea potrebbe essere lo stesso.

0

Si desidera trasmettere data.first a un int32 prima di eseguire lo spostamento, altrimenti lo spostamento traboccherà la memoria prima che abbia la possibilità di essere promosso automaticamente quando viene assegnato ai dati combinati.

Prova:

const utils::int32 combineddata = (static_cast<utils::int32>(data.first) << 16) | data.second; 

Questo è, naturalmente, assumendo che data.first e data.second sono tipi che sono garantiti per essere esattamente 16 bit lunghi, altrimenti si hanno grossi problemi.

Davvero non capisco la tua affermazione "se data.second diventa troppo grande, il | non prenderà in considerazione il fatto che sono entrambi i cortometraggi".

Modifica: E Neil ha assolutamente ragione sulla firma.

+5

Nel caso di corti non sono sicuro che sia necessario. Lo standard ha: "Gli operandi devono essere di tipo integrale o di enumerazione e vengono eseguite promozioni integrali Il tipo di risultato è quello dell'operando sinistro promosso". Pertanto, il cortometraggio verrà promosso implicitamente. –

+0

Huh. Non lo sapevo - immagino sia perché sono sempre paranoico sulle dimensioni dei tipi di dati. Grazie per il puntatore. –

+0

È bello essere paranoici perché il cast sarebbe sicuramente richiesto su piattaforme con un int a 16 bit! –

13

Quello che stai facendo è significativo solo se i corti e gli interi sono tutti senza segno. Se uno dei cortometraggi è firmato e ha un valore negativo, l'idea di combinarli in un singolo int è priva di significato, a meno che non sia stata fornita una specifica specifica del dominio per coprire tale eventualità.

+0

Accetto che il codice postato abbia un bug di estensione del segno e che la richiesta di "combinare" due in16_tss con un int32_t sia piuttosto strana. Ma non penso che sia necessariamente privo di significato, nel senso che si potrebbe definire una semantica per l'operazione. –

+1

Hai letto le ultime 15 parole della mia risposta? –

+0

Sì, sembra che l'interfaccia per l'hardware sia la divisione di un intero con segno in 2 e quindi l'invio, perché ha un MSW e un LSW, ma apparentemente è nella firma del complemento a 2. Quindi ho bisogno di ricombinare i dati sensibilmente da quello. – deworde

6

Quello che hai sembra quasi corretto, ma probabilmente fallirà se la seconda parte è negativa; la conversione implicita in int probabilmente firmerà-estenderà e riempirà i 16 bit superiori con uno. Un cast per unsigned short probabilmente impedirebbe che ciò accada, ma il modo migliore per essere sicuri è mascherare i bit.

const utils::int32 combineddata = ((data.first<<16) | ((data.second) & 0xffff)); 
+0

Eccellente, ha molto senso. – deworde

+0

In effetti la conversione implicita in int causerà problemi. Ad esempio, data.first << 16' invocherà anche un comportamento non definito in questo codice postato, se data.first è negativo. Quindi questa non è una buona soluzione. Nel complesso, non è una buona idea usare gli operatori del turno insieme ai numeri firmati. Soluzione migliore: 'uint32_t u32 = (uint32_t) data.first << 16 | (Uint32_t) data.second; combineddate = (int32_t) u32; ' – Lundin

-1

Utilizzare un sindacato per fare il lavoro sembra una buona scelta, ma è un problema di portabilità dovuto alle differenze endian dei processori. È fattibile, ma devi essere pronto a modificare il tuo sindacato in base all'architettura di destinazione. Il cambio di bit è portatile, ma per favore scrivi una funzione/metodo per farlo per te. In linea se ti piace.

Per quanto riguarda la firma dei cortocircuiti, per questo tipo di operazione, è il significato che non interessa il tipo di dati. In altre parole, se s1 e s2 devono essere interpretati come due metà di una parola a 32 bit, il bit 15 impostato è importante solo se si fa qualcosa che potrebbe causare l'estensione di s2 al segno.Vedere Mark Ransoms risposta, che potrebbe essere meglio come

inline utils::int32 CombineWord16toLong32(utils::int16 s1, utils::int16 s2) 
{ 
    return ((s1 <<16) | (s2 & 0xffff)); 
} 
3

Dal momento che nessuno ha postato, questo è ciò che il sindacato sarebbe simile. Ma i commenti sull'endianità sicuramente si applicano.

Big-endian:

typedef union { 
    struct { 
     uint16_t high; 
     uint16_t low; 
    } pieces; 
    uint32_t all; 
} splitint_t; 

Little-endian:

typedef union { 
    struct { 
     uint16_t low; 
     uint16_t high; 
    } pieces; 
    uint32_t all; 
} splitint_t; 
+0

nota che la specifica fornisce zero garanzie a riguardo. È ampiamente usato in C soprattutto, e dubito che qualsiasi compilatore intenzionalmente lo romperà, ma si tratta in realtà di un comportamento indefinito. In generale non è possibile scrivere su un membro di una struttura e leggere da un altro. – jalf

+0

Si noti che lo standard non garantisce che 'all' avrà dati sensibili dopo l'assegnazione a' high' e 'low', e viceversa. Questa soluzione non è portatile. – Juliano

+0

Non c'è motivo di usare un sindacato per questo, tutto ciò che si ottiene sono problemi di portabilità. E il tipo punning non è ben definito in C++, a differenza di C. – Lundin

3

So che questo è un vecchio post, ma la qualità del presente postato risposte è deprimente ...

Questi sono i problemi da considerare:

  • La promozione di interi impliciti di cortometraggi (o altri tipi di interi piccoli) darà come risultato un operando di tipo int firmato. Ciò avverrà indipendentemente dalla firma del tipo di intero piccolo. La promozione dei numeri interi avviene nelle operazioni di spostamento e in OR bit a bit.
  • Nel caso degli operatori di spostamento, il tipo risultante è quello dell'operando sinistro promosso. In caso di OR bit a bit, il tipo risultante viene ricavato dalle "normali conversioni aritmetiche".
  • Lo spostamento a sinistra di un numero negativo provoca un comportamento non definito. Spostando a destra un numero negativo si ottiene un comportamento definito dall'implementazione (spostamento logico vs calcolo aritmetico). Pertanto, i numeri firmati non devono essere utilizzati insieme a cambiamenti di bit nel 99% di tutti i casi d'uso.
  • Unioni, matrici e simili sono soluzioni scadenti poiché rendono il codice dipendente da endianess. Inoltre, digitare il punning attraverso i sindacati non è un comportamento ben definito in C++ (a differenza di C). Le soluzioni basate su puntatore sono pessime poiché finiranno per violare "la rigida regola di aliasing".

una soluzione adeguata sarà quindi:

  • Utilizzare operandi con tipi che sono garantiti per essere firmato e non verrà implicitamente promossi.
  • Utilizzare i bit shift, poiché sono indipendenti dall'endianess.

Sarà simile a questa:

int32_t i32 = (int32_t)((uint32_t)s1<<16 | (uint32_t)s2); 

Ogni altra soluzione è molto discutibile e nella migliore delle ipotesi non portabile.

+0

Heh. Per essere onesti, guardando indietro con 8 anni di esperienza in più, le uniche persone che avrebbero potuto davvero rispondere a questo sono i progettisti stessi dell'API hardware. All'epoca mi chiedevo se stessero usando un metodo standard noto di combinazione, ma ... – deworde

+0

@deworde Cosa intendi con "hardware API"? Questo codice è più vicino al metallo che ottiene. – Lundin

Problemi correlati