我正在阅读John Regehr's blog关于他如何给他的学生一个关于饱和算术的任务。有趣的是,代码必须按原样编译,同时使用typedef指定不同的整数类型,请参阅以下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_
的宏或导致未定义的行为。
我在这里遗漏了什么,或者作业是否存在缺陷(或者更有可能是我错过了他给学生们的一些重要信息)?
答案 0 :(得分:6)
在没有做出假设或调用实现定义(不一定是 un 定义)行为的情况下,我没有看到任何方法。但是,如果假设mysint
或uintmax_t
的表示中没有填充位,那么您可以像这样计算最大值:
mysint mysint_max = (mysint)
((~(uintmax_t)0) >> (1 + CHAR_BITS * (sizeof(uintmax_t) - sizeof(mysint))));
最小值可以是-mysint_max
(符号/幅度或者补码)或-mysint_max - 1
(补码),但确定哪个是有点棘手的。您不知道先验哪个位是符号位,并且可能存在不同表示形式的陷阱表示。你也必须小心评估表达式,因为通常的算术转换的可能性是"将值转换为其表示具有与您尝试探测的属性不同的属性的类型。
然而,您可以通过计算mysint
-1
表示的按位否定来区分负值表示的类型。对于两个补码,结果的mysint
值为0
,对于那些'补充它是1
,对于符号/幅度,它是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
必须是所需结果的有效表示。
答案 1 :(得分:4)
我没有看到在C中确定未知有符号整数类型的最大和最小可表示值的方法,而不知道更多内容。 (在C ++中,你有std::numeric_limits
可用,所以它很简单。)
无符号整数类型的最大可表示值为(myuint)(-1)
。这保证独立于填充位工作,因为(§6.3.1.3/1-2):
当具有整数类型的值转换为另一个整数类型时...如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值为止。在新类型的范围内。
因此,要将-1
转换为无符号类型,请为其添加一个多于最大可表示值的值,并且该结果必须是最大可表示值。 (标准清楚地表明“重复加或减”的含义是数学的。)
现在,如果您知道有符号类型中的填充位数与无符号类型中的填充位数相同[但请参见下文],则可以从最大可表示无符号计算最大可表示的有符号值。值:
(mysint)( (myuint)(-1) / (myuint)2 )
不幸的是,这还不足以计算最小可表示的有符号值,因为标准允许最小值比最大值(2的补码表示)的负值小一个或者最大值的负值(1的补码)或符号/幅度表示。)
此外,该标准实际上并不保证有符号类型中的填充位数与无符号类型中的填充位数相同。它保证的是,有符号类型中值位的数量不大于无符号类型中的值位数。特别是,无符号类型比相应的有符号类型多一个填充位是合法的,在这种情况下,它们将具有相同数量的值位,并且最大可表示值将是相同的。 [注意:值位既不是填充位也不是符号位。]
简而言之,如果您知道(例如被告知)该体系结构是2的补码并且相应的有符号和无符号类型具有相同数量的填充位,那么您当然可以计算有符号的最小值和最大值: / p>
myuint max_myuint = (myuint)(-1);
mysint max_mysint = (mysint)(max_myuint / (my_uint)2);
mysint min_mysint = (-max_mysint) - (mysint)1;
最后,将超出范围的无符号整数转换为有符号整数是 not 未定义的行为,尽管大多数其他有符号溢出都是。如§6.3.1.3/ 3所示,转换是实现定义的行为:
否则,新类型已签名且值无法在其中表示;无论是 结果是实现定义的或引发实现定义的信号。
实现需要记录实现定义的行为。所以,假设我们知道实现是gcc
。然后我们可以在“C实现定义的行为”一节中检查gcc documentation,我们将阅读以下内容:
是否使用sign和表示有符号整数类型 幅度,二的补码,或一个补码,以及是否 非常值是陷阱表示或普通值 (C99 6.2.6.2)。
GCC仅支持两个补码整数类型,并且所有位都支持 模式是普通的价值观。
将整数转换为a的结果或产生的信号 当值无法在一个表示时,有符号整数类型 该类型的对象(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;
答案 2 :(得分:1)
这是建议在不使用任何库的情况下使用MAX
获取特定类型集的typedef
值
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
答案 3 :(得分:0)
如果你假设8位char
s和2的补码表示(在所有现代硬件上都是合理的,除了一些嵌入式DSP的东西),那么你只需要形成一个无符号的整数(使用uintmax_t
以确保它足够大)在底部位中使用sizeof(mysint)*8 - 1
1,然后将其强制转换为mysint
。对于最小值,否定最大值并减去一。
如果你不想承担这些事情,那么它仍然可能,但是你需要做更多的挖掘限制来补偿字符的大小和标志代表。
答案 4 :(得分:0)
我想无论负数表示
,这都应该有效// 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;