Quelle est la fonction pour déterminer les valeurs minimale et maximale possibles de la valeur des types de données (c'est-à-dire int, char.etc) en C?
Vous voudrez utiliser limits.h
qui fournit les constantes suivantes (selon la référence liée):
CHAR_BIT = number of bits in a char
SCHAR_MIN = minimum value for a signed char
SCHAR_MAX = maximum value for a signed char
UCHAR_MAX = maximum value for an unsigned char
CHAR_MIN = minimum value for a char
CHAR_MAX = maximum value for a char
MB_LEN_MAX = maximum multibyte length of a character accross locales
SHRT_MIN = minimum value for a short
SHRT_MAX = maximum value for a short
USHRT_MAX = maximum value for an unsigned short
INT_MIN = minimum value for an int
INT_MAX = maximum value for an int
UINT_MAX = maximum value for an unsigned int
LONG_MIN = minimum value for a long
LONG_MAX = maximum value for a long
ULONG_MAX = maximum value for an unsigned long
LLONG_MIN = minimum value for a long long
LLONG_MAX = maximum value for a long long
ULLONG_MAX = maximum value for an unsigned long long
Où U*_MIN
est omis pour des raisons évidentes (tout type non signé a une valeur minimale de 0).
De même float.h
fournit des limites pour les types float
et double
:
-FLT_MAX = most negative value of a float
FLT_MAX = max value of a float
-DBL_MAX = most negative value of a double
DBL_MAX = max value of a double
-LDBL_MAX = most negative value of a long double
LDBL_MAX = max value of a long double
Vous devriez lire attentivement l'article sur floats.h
, bien que float
et double
puissent contenir les valeurs minimale et maximale prescrites, mais la précision avec laquelle chaque type peut représenter des données peut ne pas correspondre à ce que vous essayez de stocker. En particulier, il est difficile de stocker des nombres exceptionnellement grands avec des fractions extrêmement petites. Donc, float.h
fournit un certain nombre d'autres constantes qui vous aident à déterminer si une float
ou une double
peut, en fait, représenter un nombre particulier.
"Mais glyphe", je vous entends demander, "et si je dois déterminer la valeur maximale pour un type opaque dont le maximum pourrait éventuellement changer?" Vous pouvez continuer: "Que se passe-t-il s'il s'agit d'un typedef dans une bibliothèque que je ne contrôle pas?"
Je suis heureux que vous ayez posé la question, car je viens de passer quelques heures à élaborer une solution (que j’ai ensuite dû jeter, car elle n’a pas résolu mon problème).
Vous pouvez utiliser cette macro pratique maxof
pour déterminer la taille de tout type entier valide.
#define issigned(t) (((t)(-1)) < ((t) 0))
#define umaxof(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | \
(0xFULL << ((sizeof(t) * 8ULL) - 4ULL)))
#define smaxof(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | \
(0x7ULL << ((sizeof(t) * 8ULL) - 4ULL)))
#define maxof(t) ((unsigned long long) (issigned(t) ? smaxof(t) : umaxof(t)))
Vous pouvez l'utiliser comme ceci:
int main(int argc, char** argv) {
printf("schar: %llx uchar: %llx\n", maxof(char), maxof(unsigned char));
printf("sshort: %llx ushort: %llx\n", maxof(short), maxof(unsigned short));
printf("sint: %llx uint: %llx\n", maxof(int), maxof(unsigned int));
printf("slong: %llx ulong: %llx\n", maxof(long), maxof(unsigned long));
printf("slong long: %llx ulong long: %llx\n",
maxof(long long), maxof(unsigned long long));
return 0;
}
Si vous le souhaitez, vous pouvez placer un "(t)" sur le dessus de ces macros afin qu'elles vous donnent un résultat du type que vous demandez, et vous n'avez pas à faire de casting pour éviter les avertissements.
Maximum valeur de tout type intégral non signé: (~(t)0)
Maximum valeur de n'importe quel type intégral signé: Si vous avez une variante non signée de type t, ((t)((~(unsigned t)0)>>1))
vous donnera le résultat le plus rapide dont vous avez besoin (voir l'exemple dans le code source du noyau Linux mentionné ci-dessous) . Sinon, vous devriez probablement utiliser (~(1ULL<<(sizeof(t)*CHAR_BIT-1)))
.
Minimum valeur de tout type intégral signé: Vous devez connaître la représentation du numéro signé de votre machine. La plupart des machines utilisent un complément à 2, et -(~(1ULL<<(sizeof(t)*CHAR_BIT-1)))-1
fonctionnera pour vous.
Pour détecter si votre machine utilise le complément à 2, indiquez si (~(t)0U)
et (t)(-1)
représentent la même chose. Donc, combiné avec ci-dessus:
((~(t)0U) == (t)(-1) ? -(~(1ULL<<(sizeof(t)*CHAR_BIT-1)))-1 :
-(~(1ULL<<(sizeof(t)*CHAR_BIT-1))))
vous donnera la valeur minimale de tout type intégral signé. (En fait, il existe d'autres représentations de cela si vous connaissez la représentation du complément à 2. Par exemple, (t)(1ULL<<(sizeof(t)*CHAR_BIT-1))
devrait être équivalent à (t)(-(~(1ULL<<(sizeof(t)*CHAR_BIT-1)))-1)
.)
Par exemple: (~ (size_t) 0) vous donne la valeur maximale de size_t. (Et devinez quoi, voici comment SIZE_MAX est # défini dans le code source du noyau Linux .)
Un caveat cependant: toutes ces expressions utilisent le transtypage et ne fonctionnent donc pas dans les conditions de préprocesseur (#if ... #Elif ... #endif and like).
#include<stdio.h>
int main(void)
{
printf("Minimum Signed Char %d\n",-(char)((unsigned char) ~0 >> 1) - 1);
printf("Maximum Signed Char %d\n",(char) ((unsigned char) ~0 >> 1));
printf("Minimum Signed Short %d\n",-(short)((unsigned short)~0 >>1) -1);
printf("Maximum Signed Short %d\n",(short)((unsigned short)~0 >> 1));
printf("Minimum Signed Int %d\n",-(int)((unsigned int)~0 >> 1) -1);
printf("Maximum Signed Int %d\n",(int)((unsigned int)~0 >> 1));
printf("Minimum Signed Long %ld\n",-(long)((unsigned long)~0 >>1) -1);
printf("Maximum signed Long %ld\n",(long)((unsigned long)~0 >> 1));
/* Unsigned Maximum Values */
printf("Maximum Unsigned Char %d\n",(unsigned char)~0);
printf("Maximum Unsigned Short %d\n",(unsigned short)~0);
printf("Maximum Unsigned Int %u\n",(unsigned int)~0);
printf("Maximum Unsigned Long %lu\n",(unsigned long)~0);
return 0;
}
Le fichier d'en-tête limits.h
définit les macros qui se développent à différentes limites et paramètres des types d'entiers standard.
J'ai écrit des macros qui renvoient les min et max de n'importe quel type, peu importe la signature:
#define MAX_OF(type) \
(((type)(~0LLU) > (type)((1LLU<<((sizeof(type)<<3)-1))-1LLU)) ? (long long unsigned int)(type)(~0LLU) : (long long unsigned int)(type)((1LLU<<((sizeof(type)<<3)-1))-1LLU))
#define MIN_OF(type) \
(((type)(1LLU<<((sizeof(type)<<3)-1)) < (type)1) ? (long long int)((~0LLU)-((1LLU<<((sizeof(type)<<3)-1))-1LLU)) : 0LL)
Exemple de code:
#include <stdio.h>
#include <sys/types.h>
#include <inttypes.h>
#define MAX_OF(type) \
(((type)(~0LLU) > (type)((1LLU<<((sizeof(type)<<3)-1))-1LLU)) ? (long long unsigned int)(type)(~0LLU) : (long long unsigned int)(type)((1LLU<<((sizeof(type)<<3)-1))-1LLU))
#define MIN_OF(type) \
(((type)(1LLU<<((sizeof(type)<<3)-1)) < (type)1) ? (long long int)((~0LLU)-((1LLU<<((sizeof(type)<<3)-1))-1LLU)) : 0LL)
int main(void)
{
printf("uint32_t = %lld..%llu\n", MIN_OF(uint32_t), MAX_OF(uint32_t));
printf("int32_t = %lld..%llu\n", MIN_OF(int32_t), MAX_OF(int32_t));
printf("uint64_t = %lld..%llu\n", MIN_OF(uint64_t), MAX_OF(uint64_t));
printf("int64_t = %lld..%llu\n", MIN_OF(int64_t), MAX_OF(int64_t));
printf("size_t = %lld..%llu\n", MIN_OF(size_t), MAX_OF(size_t));
printf("ssize_t = %lld..%llu\n", MIN_OF(ssize_t), MAX_OF(ssize_t));
printf("pid_t = %lld..%llu\n", MIN_OF(pid_t), MAX_OF(pid_t));
printf("time_t = %lld..%llu\n", MIN_OF(time_t), MAX_OF(time_t));
printf("intptr_t = %lld..%llu\n", MIN_OF(intptr_t), MAX_OF(intptr_t));
printf("unsigned char = %lld..%llu\n", MIN_OF(unsigned char), MAX_OF(unsigned char));
printf("char = %lld..%llu\n", MIN_OF(char), MAX_OF(char));
printf("uint8_t = %lld..%llu\n", MIN_OF(uint8_t), MAX_OF(uint8_t));
printf("int8_t = %lld..%llu\n", MIN_OF(int8_t), MAX_OF(int8_t));
printf("uint16_t = %lld..%llu\n", MIN_OF(uint16_t), MAX_OF(uint16_t));
printf("int16_t = %lld..%llu\n", MIN_OF(int16_t), MAX_OF(int16_t));
printf("int = %lld..%llu\n", MIN_OF(int), MAX_OF(int));
printf("long int = %lld..%llu\n", MIN_OF(long int), MAX_OF(long int));
printf("long long int = %lld..%llu\n", MIN_OF(long long int), MAX_OF(long long int));
printf("off_t = %lld..%llu\n", MIN_OF(off_t), MAX_OF(off_t));
return 0;
}
Pour obtenir la valeur maximale d'un type entier non signé t
dont la largeur est au moins égale à celle de unsigned int
(sinon, on rencontre des problèmes avec les promotions sur les entiers): ~(t) 0
. Si on veut aussi supporter des types plus courts, on peut ajouter une autre distribution: (t) ~(t) 0
.
Si le type entier t
est signé, en supposant qu'il n'y a pas de bits de remplissage, on peut utiliser:
((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1)
L'avantage de cette formule est qu'elle ne repose pas sur une version non signée de t
(ou un type plus grand), qui peut être inconnue ou non disponible (même uintmax_t
peut ne pas être suffisant avec des extensions non standard). Exemple avec 6 bits (impossible en pratique, juste pour la lisibilité):
010000 (t) 1 << (sizeof(t) * CHAR_BIT - 2)
001111 - 1
011110 * 2
011111 + 1
Dans le complément à deux, la valeur minimale est l'opposé de la valeur maximale, moins 1 (dans les autres représentations de nombre entier autorisées par la norme ISO C, c'est exactement l'inverse de la valeur maximale).
Remarque: Pour détecter la signature afin de décider de la version à utiliser: (t) -1 < 0
fonctionnera avec toute représentation d’entier, en donnant 1 (vrai) pour les types entiers signés et 0 (faux) pour les types entiers non signés. Ainsi, on peut utiliser:
(t) -1 < 0 ? ((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1) : (t) ~(t) 0
Les valeurs MIN et MAX de tout type de données entier peuvent être calculées sans utiliser les fonctions de bibliothèque décrites ci-dessous et la même logique peut être appliquée à d'autres types d'entiers courts, int et longs.
printf("Signed Char : MIN -> %d & Max -> %d\n", ~(char)((unsigned char)~0>>1), (char)((unsigned char)~0 >> 1));
printf("Unsigned Char : MIN -> %u & Max -> %u\n", (unsigned char)0, (unsigned char)(~0));