Re: [问题] 不用if-else, for, while, do-while取绝

楼主: TobyH4cker (Toby (我要当好人))   2016-04-07 00:54:29
※ 引述《CaliforCat (加州猫)》之铭言:
: 对一个整数取绝对值
: 如果不用到if-else, for, while, do-while
: 可以使用什么方法
: 我想到的都是会用到上列的限制...
: 请前辈指教 谢谢
这题假设照原本的限制
再加上不能用任何relational operator或logical operator
但可以使用bitwise operator
则abs(int)有哪些做法
有放推文的表示概念上是一样我再重新用C实作
※法1
#include <stdio.h>
int main()
{
unsigned int num;
while (scanf("%d", &num) != EOF)
{
num ^= (num >> (sizeof(num) * 8 - 1)) *
((num & (~num + 1) * (num | (~num + 1)));
printf("%d\n", num);
}
return 0;
}
说明:
这只是纯粹0与1的游戏,当正数时XOR的右算子是0
sizeof(num) * 8 - 1用来找MSB
必用unsigned是因为在右移时不让它补位
※法2来了
推 CaptainH: a*((a>0)*2-1) 04/07 04:32
#include <stdio.h>
int main()
{
unsigned int num;
while (scanf("%d", &num) != EOF)
{
num *= 1 - 2 * (num >> (sizeof(num) * 8 - 1));
printf("%d\n", num);
}
return 0;
}
x86组语如下:
mov eax,[num]
shr eax,1F
add eax,eax
mov edx,00000001
sub edx,eax
mov eax,[num]
imul eax,edx
mov [num],eax
说明:
原来这么根本不必拐弯抹角,用数学关系就能解了zzz果然还是不够
原理就是当num是正数则乘以1,负数则乘以-1,找出用MSB生出1或-1的方法就行了
正数MSB是0,负数MSB是1,于是1-MSB*2就会是我们要的东西
※法3
推 chubiei: int myabs(int i) 04/07 02:09
→ chubiei: { 04/07 02:09
→ chubiei: double d = i; unsigned long long *l = &d; 04/07 02:10
→ chubiei: *l <<= 1; *l >>= 1; 04/07 02:10
→ chubiei: return d; 04/07 02:10
→ chubiei: } 04/07 02:10
推 chubiei: 上面的假设是int为32 bits, double和unsigned long long 04/07 02:17
→ chubiei: 都是64 bits 04/07 02:17
#include <stdio.h>
#include <stdint.h>
int main()
{
union db2 {
int64_t i;
double d;
};
int32_t num;
db2 tmp;
while (scanf("%d", &num) != EOF)
{
tmp.d = (double)num;
tmp.i &= ~(1ull << (sizeof(double) * 8 - 1));
num = (int32_t)tmp.d;
printf("%d\n", num);
}
return 0;
}
说明:
有点邪门,先把int转成double,而由于IEEE 754只用MSB控制正负号
于是把负数MSB干掉后这个double就变成正数,再转回int就是绝对值了
至于用union是因为double没有bitwise运算子,必须把它视为整数型别才能做
※法4
#include <stdio.h>
#include <stdlib.h> // int abs(int);
int main()
{
int num;
while (scanf("%d", &num) != EOF)
printf("%d\n", abs(num));
return 0;
}
发现abs只用了三条x86指令就完成了<(_ _)>
然而这是编译器最佳化的结果
当你写(num > 0) ? num : -num就有可能会最佳化成以下这样
mov eax,[num]
cdq
xor eax,edx
sub eax,edx
mov [num],eax
说明:
由于cdq在当eax是正数时edx是0,负数则是-1,
正数时和0做XOR此时正数会维持不变,负数时和-1做XOR变成1的补数也就是NOT结果
又在将负数转正数取2的补数时只要NOT后再加一
第三条指令刚好就是减去-1
正数处理时减0维持不变
参考 https://www.strchr.com/optimized_abs_function
※法5
get absolute value without using abs function nor if statement
StackOverflow http://stackoverflow.com/questions/9772348/
底下有一个( n >> 31 | 1 ) * n我竟然没想到要利用补位特性...
#include <stdio.h>
int main()
{
int num;
while (scanf("%d", &num) != EOF)
{
printf("%d\n", (num >> (sizeof(int) * 4 - 1) | 1 ) * num);
}
return 0;
}
太神啦
※法6
开大直接写shellcode
Intel Syntax for VC++
int abs(int a)
{
__asm
{
mov eax, dword ptr[a]
cdq
xor eax, edx
sub eax, edx
}
}
AT&T Syntax for GCC
int abs(int a)
{
asm ("mov %0, %%eax" :: "r" (a) : "%eax");
asm ("cdq");
asm ("xor %%edx, %%eax" ::: "%eax");
asm ("sub %%edx, %%eax" ::: "%eax");
}
作者: Frozenmouse (*冰之鼠*)   2016-04-07 01:11:00
https://ideone.com/lvKnkB sprintf+sscanf硬干XD
楼主: TobyH4cker (Toby (我要当好人))   2016-04-07 01:16:00
XD 在解一些无脑文字游戏我也都不喜欢用脑直接硬干
作者: chubiei (:))   2016-04-07 02:09:00
int myabs(int i){double d = i; unsigned long long *l = &d;*l <<= 1; *l >>= 1;return d;}
楼主: TobyH4cker (Toby (我要当好人))   2016-04-07 08:35:00
这个太邪门啦
作者: stupid0319 (征女友)   2016-04-07 11:32:00
i = i & 0x7FFFFFFF; 这样可以吗?
楼主: TobyH4cker (Toby (我要当好人))   2016-04-07 11:39:00
不能,负数是以2的补数表示,把整数的MSB抹掉会变别的
作者: stupid0319 (征女友)   2016-04-07 11:58:00
i = i * ((((i>>31) ^ 1) << 1 )- 1); 这样呢
作者: bibo9901 (function(){})()   2016-04-07 12:03:00
i = i * (( i>0 ) *2 - 1) 一样啊你还假设int一定要32bit
作者: stupid0319 (征女友)   2016-04-07 12:06:00
对吼,不知道有没有CPU的指令集比较快
楼主: TobyH4cker (Toby (我要当好人))   2016-04-07 12:17:00
就是abs 那样已经是最精简的了
作者: tuyutd0505 (Huang Jason)   2016-04-07 17:48:00
推 这篇一堆超狂方法XD
作者: ronin728 (浪人)   2016-04-07 23:04:00
各种被玩弄的二进制数字与位元运算子XD
作者: Frozenmouse (*冰之鼠*)   2016-04-07 23:41:00
wwwww
作者: wtchen (没有存在感的人)   2016-04-08 01:28:00
以后管版务要来设一个脑筋急转弯专区....
作者: Schottky (顺风相送)   2016-04-08 03:05:00
我怎觉得像猎奇专区...
作者: soheadsome (师大狗鼻哥)   2016-04-08 11:59:00
算法心得 短码写手表示:
作者: LiloHuang (十年一刻)   2016-04-08 12:40:00
v = (v ^ (v >> 31)) - (v >> 31); // int32_t v;更多 bithacks 技巧以及解说 https://goo.gl/2m2qzH
作者: BlazarArc (Midnight Sun)   2016-04-08 13:28:00
^看起来有点像在放阿哩固(误
作者: suhorng ( )   2016-04-08 19:42:00
cdq;xor;sub 这个好像是经典作法XD
作者: Caesar08 (Caesar)   2016-04-10 17:35:00
这篇不错

Links booklink

Contact Us: admin [ a t ] ucptt.com