如何解决如何在没有*或-运算符的情况下取反C中的正整数?
您好,我正在尝试为我的实验室编写一个函数,该函数要求我用受限运算符取正数。允许的运算符为:!
~
&
^
|
+
<<
和>>
。
我最初的方法是使用加法符x
将x + x
加倍,然后从x
中减去该值以得到相反的值,但是由于操作员的限制而受到限制。我对C还是很陌生,并且想学习,如果有人可以为我提供执行此操作背后的想法,我将不胜感激。
解决方法
这可能不是您的讲师想要看到的,但是它可以工作并且确实做到了,与~
运算符不同,它不依赖于实现(信号幅度,1补码或2补码)并且可以不会造成UB。我发布此解决方案是因为它满足要求,但又丑陋又缓慢,因为我认为任务很愚蠢(任务导致的功能只能在使用2补码的系统上运行,我敢打赌讲师会提出一个只能解决问题的解决方案在2补码系统上。
#include <limits.h>
//Returns the negative value of n,//n has to be positive. Negative values of n cause UB.
int makeNegative(int n)
{
int i=INT_MIN;
while(i+n) //continue till i+n is 0,no need for the < operator
{
i++;
}
return i;
}
如果您希望在n
为负数时不导致UB的代码,则可以检查n
的负值:
#include <limits.h>
int isNegative(int n)
{
int i=INT_MIN;
while(i)
{
i++;
if(n==INT_MAX) //when n reaches INT_MAX,it was positive
{ //we need to check for it before increment
return 0; //increment a int of value INT_MAX would cause UB
}
n++;
if(!n) //n is 0 means n was negative before
{
return 1;
}
}
return 0;
}
//Returns the negative value of n i n is positive
//returns n in any other case
int makeNegative(int n)
{
if(isNegative(n))
{
return n;
}
int i=INT_MIN;
while(i+n) //continue till i+n is 0,no need for the < operator
{
i++;
}
return i;
}
2-补码系统
正如其他人已经指出的那样,这是您的教练可能想要看到的:n=(~n)+1;
。这仅在使用2补码时有效,并且会导致INT_MIN
产生UB。
假设我们在变量1
中存储了数字n
。现在我们要取反n
。然后,我们必须反转变量,这意味着我们翻转每一位,然后添加+1
。这是一个int8_t
的示例。
Value | Bit pattern |
1 | 0b00000001 | n is set to 1
-2 | 0b11111110 | We inverted n
-1 | 0b11111111 | We added 1 to n
2补码系统的优点是,我们可以为值使用所有可能的位模式,并且对于大多数操作,我们可以对signed
和unsigned
变量使用相同的逻辑。对于无符号变量,0b11111111
的二进制值为0xFF==255
。 C标准要求无符号整数环绕。如果我们将0xFF
存储在uint8_t
变量中并添加1
,则结果将无法存储在uint8_t
中并回绕为0。意味着0b11111111+1==0
。如前所述,我们可以将值-1
存储在int8_t
中作为位模式0b11111111
。在这里我们也得到0b11111111+1==0 => -1+1==0
。
2-补码系统的一个特征是负值比正值多一个。 INT8_MIN<-INT8_MAX
。 INT8_MIN
具有位模式0b10000000
,如果将其反转,则会得到位模式0b01111111==INT8_MAX
。现在,当我们添加1
时,就会发生溢出,并且溢出会导致有符号变量的UB *。因此,对n=(~n)+1;
进行签名时,语句n
可能导致UB。对于n=-n
,n=n*-1
和n=n/-1
也是如此,因为当n具有我们无法取反的最低值时,它们全部失效。
*当您告诉编译器对符号变量使用环绕(gcc上的-fwrapv
)时,它将再次导致INT8_MIN
。
这是您可能希望产生的经典解决方案:
int negate_positive_number(int n) {
return ~n + 1;
}
此代码的问题是它仅适用于2的补码系统。这不是一个真正的问题,因为您今天可能不会遇到其他架构。
但是C标准支持带符号整数的其他表示形式:
- 符号/幅值,其中负值设置了相同的值位,但设置了符号位,
- 一个补数,其中负值的所有位都与正值的表示相反。
在这两个历史古怪的地方,0
有2种可能的表示形式,其中一种可能是陷阱值。
这是适用于所有3种架构的原始解决方案:2的补码,符号/幅度和1的补码系统:有点晦涩但更便携:
#include <limits.h>
int negate_positive_number(int n) {
return (n ^ INT_MIN ^ INT_MAX) + (1 & (INT_MIN + INT_MAX));
}
它使用+
,^
和&
以及<limits.h>
中的定义,可能是超限。我假设也允许使用括号。
2个补语
int minus(int x)
{
return ~x + 1;
}
测试:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。