Come fare unsigned saturare oltre in C?

Qual è il miglior (più pulito, più efficiente, modo di scrivere, saturando aggiunta in C?

La funzione o macro dovrebbe aggiungere due unsigned ingressi (bisogno sia di 16 – e 32-bit) per tornare all-bit-one (0xFFFF o 0xFFFFFFFF) se la somma di overflow.

Obiettivo è x86 e ARM con gcc (4.1.2) e Visual Studio (per la simulazione, in modo che una fallback attuazione è OK).

  • MSalters risposta compila il di gran lunga il miglior codice x86, eguagliando il meglio che posso fare con l’asm inline (in realtà è meglio, perché il compilatore capisce cosa sta accadendo e può scegliere quale operando sarà la destinazione di aggiungere). È allo stesso modo piuttosto bene sul BRACCIO. gcc non sembra che l’uso di un BRACCIO aggiungere con unsigned saturazione di istruzioni, però. MSalters la risposta dovrebbe essere accettato one.
  • Purtroppo la vittoria sembra scomparire con GCC 6 per il 16-bit adds16_msalters, con salti condizionali e tutto il resto.
  • Correlate: firmato saturazione: Firmato saturi aggiungere a 64 bit int? è un difficile problema. la Mia risposta non c’ bisogno di un GCC built-in funzione di compilare in modo efficiente; a differenza del carry flag, è difficile ottenere i compilatori di utilizzare la firma di overflow flag di uscita.

 

17 Replies
  1. 18

    Probabilmente si desidera portatile C qui il codice, che il compilatore trasformerà in un vero e proprio BRACCIO di montaggio. BRACCIO condizionale si muove, e questi possono essere condizionale in caso di overflow. L’algoritmo diventa quindi aggiungere, e a determinate condizioni, impostare la destinazione senza segno(-1) in caso di overflow è stato rilevato.

    uint16_t add16(uint16_t a, uint16_t b)
    {
      uint16_t c = a + b;
      if (c<a) /* Can only happen due to overflow */
        c = -1;
      return c;
    }

    Si noti che questo è diverso da altri algoritmi che si corregge in overflow, invece di basarsi su un altro calcolo per rilevare overflow.

    x86-64 clang 3.7 -O3 l’uscita per adds32: significativamente migliore rispetto a qualsiasi altra risposta:

        add     edi, esi
        mov     eax, -1
        cmovae  eax, edi
        ret

    ARMv7: gcc 4.8 -O3 -mcpu=cortex-a15 -fverbose-asm uscita per adds32:

        adds    r0, r0, r1      @ c, a, b
        it      cs
        movcs   r0, #-1         @ conditional-move
        bx      lr

    16bit: ancora non l’uso del BRACCIO unsigned-saturare istruzione add (UADD16)

        add     r1, r1, r0        @ tmp114, a
        movw    r3, #65535      @ tmp116,
        uxth    r1, r1  @ c, tmp114
        cmp     r0, r1    @ a, c
        ite     ls        @
        movls   r0, r1        @,, c
        movhi   r0, r3        @,, tmp116
        bx      lr  @
    • Questo genera ottimale di codice x86 con clang (mov eax,-1 / add / cmovnc), e circa lo stesso con gcc, a differenza di tutte le altre risposte. E ‘ l’unico che ottiene il gcc di utilizzare il flag di risultato da aggiungere, invece di fare un altro test dopo (tranne che per DGentry la risposta, ma il gcc non rendersi conto di entrambe le prove sono le stesse). Così si potrebbe dire che è l’unico in cui gcc “capisce” quello che sta succedendo. Anche l’asm inline non può fare di meglio su x86: il compilatore sa che cosa sta succedendo con il vostro, in modo che si sa che è associativo, e si potrà scegliere il reg per distruggere.
    • La cura per commentare il comportamento di più recente clang/versione di gcc? Dal clangore 3.9 e gcc 6.1, la versione a 16 bit ottiene un bel po ‘ più ingombrante. Ho convinto clang per produrre lo stesso codice come vedi disattivando likely ma gcc sembra più insistente. Le versioni a 32 bit funziona come previsto (di nuovo, la disattivazione probabilmente per clang) ma ho bisogno di un 16-bit saturare aggiungere.
    • Per senza segno a 16 bit, se il compilatore ha già valori zero estesa in registri, potrebbe essere il momento ottimale per fare una versione a 32-bit, oltre e sum & (1UL<<16) per carry-out. Compilatori non fare un ottimale lavoro con questo (con qualsiasi mezzo), ma clang6.0 ramoso versione è interessante se il caso normale è nessun overflow. godbolt.org/g/qrpPze. (Si consiglia di utilizzare lea di copia e di aggiungere, anche.) Se parziale-registro di stalla per 16-bit regs non esistono (come su Haswell), clang ramoso versione di questa risposta sembra ok, troppo, ma di gcc è uno stupido test (mancata ottimizzazione deve essere segnalato).
    • Questi potrebbe finire diverso quando l’inline; layout di filiale si sarebbe molto probabilmente essere diverso quando non è solo una funzione stand-alone.
    • il mio attuale caso d’uso è il confronto z < clamped_subtract(h, 4) dovez è un size_t e h è un uint16_t. Il codice esistente è z + 4 < h, ma che, naturalmente, non riesce se l’aggiunta overflow (molto improbabile, ma è un glitch e vorrei risolvere il problema. Non è in un percorso critico, quindi non sono troppo preoccupato, ma mi è stato lookng per vedere se c’era qualcosa di meglio di due confronti.
  2. 24

    In C:

    uint16_t sadd16(uint16_t a, uint16_t b)
        { return (a > 0xFFFF - b) ? 0xFFFF : a + b; }
    
    uint32_t sadd32(uint32_t a, uint32_t b)
        { return (a > 0xFFFFFFFF - b) ? 0xFFFFFFFF : a + b;} 

    che è quasi macro-ized e direttamente trasmette il significato.

    • Bello. Un nitpick–se non ho visto il nome sadd16 in un po ‘ di codice, la mia prima ipotesi sarebbe che il s sta per signed.
    • McQueen, Tranne per il fatto che non ci sarebbe un altro motivo per rendere una funzione.
    • Perché no? Firmato overflow/underflow non è definito.
    • Firmato aggiunta di unsigned int?
    • Craig sta parlando dal punto di vista di lettura di codice dove c’è una chiamata per sad16/32. Non vedere la firma, a meno che si trovare e aprire l’intestazione.
    • x=5,y=6,z=sadd32(x,y);? I tipi sono visibili.
    • Spesso quando si sta leggendo un diff (ad esempio, per la revisione del codice, o se si sta inviando patch open source mailing list) la chiamata è abbastanza lontano dalla dichiarazione di parametri che non si possono raccontare.
    • tranne che la maggior parte della IDE consentono di posizionare il mouse su una chiamata (o qualcosa del genere) per vedere la decelerazione.
    • Avrebbe senso inline questo? attribute((always_inline)) per GCC; __forceinline per MSVC
    • Utilizzare inline invece. Non utilizzare __attribute__((always_inline)), che di solito è un modo di seconda indovinando l’ottimizzatore. Il inline parola chiave che si desidera che in realtà ed è portatile.
    • Perché non forza inline oltre la portabilità? Anche con un semplice inline, è non portatili. MSVC richiede __inline
    • È possibile #define inline __inline abbastanza facilmente, se si desidera il supporto compilatori con scarso supporto per C, e fintanto che siete a conoscenza delle differenze semantiche. Non c’è davvero alcun motivo per utilizzare always_inline qui, e un buon motivo per non: può interferire con il debug. Per lo stesso motivo, i programmatori C hanno abbandonato dichiarando register variabili di tempo fa: non c’è nessun punto, tranne forse in circostanze molto specifiche.
    • Sono abbastanza fiera. Non ho intenzione di sedermi qui e essere data una lezione su qualcosa che già so. Tuttavia, un smart compilatore sarebbe non funzioni inline, anche se costretta a quando è in modalità di debug. Un esempio è MSVC. Se ti dico che al compilatore per la modalità di debug, non in linea (anche forzato) funzioni.
    • Scusa se la mia risposta sembrava una lezione. Ma questo non è su come “smart” un compilatore, ma solo come si comporta, e GCC inlines un always_inline anche durante la compilazione con i simboli di debug.
    • E ‘ stupido. Credo che non ho mai notato perché io lavoro in MSVC, allora la porta a GCC quando fatto.
    • Un piccolo suggerimento: Il 0xFF.. costanti deve essere modificato equivalente UINTN_MAX costanti (o (uintN_t) -1). In quel modo, ci vorrà solo un singolo di ricerca & sostituire scrivere il sadd8 o sadd64 funzioni. (E non c’è bisogno di contare il numero di Fs in 0xFFFFFFFFFFFFFFFF 😉
    • Questo produce del buon codice in gcc 5.1 è rivolto armv4t, a soli 4 rami istruzioni (due di loro condizionale).
    • Questo produce significativamente peggiore codice x86 e ARM di MSalter risposta. Di dare un’occhiata per asm in uscita (tra cui un godbolt link da confrontare con esso.)

  3. 18

    In IA32 senza salti condizionali:

    uint32_t sadd32(uint32_t a, uint32_t b)
    {
    #if defined IA32
      __asm
      {
        mov eax,a
        xor edx,edx
        add eax,b
        setnc dl
        dec edx
        or eax,edx
      }
    #elif defined ARM
      //ARM code
    #else
      //non-IA32/ARM way, copy from above
    #endif
    }
    • La domanda per la C, ma comunque, il bello di codice. È eax restituito per impostazione predefinita come il risultato della funzione?
    • Se la domanda ha voluto la portabilità, non dovrebbe avere specificato x86 e ARM 😉
    • Che la funzione è ancora portatile – una volta che il elif e l’altro i casi sono riempiti. Codice portabile non significa che non è possibile ottimizzare per particolari piattaforme.
    • Una proposta di modifica da YumeYao (di cui non ho spinto attraverso, come cambia la natura della risposta): 3 istruzioni (xor reg,reg; setne reg; dec reg;) può essere sostituito con uno più efficiente istruzione (ffs reg,reg).
    • Due cose: la __asm parola chiave è del compilatore-dipendente. La norma non specifica di una parola chiave per l’assembly inline. Quindi, questo è non portatile, nel senso che è del compilatore-dipendente. Per esempio, il processore Intel C++ compiler è solo per Windows, quindi, se hai scritto un codice portatile utilizzando Itel C++ caratteristiche, non sarebbe portatili. Un’altra cosa: l’assembly inline impedisce compilatore inline. Così questa ottimizzazione non aiuta se c’è ancora la funzione di chiamata in testa…
    • “…:…?…” sarebbe un condizionale sposta invece di “saltare”.
    • Questo schifo un pò: prima di tutto perché è MSVC inline asm, così ingressi / uscite devono passare attraverso la memoria. (O, se nessun-ritorno-dichiarazione con un valore in eax funziona, quindi la funzione stessa non può in linea. Gli ingressi devono passare attraverso la memoria, a prescindere). In secondo luogo, perché cmov è meglio: breve percorso critico perché mov eax, -1 è spento il percorso critico, a differenza di sbb.

  4. 11

    In BRACCIO si può avere già saturo aritmetica built-in. Il ARMv5 DSP-estensioni possono saturare i registri per qualsiasi bit di lunghezza. Anche sul BRACCIO di saturazione è di solito conveniente, perché si può excute più istruzioni condizionali.

    ARMv6 ha anche saturo di addizione, sottrazione e tutte le altre cose, per la versione a 32 bit e imballato numeri.

    Su x86 si ottiene saturi aritmetica via MMX o SSE.

    Tutto questo ha bisogno di assembler, quindi non è quello che hai chiesto.

    Ci sono C-trucchi per fare saturi aritmetica, come bene. Questo piccolo codice non saturi, oltre a quattro byte di un valore dword. Si basa sull’idea di calcolare 32 mezza marassi in parallelo, ad esempio, l’aggiunta di numeri senza portare overflow.

    Questo viene fatto prima. Poi la porta sono calcolati, aggiunto e sostituito con una maschera, se l’addizione di overflow.

    uint32_t SatAddUnsigned8(uint32_t x, uint32_t y) 
    {
      uint32_t signmask = 0x80808080;
      uint32_t t0 = (y ^ x) & signmask;
      uint32_t t1 = (y & x) & signmask;
      x &= ~signmask;
      y &= ~signmask;
      x += y;
      t1 |= t0 & x;
      t1 = (t1 << 1) - (t1 >> 7);
      return (x ^ t0) | t1;
    }

    Si può ottenere lo stesso per 16 bit (o qualsiasi tipo di campo di bit) cambiando il signmask costante e la sposta in basso come questo:

    uint32_t SatAddUnsigned16(uint32_t x, uint32_t y) 
    {
      uint32_t signmask = 0x80008000;
      uint32_t t0 = (y ^ x) & signmask;
      uint32_t t1 = (y & x) & signmask;
      x &= ~signmask;
      y &= ~signmask;
      x += y;
      t1 |= t0 & x;
      t1 = (t1 << 1) - (t1 >> 15);
      return (x ^ t0) | t1;
    }
    
    uint32_t SatAddUnsigned32 (uint32_t x, uint32_t y)
    {
      uint32_t signmask = 0x80000000;
      uint32_t t0 = (y ^ x) & signmask;
      uint32_t t1 = (y & x) & signmask;
      x &= ~signmask;
      y &= ~signmask;
      x += y;
      t1 |= t0 & x;
      t1 = (t1 << 1) - (t1 >> 31);
      return (x ^ t0) | t1;
    }

    Codice di cui sopra, fa lo stesso con 16 e 32 bit.

    Se non avete bisogno di funzionalità che le funzioni add e saturare di più i valori in parallelo solo mascherare i bit di cui hai bisogno. Sul BRACCIO si desidera modificare il signmask costante perché il BRACCIO non è possibile caricare tutti i possibili 32 bit costanti in un unico ciclo.

    Edit: Le versioni parallele sono probabilmente più lento di dritto in avanti metodi, ma essi sono più veloci se si hanno a saturare di più di un valore alla volta.

    • Non ho visto un unsigned saturazione istruzioni per 32bit interi, solo per packed16 UQUADD16 e packed8. C’è un 32bit aggiungere firmato-saturazione, però. Purtroppo anche questo codice C compila orribile codice per la 32bit caso: tutto l’overhead di farlo SWAR stile, ma per un solo valore. Purtroppo non ottimizzare distanza. Vedi il mio commento sul MSalters risposta: il godbolt link include la versione.
  5. 10

    Se ti interessano le prestazioni, è davvero vuole fare questo genere di cose in SIMD, dove x86 è nativo di saturare l’aritmetica.

    A causa di questa mancanza di saturare aritmetica di scalare la matematica, si può ottenere casi in cui le operazioni eseguite su 4-variabile-ampia SIMD è più di 4 volte più veloce rispetto l’equivalente C (e di conseguenza il vero con 8-variabile-ampia SIMD):

    sub8x8_dct8_c: 1332 clocks
    sub8x8_dct8_mmx: 182 clocks
    sub8x8_dct8_sse2: 127 clocks
    • Utilizza le istruzioni SSE ancor di più nei casi in cui sei solo e sempre operativo su una variabile alla volta?
    • sì, è può essere, se hai bisogno di saturare 16-bit o 8 bit aggiungere o sottrarre. O bit-retro (con SSSE3 pshufb per ogni nibble parallelo tabella di ricerca). O con SSE4.1, min o max su numeri interi a 32 bit (o abs) con una singola istruzione. O 64-bit integer matematica codice a 32 bit. Ma c’è un overhead ottenere numeri tra XMM e registri integer, in modo da utilizzare con cautela.
  6. 10

    Zero ramo soluzione:

    uint32_t sadd32(uint32_t a, uint32_t b)
    {
        uint64_t s = (uint64_t)a+b;
        return -(s>>32) | (uint32_t)s;
    }

    Un buon compilatore per ottimizzare questo per evitare di fare qualsiasi reale aritmetica a 64 bit (s>>32 sarà solo il carry flag, e -(s>>32) è il risultato di sbb %eax,%eax).

    In asm x86 (AT&T sintassi, a e b in eax e ebx, risultato in eax):

    add %eax,%ebx
    sbb %eax,%eax
    or %ebx,%eax

    A 8 e 16 bit, dovrebbe essere ovvio. Versione firmata potrebbe richiedere un po ‘ di lavoro in più.

    • Ti auguro un compilatore sarebbe posto, ma non è così. clang/gcc/icc tutti di fare una schifezza di lavoro su tutto tranne MSalter la risposta. Il vostro compila lea eax, [rdi+rsi]/ mov edx, edi / mov ecx, esi / add rdx, rcx / shr rdx, 32 / neg edx / or eax, edx
  7. 7
    uint32_t saturate_add32(uint32_t a, uint32_t b)
    {
        uint32_t sum = a + b;
        if ((sum < a) || (sum < b))
            return ~((uint32_t)0);
        else
            return sum;
    } /* saturate_add32 */
    
    uint16_t saturate_add16(uint16_t a, uint16_t b)
    {
        uint16_t sum = a + b;
        if ((sum < a) || (sum < b))
            return ~((uint16_t)0);
        else
            return sum;
    } /* saturate_add16 */

    Edit: Ora che hai postato la tua versione, io non sono sicuro che il mio è il detergente/migliore/più efficace/più tosti.

    • La tua risposta sembra che ciò che ho pensato che dovremmo fare, ma come hai detto tu io non sono davvero sicuro che è meglio, che è il motivo per cui ho pensato di aprirlo per votare qui.
    • Entrambi sembrano corrette, quindi di efficienza dovrebbe decidere. Un extra di confronto non è, ovviamente, più lento o più veloce) rispetto a un sovradimensionamento dell’aggiunta. Fare alcune prove di efficienza per entrambe le soluzioni su entrambe le architetture e scegliere quella più veloce.
    • Verifica la somma contro entrambi gli ingressi necessari? Il caso limite è (uint16_t)(0xffff + 1), che è sia < 1 e < 0xffff, quindi non mi sembra il secondo controllo può essere evitato.
    • Hai ragione, la perdita di bit di overflow è la pena di MAXINT+1, quindi il risultato di tracimato oltre è uguale a+b-(MAXINT+1), che è a meno di a e minore di b.
    • Perché utilizzare ~((uint32_t)0)? Sei già compresi <limits.h> per ottenere il uint32_t decelerazione, quindi perché non basta usare UINT32_MAX?
  8. 3

    Non sono sicuro se questo è più veloce rispetto Skizz soluzione (sempre di profilo), ma qui è un’alternativa non-filiale di assemblaggio di soluzione. Si noti che questo richiede il condizionale spostare (CMOV) istruzione, che non sono sicuro è disponibile sul vostro obiettivo.

    
    uint32_t sadd32(uint32_t a, uint32_t b)
    {
        __asm
        {
            movl eax, a
            addl eax, b
            movl edx, 0xffffffff
            cmovc eax, edx
        }
    }
    • Il BRACCIO è “C-tutto”. Non basta saltare e muoversi. Ma non ha il supporto per 32 bit costanti. Quindi, si vorrebbe un condizionale mov 0, seguito da un condizionale sub 1
    • Il BRACCIO può creare dei piccoli numeri negativi con mvn (mov-NON) con un immediato. Assemblatori sapere come utilizzare questo per voi, ad esempio, adds r0, r1 (aggiungere e impostare il flag) / `movCS r0, #-1` (mvn 0 = -1 se Eseguire Imposta). xD, MSalter propria risposta inviato dopo mostra che i compilatori già fare esattamente questo. E anche emettere questo per x86, così non devi. E in un modo che può in linea e costante di propagazione.
  9. 2

    L’implementazione corrente che stiamo usando è:

    #define sadd16(a, b)  (uint16_t)( ((uint32_t)(a)+(uint32_t)(b)) > 0xffff ? 0xffff : ((a)+(b)))
    #define sadd32(a, b)  (uint32_t)( ((uint64_t)(a)+(uint64_t)(b)) > 0xffffffff ? 0xffffffff : ((a)+(b)))
    • minuscole funzione macro? Male!
    • non si deve fare un sacco di Linux kernel lavoro 🙂
  10. 2

    Le migliori prestazioni si comportano di solito assembly inline (come alcuni hanno già detto).

    Ma per portatile C, queste funzioni richiedono solo un confronto e non type-casting (e quindi, mi sembra ottimale):

    unsigned saturate_add_uint(unsigned x, unsigned y)
    {
        if (y>UINT_MAX-x) return UINT_MAX;
        return x+y;
    }
    
    unsigned short saturate_add_ushort(unsigned short x, unsigned short y)
    {
        if (y>USHRT_MAX-x) return USHRT_MAX;
        return x+y;
    }

    Come macro, diventano:

    SATURATE_ADD_UINT(x, y) (((y)>UINT_MAX-(x)) ? UINT_MAX : ((x)+(y)))
    SATURATE_ADD_USHORT(x, y) (((y)>SHRT_MAX-(x)) ? USHRT_MAX : ((x)+(y)))

    Lascio versioni per ‘unsigned long’ e ‘unsigned long long’ come esercizio per il lettore. 😉

  11. 2

    Nel caso in cui qualcuno vuole sapere un’implementazione senza ramificazione utilizzando il complemento a 2 32bit interi.

    Attenzione! Questo codice utilizza il undefined operazione: “spostamento a destra da -1” e quindi sfrutta le proprietà del Intel Pentium SAL istruzione per mascherare il conte operando a 5 bit.

    int32_t sadd(int32_t a, int32_t b){
        int32_t sum = a+b;
        int32_t overflow = ((a^sum)&(b^sum))>>31;
        return (overflow<<31)^(sum>>overflow);
     }

    È la miglior realizzazione a me noto,

    • È possibile scrivere overflow&31, e ancora compilazione senza sprecato and ecx, 31, perché gcc e clang sapere come il passaggio opere di istruzione (ISA definisce in questo modo, su ogni CPU dal 286. Vedere la Intel insn rif manuale collegati da x86 tag wiki. Sulle destinazioni in cui il passaggio funziona in modo diverso modo, essi emettono le istruzioni necessarie per farlo funzionare. Naturalmente, questo si basa ancora sulla destra-shift di un valore integer con segno utilizzando uno shift aritmetico, che il C standard non garantisce.
    • Questo utilizza anche l’indefinito operazione di a+b traboccante! Firmato overflow è UB in C e C++.
  12. 1

    Suppongo, il modo migliore per x86 è quello di utilizzare inline assembler per controllare overflow flag, dopo l’aggiunta. Qualcosa come:

    add eax, ebx
    jno @@1
    or eax, 0FFFFFFFFh
    @@1:
    .......

    Non è molto portatile, ma IMHO il modo più efficiente.

    • Credo che la risposta per il BRACCIO è simile (e anche più efficiente con la condizionale ops), ma sto sperando che qualcuno conosce un modello che verrà trucco GCC per generare qualcosa di simile a questa.
    • che GCC versione stai usando? (gcc –version). Le versioni più recenti di fare questi trucchi.
    • E ‘ gcc 4.1.2.
    • jno controlli per firma overflow. jnc sarebbe verificare unsigned avvolgente come questo Q vuole, che sarebbe partita con mov eax, -1 (o il breve modulo con una falsa dipendenza; or eax, -1). Ma se avete intenzione di introdurre una dipendenza di dati su aggiungi, sconfiggendo il beneficio per i branch-prediction + esecuzione speculativa, si potrebbe utilizzare sbb edx,edx / or eax, edx per la trasmissione di CF per tutti i bit e / O in. Ma CMOVC sarebbe più efficiente, solo 1 o 2 uops sul percorso critico, invece di 2 o 3.
  13. 1

    Un’alternativa al ramo gratis asm x86 soluzione è (AT&T sintassi, a e b in eax e ebx, risultato in eax):

    add %eax,%ebx
    sbb $0,%ebx
    • sbb $0, %ebx sottrae 1 o meno. Questo dà la risposta sbagliata, se il componente aggiuntivo è tracimato in più di 1. Ciò che funziona (come suggerito da altri) utilizza sbb same,same a produrre come 0 o -1 maschera, e / O aggiungere il risultato con che. Tuttavia, che ha un più critici del percorso di latenza di add %edi, %esi / mov $-1, %eax / cmovnc %esi, %edi. (ffs e cmov hanno la stessa latenza su tutte le Cpu: 2 su Intel pre-Broadwell, e 1 contrario.)
  14. 0

    Utilizzando C++ si potrebbe scrivere un più flessibile variante di Remo.D‘s soluzione:

    template<typename T>
    T sadd(T first, T second)
    {
        static_assert(std::is_integral<T>::value, "sadd is not defined for non-integral types");
        return first > std::numeric_limits<T>::max() - second ? std::numeric_limits<T>::max() : first + second;
    }

    Questo può essere facilmente tradotto in C – utilizzando i limiti definiti nel limits.h. Si prega di notare che la Larghezza fissa i tipi interi potrebbe non disponibile nel sistema.

  15. 0
    //function-like macro to add signed vals, 
    //then test for overlow and clamp to max if required
    #define SATURATE_ADD(a,b,val)  ( {\
    if( (a>=0) && (b>=0) )\
    {\
        val = a + b;\
        if (val < 0) {val=0x7fffffff;}\
    }\
    else if( (a<=0) && (b<=0) )\
    {\
        val = a + b;\
        if (val > 0) {val=-1*0x7fffffff;}\
    }\
    else\
    {\
        val = a + b;\
    }\
    })

    Ho fatto una rapida prova e sembra funzionare, ma non ampiamente sfondato ancora! Questo funziona con FIRMATO a 32 bit.
    op : l’editor utilizzato sulla pagina web non mi permette di postare una macro cioè la sua non comprensione senza rientro sintassi ecc!

  16. 0
    int saturating_add(int x, int y)
    {
        int w = sizeof(int) << 3;
        int msb = 1 << (w-1);
    
        int s = x + y;
        int sign_x = msb & x;
        int sign_y = msb & y;
        int sign_s = msb & s;
    
        int nflow = sign_x && sign_y && !sign_s;
        int pflow = !sign_x && !sign_y && sign_s;
    
        int nmask = (~!nflow + 1);
        int pmask = (~!pflow + 1);
    
        return (nmask & ((pmask & s) | (~pmask & ~msb))) | (~nmask & msb);
    }

    Questa implementazione non utilizzare flussi di controllo, confrontare operatori(==, !=) e il ?: operatore. Si utilizza solo gli operatori bit a bit e operatori logici.

  17. 0

    Saturazione aritmetica non è standard per C, ma è spesso implementato attraverso il compilatore intrinseci, in modo più efficiente, in modo che non sarà il più pulito. È necessario aggiungere #ifdef blocchi per selezionare il modo corretto. MSalters di risposta è il metodo più rapido per architettura x86. Per il BRACCIO è necessario utilizzare __qadd16 funzione (compilatore ARM) di _arm_qadd16 (Microsoft Visual Studio) per 16 bit e __qadd per la versione a 32 bit. Saranno convertiti automaticamente in un BRACCIO di istruzioni.

    Link:

Lascia un commento