En ARM, vous avez peut-être déjà intégré l'arithmétique saturée. Les extensions DSP ARMv5 peuvent saturer les registres à n'importe quelle longueur de bit. De plus, sur ARM, la saturation est généralement bon marché car vous pouvez excuter la plupart des instructions conditionnelles.
ARMv6 a même l'addition saturée, la soustraction et tous les autres trucs pour les 32 bits et les nombres emballés.
Sur le x86, vous obtenez une arithmétique saturée via MMX ou SSE.
Tout cela nécessite un assembleur, ce n'est donc pas ce que vous avez demandé.
Il existe également des astuces C pour faire de l'arithmétique saturée. Ce petit code fait une addition saturée sur quatre octets d'un dword. Il est basé sur l'idée de calculer 32 demi-additionneurs en parallèle, par exemple pour additionner des nombres sans débordement de retenue.
Ceci est fait en premier. Ensuite, les reports sont calculés, additionnés et remplacés par un masque si l'addition déborde.
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;
}
Vous pouvez obtenir la même chose pour 16 bits (ou n'importe quel type de champ de bits) en modifiant la constante signmask et les décalages en bas comme ceci :
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;
}
Le code ci-dessus fait la même chose pour les valeurs de 16 et 32 bits.
Si vous n'avez pas besoin de la fonction d'addition et de saturation de plusieurs valeurs en parallèle, masquez simplement les bits dont vous avez besoin. Sur ARM, vous devez également modifier la constante signmask car ARM ne peut pas charger toutes les constantes 32 bits possibles en un seul cycle.
Editar: Les versions parallèles sont très probablement plus lentes que les méthodes directes, mais elles sont plus rapides si vous devez saturer plus d'une valeur à la fois.
2 votes
La réponse de MSalters se résume à de loin le meilleur code sur x86 Le compilateur comprend ce qui se passe et peut choisir l'opérande qui sera la destination de l'addition. C'est également assez bon sur ARM. gcc ne semble pas utiliser l'instruction add with unsigned saturation d'ARM, cependant. La réponse de MSalters devrait être la réponse acceptée. .
0 votes
Malheureusement la victoire semble disparaître avec GCC 6 pour les adds16_msalters 16 bits, avec les sauts conditionnels et tout.
0 votes
En rapport : saturation signée : Addition saturée signée d'ints 64 bits ? est un problème plus difficile. Ma réponse avait besoin d'une fonction intégrée à GCC pour compiler efficacement ; contrairement au drapeau de report, il est difficile d'obtenir des compilateurs qu'ils utilisent la sortie du drapeau de dépassement signé.