Segno e compilazione di interi tramite C

3

Sto scrivendo un compilatore che usa l'antica strategia di usare un compilatore C come back-end, e sto cercando di capire esattamente come gestire il segno intero.

Sto usando gli interi di parola macchina come default (traducendo in intptr_t e uintptr_t nella C generata), e mentre per me preferirei rendere il default senza segno, probabilmente non ho molta scelta riguardo alla sua firma solo perché tale aspettativa è così radicata.

Sto definendo il risultato dell'overflow di interi per essere wraparound (almeno per impostazione predefinita, potresti aggiungere un'opzione per lanciare un'eccezione in seguito), ma in C, l'overflow di interi con segno è un comportamento indefinito, quindi non posso emettere solo codice che esegue l'aritmetica dell'intero con segno. Mi sembra ci siano due opzioni:

  1. Emetti interi con segno, ma converti in unsigned per tutte le operazioni aritmetiche eccetto la divisione e il right shift (questi sono gli unici dove conta la differenza, e anche gli unici che non possono overflow - edit: ad eccezione di INT_MIN / -1; stavo considerando che nella stessa categoria della divisione per zero, ma ovviamente si tratta in senso stretto di un overflow).

  2. Emetti interi senza segno, ma converti in segno per divisione (resistendo alla tentazione di fare divisione dei numeri negativi comportamento indefinito), spostamento a destra e confronto diverso dall'uguaglianza.

  3. Emetti interi con segno, usa -fwrapv quando usi GCC come backend, e spera che altri compilatori C non traggano vantaggio dall'overflow firmato indefinito, o forniscano un interruttore per disabilitare tali ottimizzazioni.

La terza opzione funziona (ad es. ci sono compilatori C in cui non è possibile eseguire il wrap overflow con firma)? In caso contrario, quale delle due prime probabilità implicherà meno codice e opportunità di errore? C'è qualcosa che mi manca?

    
posta rwallace 22.04.2012 - 10:56
fonte

4 risposte

1

Delle tre opzioni che hai pubblicato, voterei per 2.

La prima opzione sta ancora invocando un comportamento indefinito, in affermazioni come questa:

int result = (unsigned)1 - (unsigned)10;

La terza opzione è qualcosa di simile a "facciamolo comunque e spero che funzioni". Certamente, non lo consiglierei.

Il secondo probabilmente fa ciò che vuoi (IIUC) e non richiama un comportamento indefinito.

    
risposta data 22.04.2012 - 13:31
fonte
5

Perché non basta che il compilatore generi codice per verificare se è possibile un overflow prima di eseguire operazioni aritmetiche su interi con segno? In tal modo, assicurati di non causare un comportamento indefinito prima di eseguire l'operazione. Puoi eseguire l'aritmetica o lanciare un'eccezione (o qualsiasi altra cosa).

Qualcosa di simile (tratto dal link ):

int add(int lhs, int rhs)
{
 if (lhs >= 0) {
  if (INT_MAX - lhs < rhs) {
   /* overflow will occur */
   abort();
  }
 }
 else {
  if (rhs < INT_MIN - lhs) {
   /* overflow will occur */
   abort();
  }
 }
 return lhs + rhs;
}

Inoltre, la tua ipotesi che la divisione non possa traboccare non è vera. L'espressione INT_MIN / -1 provoca un overflow.

    
risposta data 22.04.2012 - 13:14
fonte
0

Suggerirei di utilizzare l'aritmetica non firmata per tutto, utilizzando alcune definizioni di tipo condizionale per garantire che nulla venga promosso in modo imprevisto a int . Per confrontare due valori "firmati" a 32 bit, xor entrambi con 0x80000000U e quindi confrontare come non firmati. Per la divisione con segno, utilizzare un metodo di libreria per elaborare entrambi gli operandi come positivi, in modo da utilizzare la divisione senza segno.

    
risposta data 06.11.2014 - 23:52
fonte
-1

Devi essere conforme allo standard al 100%?
È molto buono non assumere nulla che lo standard non garantisca e non garantisce nulla sull'overflow dei numeri interi firmati.
Ma in pratica, la maggior parte dei sistemi gestisce un overflow di interi con segno molto semplicemente (MAX_INT + 1 = MIN_INT).

Quindi, se conosci la piattaforma di destinazione e non devi supportare alcuna piattaforma arbitraria che supporti C, puoi semplicemente utilizzare gli interi con segno così come sono.

Un'alternativa, che è tecnicamente la stessa cosa, è di fare overflow di interi con segno indefinito nella lingua che stai compilando, proprio come in C.

    
risposta data 22.04.2012 - 16:58
fonte

Leggi altre domande sui tag