C 问题:off_t(和其他有符号整数类型)最小值和最大值
我偶尔会遇到一种整数类型(例如 POSIX 有符号整数类型 off_t
),如果有一个用于其最小值和最大值的宏会很有帮助,但我不知道如何制作一个是真正的便携式。
对于无符号整数类型,我一直认为这很简单。 0
为最小值,~0
为最大值。此后我读到了几个不同的 SO 线程,它们建议使用 -1
而不是 ~0
以实现可移植性。这里有一个有趣的话题,有一些争议:
c++ - 使用安全吗 - 1 将所有位设置为真? - Stack Overflow
然而,即使在阅读了这个问题之后,我仍然感到困惑。另外,我正在寻找同时兼容 C89 和 C99 的东西,所以我不知道相同的方法是否适用。假设我有一个 uint_whatever_t
类型。我不能先转换为 0,然后按位求补吗?这样可以吗?:
#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )
有符号整数类型看起来是一个更难破解的难题。我已经看到了几种不同的可能解决方案,但只有 一个 似乎是可移植的。要么是这样,要么是不正确的。我在谷歌搜索 OFF_T_MAX 和 OFF_T_MIN 时发现了它。感谢克里斯蒂安·比埃尔:
#define MAX_INT_VAL_STEP(t) \
((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1 < 1)))
#define MAX_INT_VAL(t) \
((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t))
#define MIN_INT_VAL(t) \
((t) -MAX_INT_VAL(t) - 1)
[...]
#define OFF_T_MAX MAX_INT_VAL(off_t)
我在 C89 中找不到有关不同允许类型的有符号整数表示的任何内容,但 C99 在 §J.3.5 中有关于整数可移植性问题的注释:
有符号整数类型是否使用符号和大小、二进制表示 补语,还是一个人的补语,以及非凡的价值是否是一个陷阱 表示或普通值(6.2.6.2)。
这似乎意味着只能使用列出的三个有符号数字表示形式。含义是否正确,上面的宏是否与所有三种表示形式兼容?
Other thoughts:
It seems that the function-like macro
MAX_INT_VAL_STEP()
would give an incorrect result if there were padding bits. I wonder if there is any way around this.通读 维基百科上的有符号数字表示,我发现对于所有三个有符号整数表示,任何有符号整数类型的 MAX 为:
符号位关闭,所有值位打开(全部三个)
它的 MIN 可以是:
符号位打开,所有值位打开(符号和幅度)
符号位打开,所有值位关闭(一个/两个补码)
我想我可以通过这样做来测试符号和幅度:
#define OFF_T_MIN ( ( ( (off_t)1 | ( ~ (off_t) -1 ) ) != (off_t)1 ) ? /* sign and magnitude minimum value here */ : /* ones and twos complement minimum value here */ )
然后,由于符号和幅度是符号位打开,并且所有值位打开不会是 off_t 的最小值情况是 ~ (off_t) 0
?对于一/二补码最小值,我需要某种方法来关闭所有值位,但保留符号位。在不知道值位数的情况下不知道如何做到这一点。另外,符号位是否保证始终比最高有效值位高一位?
谢谢,如果这篇文章太长,请告诉我
编辑于 2010 年 12 月 29 日下午 5 点(美国东部时间):
正如下面 ephemient 回答的那样,为了获取无符号类型最大值,(unsigned type)-1
比 ~0
甚至 ~(unsigned type)0 更正确
。据我所知,当您使用 -1 时,它与 0-1 相同,这总是会导致无符号类型中的最大值。
此外,因为可以确定无符号类型的最大值,所以可以确定无符号类型中有多少个值位。感谢 Hallvard B. Furuseth 在回复 关于 comp.lang.c 的问题
/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
+ (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))
IMAX_BITS(INT_MAX) 计算 int 中的位数,IMAX_BITS((unsigned_type)-1) 计算 unsigned_type 中的位数。无论如何,直到有人实现 4 GB 整数:-)
然而,我的问题的核心仍然没有得到解答:如何通过宏确定有符号类型的最小值和最大值。我还在研究这个。也许答案是没有答案。
如果您没有在 StackOverflow 上查看此问题,则在大多数情况下您无法看到建议的答案,直到它们被接受为止。建议在 StackOverflow 上查看此问题。
I occasionally will come across an integer type (e.g. POSIX signed integer type off_t
) where it would be helpful to have a macro for its minimum and maximum values, but I don't know how to make one that is truly portable.
For unsigned integer types I had always thought this was simple. 0
for the minimum and ~0
for the maximum. I have since read of several different SO threads which suggest using -1
instead of ~0
for portability. An interesting thread with some contention is here:
c++ - Is it safe to use -1 to set all bits to true? - Stack Overflow
However even after reading about this issue I'm still confused. Also, I'm looking for something both C89 and C99 compliant so I don't know if the same methods apply. Say I had a type of uint_whatever_t
. Couldn't I just cast to 0 first and then bitwise complement? Would this be ok?:
#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )
Signed integer types look like they'll be a tougher nut to crack. I've seen several different possible solutions but only one appears to be portable. Either that or it's incorrect. I found it while googling for an OFF_T_MAX and OFF_T_MIN. Credit to Christian Biere:
#define MAX_INT_VAL_STEP(t) \
((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1 < 1)))
#define MAX_INT_VAL(t) \
((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t))
#define MIN_INT_VAL(t) \
((t) -MAX_INT_VAL(t) - 1)
[...]
#define OFF_T_MAX MAX_INT_VAL(off_t)
I couldn't find anything regarding the different allowable types of signed integer representations in C89, but C99 has notes for integer portability issues in §J.3.5:
Whether signed integer types are represented using sign and magnitude, two’s
complement, or ones’ complement, and whether the extraordinary value is a trap
representation or an ordinary value (6.2.6.2).
That would seem to imply that only those three listed signed number representations can be used. Is the implication correct, and are the macros above compatible with all three representations?
Other thoughts:
It seems that the function-like macro
MAX_INT_VAL_STEP()
would give an incorrect result if there were padding bits. I wonder if there is any way around this.Reading through signed number representations on Wikipedia it occurs to me that for all three signed integer representations any signed integer type's MAX would be:
sign bit off, all value bits on (all three)
And its MIN would be either:
sign bit on, all value bits on (sign and magnitude)
sign bit on, all value bits off (ones/twos complement)
I think I could test for sign and magnitude by doing this:
#define OFF_T_MIN ( ( ( (off_t)1 | ( ~ (off_t) -1 ) ) != (off_t)1 ) ? /* sign and magnitude minimum value here */ : /* ones and twos complement minimum value here */ )
Then as sign and magnitude is sign bit on and all value bits on wouldn't the minimum for off_t in that case be ~ (off_t) 0
? And for ones/twos complement minimum I would need some way to turn all the value bits off but leave the sign bit on. No idea how to do this without knowing the number of value bits. Also is the sign bit guaranteed to always be one more significant than the most significant value bit?
Thanks and please let me know if this is too long a post
EDIT 12/29/2010 5PM EST:
As answered below by ephemient to get the unsigned type max value, (unsigned type)-1
is more correct than ~0
or even ~(unsigned type)0
. From what I can gather when you use -1 it is just the same as 0-1 which will always lead to the maximum value in an unsigned type.
Also, because the maximum value of an unsigned type can be determined it is possible to determine how many value bits are in an unsigned type. Credit to Hallvard B. Furuseth for his IMAX_BITS() function-like macro that he posted in reply to a question on comp.lang.c
/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
+ (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))
IMAX_BITS(INT_MAX) computes the number of bits in an int, and IMAX_BITS((unsigned_type)-1) computes the number of bits in an unsigned_type. Until someone implements 4-gigabyte integers, anyway:-)
The heart of my question however remains unanswered: how to determine the minimum and maximum values of a signed type via macro. I'm still looking into this. Maybe the answer is there is no answer.
If you are not viewing this question on StackOverflow in most cases you cannot see the proposed answers until they are accepted. It is suggested to view this question on StackOverflow.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
我相信我终于解决了这个问题,但该解决方案仅在
配置
时可用,而不是在编译时或运行时可用,所以它仍然不知道。如下:这个想法来自 6.7.2.1 第 3 段:
如果这能带来任何在编译时解决问题的想法,我将非常高兴。
I believe I have finally solved this problem, but the solution is only available at
configure
-time, not compile-time or runtime, so it's still not idea. Here it is:The idea comes from 6.7.2.1 paragraph 3:
I would be quite pleased if this leads to any ideas for solving the problem at compile-time.
令人惊讶的是,C 在算术运算之前将类型提升到
int
,结果至少为int
大小。 (类似的奇怪之处包括'a'
字符文字的类型为int
,而不是char
。)因此
#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )
不一定没问题。Surprisingly, C promotes types up to
int
before arithmetic operations, with results being at leastint
sized. (Similarly oddities include'a'
character literal having typeint
, notchar
.)So
#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )
isn't necessarily okay.有符号最大值:
假设您的系统使用二进制补码,有符号最小值应该是:
这些应该是完全可移植的,除了 long long 在技术上是 C89 中的编译器扩展。这也避免了有符号整数上溢/下溢的未定义行为。
Signed max:
Assuming your system uses two's complement, the signed min should be:
These should be totally portable, except that long long is technically a compiler extension in C89. This also avoids the undefined behavior of over/underflowing a signed integer.
对于符号数值表示,这相当简单(无论如何,对于至少与 int 一样宽的类型):
不幸的是,符号数值表示在地面上相当薄弱;)
For sign-magnitude representations, it's fairly easy (for types at least as wide as
int
, anyway):Unfortunately, sign-magnitude representations are rather thin on the ground ;)
仅快速回答:
#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )
对我来说看起来不错,-1 的偏好是uint_whatever_t = -1;
比更简洁code>uint_whatever_t = ~(uint_whatever_t)0;
(CHAR_BIT * sizeof(t))
看起来并不严格符合我的要求。您对填充位的看法是正确的,因此该值可能比类型的宽度大得多,除非 Posix 关于off_t
另有说明。相比之下,C99 中的固定宽度整数类型不得具有填充位,因此对于
intN_t
,您可以使用大小来推断宽度。它们也保证是补码。是的。 6.2.6.2/2 列出了符号位的三种允许的含义,以及三种允许的带符号数字表示形式。
它间接要求比值位更重要,事实上(再次参见 6.2.6.2/2)“作为值位的每个位应具有与
相应无符号类型的对象表示中的相同位”。因此值位必须是从最低有效位开始的连续范围。
但是,您不能仅可移植地设置符号位。请阅读 6.2.6.2/3 和 / 4,关于负零,请注意,即使实现原则上使用具有它们的表示,它也不必支持它们,并且在符号+幅度实现上没有保证生成负零的方法。 。
[编辑:哦,我读错了,你只需要在排除符号+幅度之后生成该值,所以
说实话,如果我听起来有点麻木 Posix 定义了一个整数类型,但没有为它们提供限制,我可能会采用旧的“移植标头”方法,将可能在任何地方都可以工作的东西放在标头中,并记录下来。与他们通常必须做的事情相比,他们可能会在任何奇怪的实现上编译代码之前检查它,他们会很高兴地接受这一点。
Quick answers only:
#define UINT_WHATEVER_T_MAX ( ~ (uint_whatever_t) 0 )
looks OK to me, the preference for -1 is thatuint_whatever_t = -1;
is more concise thanuint_whatever_t = ~(uint_whatever_t)0;
(CHAR_BIT * sizeof(t))
looks not strictly conforming to me. You're right about padding bits, so this value might be considerably more than the width of the type unless Posix says otherwise aboutoff_t
.In contrast, the fixed-width integer types in C99 must not have padding bits, so for
intN_t
you're on firmer ground using the size to deduce the width. They're also guaranteed two's complement.Yes. 6.2.6.2/2 lists the three permissible meanings of the sign bit, and hence the three permissible signed number representations.
It's indirectly required to be more significant than the value bits, by the fact (6.2.6.2/2 again) that "Each bit that is a value bit shall have the same value as
the same bit in the object representation of the corresponding unsigned type". So the value bits must be a contiguous range starting at the least significant.
However, you can't portably set just the sign bit. Read 6.2.6.2/3 and /4, about negative zeros, and note that even if the implementation uses a representation that has them in principle, it doesn't have to support them, and there's no guaranteed way of generating one. On a sign+magnitude implementation, the thing you want is a negative zero.
[Edit: oh, I misread, you only need to generate that value after you've ruled out sign+magnitude, so you could still be OK.
To be honest, it sounds a bit numpty to me if Posix has defined an integer type and not provided limits for it. Boo to them. I'd probably go with the old, "porting header" approach, where you put the thing that probably works everywhere in a header, and document that someone should probably check it before compiling the code on any freakish implementations. Compared with what they normally have to do to get anybody's code to work, they'll happily live with that.]
从技术上讲,它不是一个宏,但实际上,以下内容应始终折叠为
off_t
或任何有符号类型的常量最小值,无论符号表示如何。虽然我不确定什么不需要两个人的恭维,如果有的话。POSIX 要求
off_t
使用有符号整数类型,因此 C99 有符号精确宽度值应该足够了。有些平台实际上定义了OFF_T_MIN
(OSX),但不幸的是 POSIX 并不需要它。同样可用于获得最大值。
不过,可以使用 autoconf 或 cmake 将其转换为宏。
It's technically not a macro, but in practice the following should always be folded into a constant minimum for
off_t
, or any signed type, regardless of sign representation. Although I'm not sure what doesn't use two's compliment, if anything.POSIX requires a signed integer type for
off_t
, so the C99 signed exact width values should be sufficient. Some platforms actually defineOFF_T_MIN
(OSX), but POSIX unfortunately doesn't require it.The same is usable to obtain the maximum value.
This could be turned into a macro using
autoconf
orcmake
though.从 C11 开始,您可以使用 _Generic 来查找基础类型。在此之前,带有
__typeof
和__builtin_types_known_p
的 __builtin_choose_expr 是相当可移植的。如果您不想使用其中任何一个,您可以根据其大小和符号来猜测类型。
(如果您想在没有限制的情况下执行此操作,请查看我的答案 https://stackoverflow.com/a/53470064 /1084774)。
Since C11 you can use _Generic to find the underlying type. Before then
__builtin_choose_expr
with__typeof
and__builtin_types_compatible_p
was fairly portable.If you don't want to use any of those, you could guess the type based on its size and signedness.
(If you want to do it even without limits.h, please check out my answer at https://stackoverflow.com/a/53470064/1084774).
TL;DR:使用下面列出的头文件,然后使用
TYPE_MAX(someType)
获取 someType 使用的类型的最大值。您可以使用 C11 中引入的
_Generic
表达式。您需要编译器支持的每个基本整数类型(例如
char
、long
等)的列表,以及每个类型定义的整数(最有可能的是uint8_t< /code>,
off_t
) 将被视为基础类型。下面是一个示例头文件:
假设
off_t
是用typedef int64_t off_t
定义的,而int64_t
是用typedef long long int64_t< 定义的/code>,那么C编译器将处理
off_t foo; TYPE_MAX(foo)
与long long foo 相同; TYPE_MAX(foo)
并将选择值为LLONG_MAX
的选项,为您提供最大值。如果系统具有此头文件中未列出的其他本机类型,请创建一个预处理器变量,该变量在其他系统上为空,但在使用该类型的系统上进行编译时包含该本机类型的值。然后将此预处理器变量添加到列表中,类似于此处使用 uintmax_t 完成的操作。
TL;DR: Use the header file listed below and then use
TYPE_MAX(someType)
to get the maximum of the type used by someType.You can use the
_Generic
expression, introduced with C11.You need a list of every base integer type (like
char
,long
, ...) your compiler supports, every typedefed integer (most likely things likeuint8_t
,off_t
) will be treated like the underlying type.Here is an example header file:
Let's say
off_t
is defined withtypedef int64_t off_t
andint64_t
is defined withtypedef long long int64_t
, then the C compiler will treatoff_t foo; TYPE_MAX(foo)
the same aslong long foo; TYPE_MAX(foo)
and will chose the option with the valueLLONG_MAX
, giving you the maximum.If a system has another native type not listed in this header file, make a preprocessor variable that is empty on other systems but contains the values for that native type when you compile on a system with that type. And then add this preprocessor variable to the list, similar to how it was done here with
uintmax_t
.我使用以下模式来解决问题(假设没有填充位):
number_of_bits_in_type
派生为CHAR_BIT * sizeof (type)
,如其他答案中所示。我们基本上将 1 位“推”到位,同时避免符号位。
您可以看到这是如何工作的。假设宽度为16位。然后我们将 1 左移 16 - 2 = 14,生成位模式
0100000000000000
。我们小心翼翼地避免将1
移入符号位。接下来,我们从中减去 1,得到0011111111111111
。看看这是怎么回事?我们将其左移 1,得到0111111111111110
,再次避免符号位。最后加1,得到0111111111111111
,这是最高的有符号16位值。如果你在博物馆工作,那里有这样的东西,那么这在补码和符号数值机器上应该可以很好地工作。如果你有填充位,它就不起作用。为此,您可能所能做的就是#ifdef,或者切换到编译器和预处理器之外的替代配置机制。
因此,对于
off_t
:I have used the following pattern to solve the problem (assuming there are no padding bits):
The
number_of_bits_in_type
is derived asCHAR_BIT * sizeof (type)
as in the other answers.We basically "nudge" the 1 bits into place, while avoiding the sign bit.
You can see how this works. Suppose that the width is 16 bits. Then we take 1 and shift it left by 16 - 2 = 14, producing the bit pattern
0100000000000000
. We carefully avoided shifting a1
into the sign bit. Next, we subtract 1 from this, obtaining0011111111111111
. See where this is going? We shift this left by 1 obtaining0111111111111110
, again avoiding the sign bit. Finally we add 1, obtaining0111111111111111
, which is the highest signed 16 bit value.This should work fine on one's complement and sign-magnitude machines, if you work in a museum where they have such things. It doesn't work if you have padding bits. For that, probably all you can do is
#ifdef
, or switch to alternative configuration mechanisms outside of the compiler and preprocessor.So, for
off_t
:您可能想查看 limit.h (在 C99 中添加),该标头提供了应设置为匹配编译器范围的宏。 (它与编译器附带的标准库一起提供,或者第三方标准库替换负责使其正确)
You probably want to look at limits.h (added in C99) this header provides macros that should be set to match the compiler's ranges. (either it is provided along with the Standard library that came with the compiler, or a third party standard library replacement is responsible for getting it right)