Яка функція визначає мінімальне та максимальне можливе значення типів даних (тобто int, char.etc) у C?
Яка функція визначає мінімальне та максимальне можливе значення типів даних (тобто int, char.etc) у C?
Відповіді:
Ви захочете використовувати, limits.hщо надає такі константи (відповідно до зв’язаного посилання):
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
Де U*_MINпропущено із зрозумілих причин (будь-який тип без підпису має мінімальне значення 0).
Аналогічним чином float.hпередбачені обмеження для floatі 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
Ви повинні уважно прочитати статтю floats.h, хоча floatі doubleможете містити встановлені мінімальні та максимальні значення, але точність, з якою кожен тип може представляти дані, може не відповідати тому, що ви намагаєтеся зберегти. Зокрема, важко зберігати винятково великі числа з приєднаними надзвичайно малими фракціями. Отже, float.hнадається ряд інших констант, які допомагають вам визначити, чи a floatчи doublecan, насправді, представляють певне число.
SIZE_MAX(максимальний розмір a size_t) - ще один корисний.
"Але гліф", я чую, як ви запитуєте, "що, якщо мені доведеться визначити максимальне значення для непрозорого типу, максимум якого з часом може змінитися?" Ви можете продовжити: "Що робити, якщо це typedef у бібліотеці, яку я не контролюю?"
Я радий, що ви запитали, тому що я просто витратив пару годин, готуючи розчин (який потім мені довелося викинути, бо це не вирішило моєї фактичної проблеми).
Ви можете використовувати цей зручний maxofмакрос для визначення розміру будь-якого допустимого цілочисельного типу.
#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)))
Ви можете використовувати його так:
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;
}
Якщо ви хочете, ви можете кинути '(t)' на передню частину цих макросів, щоб вони дали вам результат типу, про який ви запитуєте, і вам не потрібно робити кастинг, щоб уникнути попереджень.
~((t) 0)працювати максимум без підпису? (це не так, але я ще не впевнений, чому).
Максимальне значення будь-якого непідписаного інтегрального типу:
((t)~(t)0) // Загальний вираз, який би працював майже за всіх обставин.
(~(t)0)// Якщо ви знаєте, що ваш тип tмає однаковий або більший розмір, ніж
unsigned int. (Цей привід змушує просувати тип.)
((t)~0U)// Якщо ви знаєте, що ваш тип tмає менший розмір, ніж
unsigned int. (Цей тип пониженого статусу після оцінки unsigned intвиразу -type ~0Uоцінюється.)
Максимальне значення будь-якого підписаного інтегрального типу:
Якщо у вас є непідписаний варіант типу t, ((t)(((unsigned t)~(unsigned t)0)>>1))це дасть вам найшвидший результат, який вам потрібен.
В іншому випадку використовуйте це (дякую @ vinc17 за пропозицію): (((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)
Мінімальне значення будь-якого підписаного інтегрального типу:
Ви повинні знати поданий номер машини з підписом. У більшості машин використовується доповнення 2, і це -(((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-1буде працювати для вас.
Щоб визначити, чи використовує ваша машина доповнення 2, визначте (~(t)0U)і (t)(-1)представляйте те саме.
Отже, у поєднанні з вищезазначеним:
(-(((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-(((~(t)0U)==(t)(-1)))
дасть вам мінімальне значення будь-якого підписаного інтегрального типу.
Як приклад: Максимальне значення size_t(воно ж SIZE_MAXмакрос) можна визначити як (~(size_t)0). Вихідний код ядра Linux визначає SIZE_MAXмакрос таким чином.
Однак одне застереження : усі ці вирази використовують або кастинг типу, або sizeofоператор, і тому жодне з них не буде працювати в умовних умовах попереднього процесора ( #if... #elif... #endifтощо).
(Відповідь оновлено за включення пропозицій від @chux та @ vinc17. Дякую вам обом.)
unsigned long longможе бути не найбільший цілочисельний тип; uintmax_tмає бути кращим, але навіть не завжди є найбільшим цілочисельним типом на практиці (див. GCC __int128). У своїй відповіді я дав більш портативне рішення для максимальної кількості підписаних типів. Тоді з нього можна було б вивести мінімум, як і ви. Що стосується умов попереднього процесора, sizeofїх також не можна використовувати, оскільки попередня обробка відбувається перед семантичним аналізом, тобто препроцесор не має поняття типів.
xxx_MAX == Uxxx_MAXце дозволено в C, а також xxx_MAX < Uxxx_MAX/2. Зазначено те, що xxx_MAX <= Uxxx_MAXобидва типи мають однаковий розмір.
charце єдиний тип стандарту C, який, можливо xxx_MAX == Uxxx_MAX, charможе задовольнити , оскільки може бути підписаним або непідписаним залежно від реалізації. І для xxx_MAX < Uxxx_MAX/2випадку це, найімовірніше, буде спричинено арифметикою доповнення не-2 (інакше це не матиме сенсу для реалізації).
~((t) 0)не працює, коли (t)0вужче, ніж int.
~((t) 0). Що стосується випадків xxx_MAX == Uxxx_MAXand xxx_MAX < Uxxx_MAX/2, з того, що я прочитав у стандарті C99, так, вони дозволені.
#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;
}
Я написав кілька макросів, які повертають мінімальний і максимальний значення будь-якого типу, незалежно від підпису:
#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)
Приклад коду:
#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;
}
Заголовний файл limits.hвизначає макроси, які розширюються до різних меж та параметрів стандартних цілочисельних типів.
Щоб отримати максимальне значення цілочисельного типу без підпису t , ширина якого по крайней мере, один з unsigned int( в іншому випадку кожен отримує проблеми з цілими заохоченнями): ~(t) 0. Якщо один хоче також підтримувати більш короткі тип, можна додати ще один кидок: (t) ~(t) 0.
Якщо цілочисельний тип tпідписаний, припускаючи, що немає бітів доповнення, можна використовувати:
((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1)
Перевага цієї формули полягає в тому, що вона не базується на якійсь непідписаній версії t (або більшого типу), яка може бути невідомою або недоступною (навіть uintmax_tможе бути недостатньою при нестандартних розширеннях). Приклад із 6 бітами (на практиці неможливо, лише для читабельності):
010000 (t) 1 << (sizeof(t) * CHAR_BIT - 2)
001111 - 1
011110 * 2
011111 + 1
У доповненні двох мінімальне значення протилежне максимальному значенню, мінус 1 (в інших цілочисельних поданнях, дозволених стандартом ISO C, це якраз протилежне максимальному значенню).
Примітка: Щоб виявити підпис, щоб вирішити, яку версію використовувати: (t) -1 < 0буде працювати з будь-яким цілочисельним представленням, даючи 1 (true) для підписаних цілих типів і 0 (false) для цілих беззнакових типів. Таким чином, можна використовувати:
(t) -1 < 0 ? ((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1) : (t) ~(t) 0
sizeof(t) * CHAR_BIT - 1неможливо представити у підписаному типі t. Ви припускаєте поведінку "загортання" лівого зсуву, яка не є стандартною (і може не вдатися при оптимізації компіляторів) і навіть не матиме сенсу в цілочисельних поданнях, відмінних від доповнення двох (як це дозволено стандартом С).
Значення MIN та MAX будь-якого цілочисельного типу даних можуть бути обчислені без використання будь-яких бібліотечних функцій, як показано нижче, і та сама логіка може бути застосована до інших цілочисельних типів short, int та long.
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));