abs()函数对比
在C\C++的<math.h
>和<cmath
>中均有abs的实现,而今天心血来潮,另外手动实现了两个abs()函数,用来做一下对比,一种是通过关系运算符判断正负求解,一种是通过位运算求解,仔细看哦,很值得回味的测试……(PS:为了增大区别,每个函数的运算次数为MAXN
次)。
#include <cstdio>
#include <iostream>
#include <ctime>
#include <cmath>
using namespace std;
const int MAXN = 1000000;
int abs_1(int n)
{
if (n < 0)
{
return -n;
}
else
{
return n;
}
}
int abs_2(int n)
{
return (n ^ (n >> 31)) - (n >> 31);
}
int main()
{
int a, b = 0;
while (cin >> a)
{
// 系统abs()函数测试
long long start = clock();
for (int i = 0; i < MAXN; i++)
{
b = abs(a);
}
long long end = clock();
cout << "abs() : " << a << ' ' << b << ' ' << end - start << '\n';
// 判断正负abs()函数实现
start = clock();
for (int i = 0; i < MAXN; i++)
{
b = abs_1(a);
}
end = clock();
cout << "abs_1() : " << a << ' ' << b << ' ' << end - start << '\n';
// 位运算abs()函数实现
start = clock();
for (int i = 0; i < MAXN; i++)
{
b = abs_2(a);
}
end = clock();
cout << "abs_2() : " << a << ' ' << b << ' ' << end - start << '\n';
}
return 0;
}
经过多次测试,发现时间差距还是挺明显的,当然对于一次abs()而言,差距可以视为没有吧,当然这么说不太严谨。
根据数据我们不难发现,abs_1()
和abs_2()
对于正负数的处理时间差别还是挺大的,但是令人好奇的是<cmath
>中abs()
对于正负数的处理时间却几乎相等,至于为什么,就麻烦好奇心比我强的人去挖掘源码吧,然后告诉我怎么实现的。
接着呢,我想abs_1()
不用分析了,有脑子的都看得懂,而对于位运算搞定的abs_2()
来说,可读性真的不算高,对于不经常和位运算打交道的人而言,挺绕的。首先我们定义的是4个字节的32位2进制有符号整形,也就是int
型,原谅我把一个整形形容的那么复杂,好了,言归正传,因为有符号,所以我们知道第32位是符号位,所以n>>31
最后得到的就是符号位的值,如果是负数,结果为-1,反之则为0。那么如果n为正数,n^0-0=n
,而如果为负数,n^(-1)
的结果也就是|n|-1
,所以最后再-(-1)
也就是n的绝对值了,我想这里不用过多解释,直接用一组数据写写画画就知道了,真的不知道,那就麻烦你去看看位运算了,因为今天关键不是要搞位运算,所以,我还是不会解释,毕竟我是一个懒人。
由于闲着也是数星星、数羊,于是我又进行了一个测试,测试<cmath
>与<math.h
>中abs()
的区别,上面的数据已经有了前者的测试结果,所以鉴于我懒,我只再给出<math.h
>的测试数据。
虽然和前边的数据对比感觉不明显,但是还是有些许区别的,我认为<math.h
>的要比<cmath
>的abs()
稳定性差些。大概这就是为什么C++推荐使用的头文件是<cmath
>,而<math.h
>只是为了兼容。
当然,以上所有测试数据都是基于我的机子,我的测试样本,也许有的地方不够准确,那么我只能将其归咎于我的失责,但是一贯喜欢找客观原因的我会告诉你:测试样本不够大,测试样本分布不够均匀。但是,懒惰的我,从不会因为知道这些就去重测,因为,现在都三点了,我还有线代没看/(ㄒoㄒ)/~~,看过线代后还要看会儿代码大全……如果非要说我不够严谨,没有治学精神,那么我也不得不承认,毕竟这种放大了一百万倍后产生的差别还如此甚微的,真的没有太大锱铢必较的价值。
测试了这么大会儿,虽然测试数据上有些不够严谨,但是我们基本上可以得出,位运算是最牛逼的!!!对于某种特定的需求,这里的确可以产生可见的优化。
最后,跟上一句,倒数第三段是在浪费你们时间,我只是给自己找一个随性一些的借口,当然,这一段也是废话。