Comment trouver de manière portable min (INT MAX, abs (INT MIN))?
Comment puis-je trouver de manière portable le plus petit des INT_MAX
et abs (INT_MIN
)? (C'est la valeur absolue mathématique de INT_MIN
, pas un appel à la fonction abs
.)
Il devrait être le même que INT_MAX
dans la plupart des systèmes, mais je cherche un moyen plus portable.
6 réponses
, Tandis que le typique valeur de INT_MIN
est -2147483648, et la typique valeur de INT_MAX
est 2 147 483 647, il n'est pas garanti par la norme. TL; DR: la valeur que vous recherchez est INT_MAX
dans une implémentation conforme. Mais calcul de min(INT_MAX, abs(INT_MIN))
n'est pas portable.
Les valeurs possibles de INT_MIN
et INT_MAX
INT_MIN
et INT_MAX
sont définis par L'Annexe E (limites D'implémentation) 1 (norme C, C++ hérite de ce genre de choses):
Le contenu de l'en-tête est donné ci-dessous, par ordre alphabétique ordre. Les valeurs minimales indiquées sont remplacées par magnitudes définies par l'implémentation avec le même signe. Les valeurs sont toutes sont des expressions constantes pouvant être utilisées dans le prétraitement # if directive. Les composants sont décrits plus en détail au 5.2.4.2.1.
[...]
#define INT_MAX +32767
#define INT_MIN -32767
[...]
La norme exige que le type int
soit un type entier pouvant représenter la plage [INT_MIN, INT_MAX]
(section 5.2.4.2.1.).
Ensuite, 6.2.6.2. (Les types entiers, encore une fois une partie de la norme C), entre en jeu et restreint davantage cela à ce que nous connaissons comme deux ou un complément :
Pour les types entiers signés, les bits de la représentation de l'objet doivent être divisés en trois groupes: bits de valeur, bits de remplissage et bits de signe. Il n'y a pas besoin de bits de rembourrage; char signé ne doit pas avoir de bits de remplissage. Il doit y avoir exactement un signe bit. Chaque bit de la valeur des bits ont la même valeur que le même bit dans l'objet représentation du type non signé correspondant (S'il y a M bits de valeur dans le type et N dans le type non signé, puis M ≤ N). Si le bit de signe est nul, il ne doit pas affecter la valeur résultante. Si le bit de signe est un, la valeur doit être modifiée dans l'un des façons suivantes:
- la valeur correspondante avec le bit de signe 0 est annulée (signe et grandeur);
- le bit de signe a la valeur - (2M) (complément de deux);
- le bit de signe a la valeur- (2M-1) (le complément de ceux).
Section 6.2.6.2. est également très important de relier la représentation de la valeur des types entiers signés avec la représentation de la valeur de ses frères et sœurs non signés.
Cela signifie que, soit l'ensemble [-(2^n - 1), (2^n - 1)]
ou [-2^n, (2^n - 1)]
, où n
est généralement, 15 ou 31.
Opérations sur un entier signé types de
Maintenant, pour la deuxième chose: les opérations sur les types entiers signés, qui aboutissent à une valeur qui n'est pas dans la plage [INT_MIN, INT_MAX]
, le comportement est indéfini. Ceci est explicitement mandaté en C++ par le paragraphe 5/4:
Si lors de l'évaluation d'une expression, le résultat n'est pas défini mathématiquement ou n'est pas dans la plage de valeurs représentables pour son type, le comportement est indéfini.
Pour C, 6.5 / 5 offre un passage très similaire:
Si une condition exceptionnelle se produit lors de l'évaluation d'une expression (c'est-à-dire si le résultat n'est pas défini mathématiquement ou n'est pas dans la plage de valeurs représentables pour son type), le comportement n'est pas défini.
Alors, que se passe-t-il si la valeur de INT_MIN
est inférieure au négatif de INT_MAX
(par exemple -32768 et 32767 respectivement)? Le calcul de -(INT_MIN)
sera indéfini, comme INT_MAX + 1
.
Nous devons donc éviter de calculer une valeur qui ne l'est pas dans la plage de [INT_MIN, INT_MAX]
. Heureusement, INT_MAX + INT_MIN
est toujours dans cette plage, car INT_MAX
est une valeur strictement positive et INT_MIN
une valeur strictement négative. D'où INT_MIN < INT_MAX + INT_MIN < INT_MAX
.
Maintenant, nous pouvons vérifier, si, INT_MAX + INT_MIN
est égal à, inférieur ou supérieur à 0.
`INT_MAX + INT_MIN` | value of -INT_MIN | value of -INT_MAX
------------------------------------------------------------------
< 0 | undefined | -INT_MAX
= 0 | INT_MAX = -INT_MIN | -INT_MAX = INT_MIN
> 0 | cannot occur according to 6.2.6.2. of the C standard
Par conséquent, pour déterminer le minimum de INT_MAX
et -INT_MIN
(au sens mathématique), le code suivant est suffisant:
if ( INT_MAX + INT_MIN == 0 )
{
return INT_MAX; // or -INT_MIN, it doesn't matter
}
else if ( INT_MAX + INT_MIN < 0 )
{
return INT_MAX; // INT_MAX is smaller, -INT_MIN cannot be represented.
}
else // ( INT_MAX + INT_MIN > 0 )
{
return -INT_MIN; // -INT_MIN is actually smaller than INT_MAX, may not occur in a conforming implementation.
}
, Ou, pour simplifier:
return (INT_MAX + INT_MIN <= 0) ? INT_MAX : -INT_MIN;
Les valeurs d'un opérateur ternaire ne seront évaluées que si nécessaire. Par conséquent, -INT_MIN
est soit laissé non évalué (donc ne peut pas produire UB), soit est une valeur bien définie.
Ou, si vous voulez une affirmation:
assert(INT_MAX + INT_MIN <= 0);
return INT_MAX;
Ou, si vous le voulez au moment de la compilation:
static_assert(INT_MAX + INT_MIN <= 0, "non-conforming implementation");
return INT_MAX;
Obtenir les opérations entières correctement (c'est-à-dire si l'exactitude compte)
Si vous êtes intéressé par l'arithmétique entière sécurisée, jetez un oeil à mon implémentation des opérations entières sécurisées . Si vous voulez voir les modèles (plutôt que cette longue sortie de texte) sur quelles opérations échouent et qui réussissent, choisissez cette démo .
Selon l'architecture, il peut y avoir d'autres options pour assurer l'exactitude, telles que l'option de gcc -ftrapv
.
INT_MAX + INT_MIN < 0 ? INT_MAX : -INT_MIN
édité pour ajouter une explication: Bien sûr, la difficulté est que -INT_MIN
ou abs(INT_MIN)
seront indéfinis si -INT_MIN
est trop grand pour tenir dans un int
. Nous avons donc besoin d'un moyen de vérifier si c'est le cas. La condition INT_MAX + INT_MIN < 0
teste si -INT_MIN
est supérieure à INT_MAX
. Si c'est le cas, alors INT_MAX
est la plus petite des deux valeurs absolues. Si non, alors INT_MAX
est la plus grande des deux valeurs absolues, et -INT_MIN
est la réponse correcte.
En C99 et au-dessus, INT_MAX
.
Quot la spécification:
Pour les types entiers signés, les bits de la représentation de l'objet doivent être divisés en trois groupes: bits de valeur, bits de remplissage et bits de signe. Il n'y a pas besoin de bits de rembourrage; char signé ne doit pas avoir de bits de remplissage. Il doit y avoir exactement un signe bit. Chaque bit de la valeur des bits ont la même valeur que le même bit dans l'objet représentation du type non signé correspondant (s'il y a M bits de valeur dans le type et N dans le type non signé, puis M ≤ N). Si le signe peu est zéro, il ne doit pas affecter la valeur qui en résulte. Si le bit de signe est un, la valeur doit être modifiée dans l'un des façons suivantes:
- la valeur correspondante avec le bit de signe 0 est annulée (signe et grandeur);
- le bit de signe a la valeur- (2^M) (complément de deux);
- le bit de signe a la valeur- (2^m - 1) (le complément des uns).
(Section 6.2.6.2 de http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)
-INT_MAX
est représentable en tant que int
dans tous les dialectes C et c++, pour autant que je sache. Par conséquent:
-INT_MAX <= INT_MIN ? -INT_MIN : INT_MAX
Sur la plupart des systèmes, abs (INT_MIN) n'est pas défini. Par exemple, sur les machines 32 bits typiques, INT_MAX = 2^31 - 1, INT_MIN = - 2^31 et abs (INT_MIN) ne peuvent pas être 2^31.
abs(INT_MIN)
invoquera un comportement indéfini. La norme dit
7.22.6.1 L'abs
, labs
et llabs
fonctions:
Le
abs
,labs
, etllabs
fonctions de calculer la valeur absolue d'un entierj
. Si le résultat ne peut pas être représenté, le comportement est indéfini.
Essayez ceci à la place:
Convertir INT_MIN
en unsignrd int
. Les nombres Since-ve ne peuvent pas être représentés comme unsigned int
, INT_MAX
sera converti en UINT_MAX + 1 + INT_MIN
.
#include <stdio.h>
#include <stdlib.h>
unsigned min(unsigned a, unsigned b)
{
return a < b ? a : b;
}
int main(void)
{
printf("%u\n", min(INT_MAX, INT_MIN));
}