Hàm để xác định giá trị tối thiểu và tối đa có thể có của các kiểu dữ liệu (tức là int, char.etc) trong C là gì?
Hàm để xác định giá trị tối thiểu và tối đa có thể có của các kiểu dữ liệu (tức là int, char.etc) trong C là gì?
Câu trả lời:
Bạn sẽ muốn sử dụng limits.h
cung cấp các hằng số sau (theo tham chiếu được liên kết):
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
Trường hợp U*_MIN
bị bỏ qua vì những lý do rõ ràng (bất kỳ kiểu không dấu nào có giá trị nhỏ nhất là 0).
Tương tự, float.h
cung cấp các giới hạn cho float
và double
các loại:
-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
Bạn nên đọc kỹ bài viết về floats.h
mặc dù float
và double
có thể giữ các giá trị tối thiểu và tối đa được quy định nhưng độ chính xác mà mỗi loại có thể đại diện cho dữ liệu có thể không khớp với những gì bạn đang cố gắng lưu trữ. Đặc biệt, rất khó để lưu trữ các số đặc biệt lớn với các phân số cực nhỏ kèm theo. Vì vậy, float.h
cung cấp một số hằng số khác giúp bạn xác định xem trên thực tế, một float
hoặc một double
có thể đại diện cho một số cụ thể.
SIZE_MAX
(kích thước tối đa của a size_t
) là một kích thước hữu ích khác.
"Nhưng glyph", tôi nghe thấy bạn hỏi, "điều gì sẽ xảy ra nếu tôi phải xác định giá trị lớn nhất cho một loại không trong suốt mà giá trị tối đa cuối cùng có thể thay đổi?" Bạn có thể tiếp tục: "Điều gì sẽ xảy ra nếu đó là một typedef trong thư viện mà tôi không kiểm soát?"
Tôi rất vui vì bạn đã hỏi, vì tôi vừa dành vài giờ để tìm ra giải pháp (mà sau đó tôi phải vứt bỏ, vì nó không giải quyết được vấn đề thực tế của tôi).
Bạn có thể sử dụng maxof
macro tiện dụng này để xác định kích thước của bất kỳ kiểu số nguyên hợp lệ nào.
#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)))
Bạn có thể sử dụng nó như vậy:
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;
}
Nếu muốn, bạn có thể ném '(t)' lên phía trước các macro đó để chúng cung cấp cho bạn kết quả về loại mà bạn đang hỏi và bạn không phải thực hiện truyền để tránh cảnh báo.
~((t) 0)
hoạt động cho tối đa không dấu? (nó không, nhưng tôi không chắc tại sao được nêu ra).
Giá trị lớn nhất của bất kỳ loại tích phân không dấu nào :
((t)~(t)0)
// Biểu thức chung sẽ hoạt động trong hầu hết mọi trường hợp.
(~(t)0)
// Nếu bạn biết loại của bạn t
có kích thước bằng hoặc lớn hơn
unsigned int
. (Diễn viên này buộc phải thăng hạng.)
((t)~0U)
// Nếu bạn biết loại của bạn t
có kích thước nhỏ hơn
unsigned int
. (Loại giảm hạng truyền này sau khi unsigned int
biểu thức -type ~0U
được đánh giá.)
Tối đa giá trị của bất kỳ ký kiểu không thể thiếu:
Nếu bạn có kiểu biến thể không dấu t
, ((t)(((unsigned t)~(unsigned t)0)>>1))
sẽ cung cấp cho bạn kết quả nhanh nhất mà bạn cần.
Nếu không, hãy sử dụng cái này (nhờ @ vinc17 gợi ý): (((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)
Tối thiểu giá trị của bất kỳ ký kiểu không thể thiếu:
Bạn phải biết đại diện số đã ký trên máy của bạn. Hầu hết các máy sử dụng phần bổ sung của 2 và vì vậy -(((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-1
sẽ phù hợp với bạn.
Để phát hiện xem máy của bạn có sử dụng phần bổ sung của 2 hay không, hãy phát hiện xem có (~(t)0U)
và (t)(-1)
đại diện cho cùng một thứ hay không.
Vì vậy, kết hợp với trên:
(-(((1ULL<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-(((~(t)0U)==(t)(-1)))
sẽ cung cấp cho bạn giá trị nhỏ nhất của bất kỳ loại tích phân có dấu nào.
Ví dụ: Giá trị tối đa của size_t
(hay còn gọi là SIZE_MAX
macro) có thể được xác định là (~(size_t)0)
. Mã nguồn nhân Linux xác định SIZE_MAX
macro theo cách này.
Tuy nhiên, có một lưu ý : Tất cả các biểu thức này đều sử dụng kiểu ép kiểu hoặc sizeof
toán tử và vì vậy không có điều nào trong số này sẽ hoạt động trong các điều kiện tiền xử lý ( #if
... #elif
... #endif
và tương tự).
(Đã cập nhật câu trả lời cho các đề xuất bổ sung từ @chux và @ vinc17. Cảm ơn cả hai.)
unsigned long long
có thể không phải là kiểu số nguyên lớn nhất; uintmax_t
sẽ tốt hơn nhưng thậm chí không phải lúc nào cũng là kiểu số nguyên lớn nhất trong thực tế (xem GCC's __int128
). Tôi đã đưa ra một giải pháp di động hơn cho tối đa các loại có chữ ký trong câu trả lời của mình. Sau đó, mức tối thiểu có thể được suy ra từ nó như bạn đã làm. Liên quan đến các điều kiện của bộ tiền xử lý, sizeof
cũng không thể được sử dụng vì tiền xử lý xảy ra trước khi phân tích ngữ nghĩa, tức là bộ tiền xử lý không có khái niệm về loại.
xxx_MAX == Uxxx_MAX
được cho phép trong C cũng như xxx_MAX < Uxxx_MAX/2
. Điều đặc biệt là đó xxx_MAX <= Uxxx_MAX
và cả hai loại đều có cùng kích thước.
char
là loại tiêu chuẩn C duy nhất có thể đáp ứng xxx_MAX == Uxxx_MAX
, char
có thể có dấu hoặc không dấu tùy thuộc vào việc triển khai. Và đối với xxx_MAX < Uxxx_MAX/2
trường hợp này, rất có thể nó được gây ra bởi số học phần bù không phải của 2 (nếu không sẽ không có ý nghĩa đối với việc triển khai).
~((t) 0)
không hoạt động khi (t)0
hẹp hơn an int
.
~((t) 0)
. Đối với các trường hợp xxx_MAX == Uxxx_MAX
và xxx_MAX < Uxxx_MAX/2
trường hợp, từ những gì tôi đã đọc trong tiêu chuẩn C99, vâng chúng được phép.
#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;
}
Tôi đã viết một số macro trả về giá trị tối thiểu và tối đa của bất kỳ loại nào, bất kể độ ký:
#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)
Mã ví dụ:
#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;
}
Tệp tiêu đề limits.h
xác định các macro mở rộng đến các giới hạn và tham số khác nhau của các kiểu số nguyên chuẩn.
Để có được giá trị lớn nhất của một kiểu dữ liệu integer unsigned t
có chiều rộng tối thiểu là một trong những unsigned int
(nếu không ai được vấn đề với chương trình khuyến mãi số nguyên): ~(t) 0
. Nếu ai muốn cũng hỗ trợ các kiểu ngắn hơn, người ta có thể thêm dàn diễn viên khác: (t) ~(t) 0
.
Nếu kiểu số nguyên t
được ký, giả sử rằng không có bit đệm, người ta có thể sử dụng:
((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1)
Ưu điểm của công thức này là nó không dựa trên một số phiên bản không dấu của t
(hoặc một loại lớn hơn), có thể không xác định hoặc không có sẵn (thậm chí uintmax_t
có thể không đủ với các phần mở rộng không chuẩn). Ví dụ với 6 bit (không thể thực hiện được trong thực tế, chỉ để dễ đọc):
010000 (t) 1 << (sizeof(t) * CHAR_BIT - 2)
001111 - 1
011110 * 2
011111 + 1
Trong phần bù của hai, giá trị nhỏ nhất đối lập với giá trị lớn nhất, trừ đi 1 (trong các đại diện số nguyên khác được tiêu chuẩn ISO C cho phép, giá trị này chỉ ngược lại với giá trị lớn nhất).
Lưu ý: Để phát hiện tính có dấu để quyết định sử dụng phiên bản nào: (t) -1 < 0
sẽ hoạt động với bất kỳ biểu diễn số nguyên nào, đưa ra 1 (đúng) cho các kiểu số nguyên có dấu và 0 (sai) cho các kiểu số nguyên không dấu. Vì vậy, người ta có thể sử dụng:
(t) -1 < 0 ? ((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1) : (t) ~(t) 0
sizeof(t) * CHAR_BIT - 1
không thể biểu diễn trong kiểu có dấu t
. Bạn đang giả định hành vi "bao bọc" của dịch chuyển trái, không phải là tiêu chuẩn (và có thể không thành công với việc tối ưu hóa trình biên dịch) và thậm chí sẽ không có ý nghĩa trong các biểu diễn số nguyên khác với phần bổ sung của hai (như tiêu chuẩn C cho phép).
Giá trị MIN và MAX của bất kỳ kiểu dữ liệu số nguyên nào có thể được tính toán mà không cần sử dụng bất kỳ hàm thư viện nào như bên dưới và logic tương tự có thể được áp dụng cho các kiểu số nguyên khác là short, int và 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));