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.

30
demandé sur Nathaniel Ford 2015-04-22 23:42:37

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.

22
répondu stefan 2015-04-23 13:30:50
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.

10
répondu Brian 2015-04-22 20:52:59

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)

7
répondu Jeremy Roman 2015-04-22 20:56:24

-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
1
répondu Gilles 2015-04-22 20:52:07

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.

0
répondu gnasher729 2015-04-22 20:45: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, et llabs fonctions de calculer la valeur absolue d'un entier j. 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));
}  
0
répondu haccks 2015-04-23 05:06:16