typedefed 有符号类型的最大值

Maximum value of typedefed signed type

我正在阅读 John Regehr's blog on how he gives his students an assignment about saturating arithmetic. The interesting part is that the code has to compile as-is while using typedefs to specify different integer types, see the following excerpt of the full header:

typedef signed int mysint;
//typedef signed long int mysint;

mysint sat_signed_add (mysint, mysint);

mysint sat_signed_sub (mysint, mysint);

相应的无符号版本很容易实现(虽然我实际上不确定填充位是否也不会造成问题),但我实际上不知道如何获得最大值(或最小值) C 中未知有符号类型的值,不使用 MAX_MIN_ 的宏或导致未定义的行为。

我是不是遗漏了什么或者作业有问题(或者更可能是我遗漏了他给学生的一些重要信息)?

如果您假设八位 chars 和二进制补码表示(在所有现代硬件上都是合理的,除了一些嵌入式 DSP 东西),那么您只需要形成一个无符号整数(使用 uintmax_t 以确保它足够大),底部位为 sizeof(mysint)*8 - 1 1,然后将其转换为 mysint。最小值取最大值减一

如果您不想假设这些事情,那么它仍然是可能的,但您需要通过 limits.h 进行更多挖掘以补偿字符的大小和符号表示。

这是在不使用任何库

的情况下使用 typedef 获取特定类型集的 MAX 值的建议
typedef signed int mysint;
mysint size; // will give the size of the type
size=sizeof(mysint)*(mysint)8-(mysint)1; // as it is signed so a bit
                                        // will be deleted for sign bit
mysint max=1;//start with first bit
while(--size)
{
   mysint temp;
   temp=(max<<(mysint)1)|(mysint)1;// set all bit to 1
   max=temp;
}
/// max will contain the max value of the type mysint

在不了解更多信息的情况下,我没有找到一种方法来确定 C 中未知有符号整数类型的最大和最小可表示值。 (在 C++ 中,您有 std::numeric_limits 可用,所以它很简单。)

无符号整数类型的最大可表示值是 (myuint)(-1)。这保证独立于填充位工作,因为 (§ 6.3.1.3/1-2):

When a value with integer type is converted to another integer type… if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

因此,要将 -1 转换为无符号类型,您需要将它的最大可表示值加一,并且该结果必须是最大可表示值。 (标准明确表示 "repeatedly adding or subtracting" 是数学意义上的。)

现在,如果您知道有符号类型中的填充位数与无符号类型中的填充位数相同[但见下文],您可以从最大的可表示符号值中计算出最大的可表示符号值可表示的无符号值:

(mysint)( (myuint)(-1) / (myuint)2 )

不幸的是,这不足以计算最小可表示的有符号值,因为标准允许最小值比最大值的负数(2 的补码表示)小一或正好是最大值的负数(1 的补码表示) -补充或 sign/magnitude 表示)。

而且,标准实际上并不保证有符号类型的填充位数与无符号类型的填充位数相同。它只保证有符号类型中的 value 位数不大于无符号类型中的值位数。特别是,无符号类型比相应的有符号类型多一个填充位是合法的,在这种情况下,它们将具有相同数量的值位,并且最大可表示值将相同。 [注意:值位既不是填充位也不是符号位。]

简而言之,如果您知道(例如被告知)架构是 2 的补码并且相应的有符号和无符号类型具有相同数量的填充位,那么您当然可以计算有符号的最小值和最大值:

myuint max_myuint = (myuint)(-1);
mysint max_mysint = (mysint)(max_myuint / (my_uint)2);
mysint min_mysint = (-max_mysint) - (mysint)1;

最后,将超出范围的无符号整数转换为有符号整数是不是未定义的行为,尽管大多数其他有符号溢出是。如 §6.3.1.3/3 所示,转换是 实现定义的 行为:

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

实施定义的行为需要由实施记录。所以,假设我们知道实现是 gcc。然后我们可以检查 gcc documentation,我们将在 "C Implementation-defined behaviour" 部分中阅读以下内容:

  • 有符号整数类型是否使用符号和 幅度,二进制补码,或一个的补码,以及是否 非凡价值是陷阱表示或普通价值 (C99 6.2.6.2).

    GCC 只支持二进制补码整数类型,所有位 模式是普通值。

  • 将整数转换为整数的结果或引发的信号 当值不能用一个表示时的有符号整数类型 该类型的对象(C90 6.2.1.2、C99 6.3.1.3)。

    转换成宽度为N的类型,取模减去值 2^N 在类型范围内;没有发出信号。

知道有符号整数是 2s 补码并且无符号到有符号的转换不会陷入陷阱,但会产生预期的低位模式,我们可以找到以开头的任何有符号类型的最大值和最小值最宽无符号类型的最大可表示值,uintmax_t:

uintmax_t umax = (uintmax_t)(-1);
while ( (mysint)(umax) < 0 ) umax >>= 1;
mysint max_mysint = (mysint)(umax);
mysint min_mysint = (-max_mysint) - (mysint)1;

如果不做出假设或调用实现定义的(不一定 un 定义的)行为,我看不出有任何方法可以做到这一点。但是,如果您假设 mysintuintmax_t 的表示中没有填充位,那么您可以像这样计算最大值:

mysint mysint_max = (mysint)
    ((~(uintmax_t)0) >> (1 + CHAR_BITS * (sizeof(uintmax_t) - sizeof(mysint))));

然后最小值是 -mysint_max(sign/magnitude 或一个的补码)或 -mysint_max - 1(两个的补码),但确定哪个有点棘手。你不知道先验 哪个位是符号位,并且可能存在因表示样式不同而不同的陷阱表示。您还必须小心评估表达式,因为可能 "the usual arithmetic conversions" 将值转换为一种类型,其表示具有与您尝试探测的属性不同的属性。

不过,您可以通过计算 -1mysint 表示的按位求反来区分负值表示的类型。对于二进制补码,mysint 结果的值为 0,对于二进制补码,结果为 1,对于 sign/magnitude,结果为 mysint_max - 1.

如果您假设所有有符号整数类型都具有相同类型的负值表示形式,那么您可以使用默认 int 文字上的普通表达式简单地执行此类测试。但是,您不需要做出这样的假设。相反,您可以通过 union:

直接对类型表示的位模式执行操作
union mysint_bits {
    mysint i;
    unsigned char bits[sizeof(mysint)];
} msib;

int counter = 0;

for (msib.i = -1; counter < sizeof(mysint); counter += 1) {
    msib.bits[counter] = ~msib.bits[counter];
}

只要初始假设成立(mysint 类型的表示中没有填充位),msib.i 就必须是所需结果的有效表示。

我想不管负数表示如何这都应该有效

// MSB is 1 and rests are zero is minimum number in both 2's and 1's
// compliments representations.
mysint min =  (1 << (sizeof(mysint) * 8 - 1));
mysint max = ~x;