例如,可以使用位运算符来实现乘法和除法
i*2 = i<<1
i*3 = (i<<1) + i;
i*10 = (i<<3) + (i<<1)
等等。
使用说(i<<3)+(i<<1)
乘以10 真的比i*10
直接使用要快吗?是否存在无法以这种方式相乘或除法的输入?
例如,可以使用位运算符来实现乘法和除法
i*2 = i<<1
i*3 = (i<<1) + i;
i*10 = (i<<3) + (i<<1)
等等。
使用说(i<<3)+(i<<1)
乘以10 真的比i*10
直接使用要快吗?是否存在无法以这种方式相乘或除法的输入?
Answers:
简短答案:不太可能。
长答案:您的编译器中包含一个优化器,该优化器知道如何以目标处理器体系结构的能力进行快速乘法。最好的选择是清楚地告诉编译器您的意图(即i * 2而不是i << 1),然后让它决定最快的汇编/机器码序列。处理器本身甚至可能已将乘法指令实现为一系列移位和微码加法运算。
最重要的是-不要花很多时间担心这个问题。如果您要转移,那就转移。如果要乘,就乘。做语义上最清晰的事情-您的同事稍后会感谢您。或者,更可能的是,如果以后否则诅咒您。
gcc -O3
return i*10
millis() >> 2
; 要求划分是否会太多?
i / 32
vs i >> 5
和i / 4
vs i >> 2
在gcc上测试了cortex-a9(没有硬件划分),并且得到的组件完全相同。我不喜欢先使用除法,但是它描述了我的意图,并且输出是相同的。
只是一个具体的测量点:很多年前,我对哈希算法的两个版本进行了基准测试:
unsigned
hash( char const* s )
{
unsigned h = 0;
while ( *s != '\0' ) {
h = 127 * h + (unsigned char)*s;
++ s;
}
return h;
}
和
unsigned
hash( char const* s )
{
unsigned h = 0;
while ( *s != '\0' ) {
h = (h << 7) - h + (unsigned char)*s;
++ s;
}
return h;
}
在我对其进行基准测试的每台机器上,第一台至少和第二台一样快。令人惊讶的是,有时速度更快(例如在Sun Sparc上)。当硬件不支持快速乘法(大多数都不支持快速乘法)时,编译器会将乘法转换为移位和加/减的适当组合。而且由于它知道最终目标,所以有时可以用比您明确地编写班次和加/减时更少的指令来做到。
请注意,这就像15年前。希望从那以后编译器只会变得更好,因此您可以指望编译器做正确的事情,可能比您做的更好。(此外,代码看起来如此“ C”的原因是因为它已经超过15年了。我std::string
今天显然会使用和迭代器。)
除了这里所有其他好的答案之外,让我指出在表示除法或乘法时不使用移位的另一个原因。我从未见过有人通过忘记乘法和加法的相对优先级来引入错误。当维护程序员忘记通过移位进行“乘法”在逻辑上是乘法,但在语法上却没有与乘法相同的优先级时,我已经看到了引入的错误。x * 2 + z
而且x << 1 + z
有很大的不同!
如果您正在处理数字,请使用算术运算符,例如+ - * / %
。如果您正在处理位数组,请使用位旋转运算符,例如& ^ | >>
。不要混在一起;一个既有点摇摆又有算术的表达式是一个等待发生的错误。
这取决于处理器和编译器。一些编译器已经以这种方式优化代码,而另一些则没有。因此,您需要每次检查需要以这种方式优化代码的时间。
除非您迫切需要优化,否则我不会为了保存汇编指令或处理器周期而对源代码进行加扰。
>>
运算符会比快,/
并且,如果有符号值可以为负数,则它在语义上也通常比较优越。如果需要一个x>>4
将产生的值,那将比清晰得多x < 0 ? -((-1-x)/16)-1 : x/16;
,而且我无法想象编译器如何将后一种表达式优化为更好的形式。
使用(i << 3)+(i << 1)乘以10真的比直接使用i * 10更快吗?
它可能会或可能不会在您的计算机上-如果您愿意,请衡量实际使用情况。
基准测试很难进行有意义的工作,但是我们可以看看一些事实。从http://www.penguin.cz/~literakl/intel/s.html#SAL和 http://www.penguin.cz/~literakl/intel/i.html#IMUL中,我们了解了x86时钟周期算术移位和乘法需要。假设我们坚持使用“ 486”(列出的最新寄存器),32位寄存器和立即数,IMUL占用13-42个周期,IDIV占用44。每个SAL占用2,并加1,所以即使其中的一些一起在表面上转移像赢家一样
这些天,随着核心i7:
(来自http://software.intel.com/zh-cn/forums/showthread.php?t=61481)
对于整数加法,等待时间为1个周期,对于整数乘法,为3个周期。您可以在http://www.intel.com/products/processor/manuals/上的“英特尔®64和IA-32架构优化参考手册”的附录C中找到延迟和吞吐量。
(摘自一些Intel blurb)
使用SSE,Core i7可以同时发出加法和乘法指令,从而使每个时钟周期的峰值速率达到8个浮点运算(FLOP)
这使您了解事情的进展。*
甚至在90年代就被认真对待的优化琐事-像位移与- 相比现在已经过时了。位移仍然更快,但是对于非2乘方/ div的功率,您进行所有移位并添加结果后,它会再次变慢。然后,更多的指令意味着更多的高速缓存错误,更多的流水线潜在问题,更多使用临时寄存器可能意味着更多地从堆栈中保存和恢复寄存器内容...它很快变得太复杂了,无法确切地量化所有影响,但是它们主要是负面的。
一般而言,您的问题被标记为C和C ++。作为第三代语言,它们专门用于隐藏基础CPU指令集的详细信息。为了满足其语言标准,即使基础硬件不支持乘法和移位运算(以及许多其他运算),它们也必须支持。在这种情况下,他们必须使用许多其他指令来综合所需的结果。同样,如果CPU缺少浮点运算并且没有FPU,它们必须为浮点运算提供软件支持。现代CPU都支持*
和<<
,因此这在理论上和历史上似乎都是荒谬的,但重要的是选择实现的自由是双向的:即使在一般情况下,CPU拥有实现源代码中所请求的操作的指令,编译器也可以自由选择选择它更喜欢的其他选项,因为这对于编译器所面对的特定情况更好。
示例(假设的汇编语言)
source literal approach optimised approach
#define N 0
int x; .word x xor registerA, registerA
x *= N; move x -> registerA
move x -> registerB
A = B * immediate(0)
store registerA -> x
...............do something more with x...............
诸如Exclusive或(xor
)之类的指令与源代码没有关系,但是对其进行异或运算会清除所有位,因此可以将其设置为0。隐含内存地址的源代码可能不需要使用任何代码。
只要计算机存在,这类黑客就一直被使用。在3GL的早期,为了确保开发人员的使用,编译器的输出必须满足现有的核心手动优化汇编语言开发人员的要求。社区认为所生成的代码不会变慢,更冗长或更糟。编译器迅速采用了许多重大优化措施-它们成为比任何单个汇编语言程序员都可能更好的集中存储方式,尽管他们总是有机会错过在特定情况下至关重要的特定优化措施-人们有时可以总结一下,摸索出更好的东西,而编译器则按照被告知的方式工作,直到有人将经验反馈给他们。
因此,即使在某些特定的硬件上转移和添加仍然更快,那么编译器编写者可能会在安全和有益的情况下准确地进行计算。
如果更改了硬件,则可以重新编译,并且它将查看目标CPU并做出另一个最佳选择,而您不太可能希望重新访问“优化”或列出哪些编译环境应该使用乘法,哪些应该移位。想想10年前写的所有非2幂次移位的“优化”,它们现在在现代处理器上运行时正在放慢它们的代码...!
值得庆幸的是,当启用任何优化后(例如...main(...) { return (argc << 4) + (argc << 2) + argc; }
-> imull $21, 8(%ebp), %eax
),像GCC这样的优秀编译器通常都可以用直接乘法替换一系列移位和算术运算,因此,即使不修复代码也可以重新编译,但这是不能保证的。
实现乘法或除法的奇怪的移位代码无法充分表达您在概念上想要实现的目标,因此其他开发人员会对此感到困惑,并且困惑的程序员更可能会引入错误或删除一些必要的东西,以恢复看似理智的状态。如果您仅在非显而易见的事情真正受益时才做,然后很好地记录下来(但无论如何也不要记录其他直观的事情),那么每个人都会更加快乐。
如果你有一些额外的知识,比如,你int
真的会只存储值x
,y
并且z
,然后你可以制定出一些指令,对于那些价值的工作,你更快地得到您的结果比当编译器没有这种洞察力,并需要适用于所有int
价值的实施方案。例如,考虑您的问题:
可以使用位运算符来实现乘法和除法...
您说明了乘法,但是除法又如何呢?
int x;
x >> 1; // divide by 2?
根据C ++标准5.8:
-3- E1 >> E2的值是E1右移E2位的位置。如果E1具有无符号类型,或者E1具有带符号类型和非负值,则结果的值是E1商的整数部分除以加到幂E2的数量2。如果E1具有带符号的类型和负值,则结果值是实现定义的。
因此,当x
为负数时,您的移位具有一个实现定义的结果:在不同的机器上,它的工作方式可能不同。但是,/
工作远比预期的要好。 (这也可能不是完全一致的,因为不同的机器可能具有不同的负数表示形式,因此即使在表示位数的位数相同的情况下,范围也不同。)
您可能会说:“我不在乎...那int
是在存储员工的年龄,它永远不会是负面的”。如果您有这种特殊的见识,那么可以-您的>>
安全优化可能会被编译器传递,除非您在代码中明确进行了优化。但是,这是冒险的,并且很少会用到,因为在很多时候您不会有这种洞察力,并且其他从事同一代码的程序员也不知道您是否对数据的某些非常规期望下了赌注将会处理...由于您的“优化”,对他们来说似乎完全安全的更改可能适得其反。
是否存在无法以这种方式相乘或除法的输入?
是的...如上所述,当通过移位将“负数”分开时,负数具有实现定义的行为。
intVal>>1
它们具有相同的语义,但intVal/2
有时会有所不同。如果需要以可移植的方式计算通用架构所能产生的价值intVal >> 1
,则表达式将变得更加复杂且难于阅读,并且可能会生成劣于为其生成的代码intVal >> 1
。
刚尝试在我的机器上编译:
int a = ...;
int b = a * 10;
拆卸时会产生输出:
MOV EAX,DWORD PTR SS:[ESP+1C] ; Move a into EAX
LEA EAX,DWORD PTR DS:[EAX+EAX*4] ; Multiply by 5 without shift !
SHL EAX, 1 ; Multiply by 2 using shift
该版本比纯手工移位和加法的手动优化代码要快。
您实际上永远都不知道编译器将要做什么,因此最好编写一个普通的乘法并让他优化他想要的方式,除非在非常精确的情况下,您知道编译器无法优化。
vector<T>::size()
。我的编译器很古老!:)
移位通常比指令级的乘法快很多,但是您可能会浪费时间进行过早的优化。编译器可以在编译时很好地执行这些优化。自己执行此操作会影响可读性,并且可能对性能没有影响。如果您已经剖析并发现这是一个瓶颈,那么做这样的事情可能只是值得的。
实际上,被称为“魔术师”的分裂技巧实际上可以产生巨大的回报。同样,您应该首先进行概要分析以查看是否需要。但是,如果您确实使用它,则周围会有一些有用的程序可以帮助您弄清楚相同的划分语义需要哪些指令。这是一个例子: http //www.masm32.com/board/index.php?topic=12421.0
我从MASM32上的OP线程提取的一个示例:
include ConstDiv.inc
...
mov eax,9999999
; divide eax by 100000
cdiv 100000
; edx = quotient
会产生:
mov eax,9999999
mov edx,0A7C5AC47h
add eax,1
.if !CARRY?
mul edx
.endif
shr edx,16
移位和整数乘法指令在大多数现代CPU上具有相似的性能-整数乘法指令在1980年代相对较慢,但总的来说,这不再成立。整数乘法指令可能具有更高的延迟,因此在某些情况下,最好还是使用移位。同理,可以让更多的执行单元处于繁忙状态(尽管这可能会双向中断)。
不过,整数除法仍然相对较慢,因此使用移位而不是除以2的幂仍然是一个胜利,大多数编译器会将其实现为优化。但是请注意,要使此优化有效,就需要对红利进行无符号化或者必须知道是正数。对于负股息,转移和除数并不相等!
#include <stdio.h>
int main(void)
{
int i;
for (i = 5; i >= -5; --i)
{
printf("%d / 2 = %d, %d >> 1 = %d\n", i, i / 2, i, i >> 1);
}
return 0;
}
输出:
5 / 2 = 2, 5 >> 1 = 2
4 / 2 = 2, 4 >> 1 = 2
3 / 2 = 1, 3 >> 1 = 1
2 / 2 = 1, 2 >> 1 = 1
1 / 2 = 0, 1 >> 1 = 0
0 / 2 = 0, 0 >> 1 = 0
-1 / 2 = 0, -1 >> 1 = -1
-2 / 2 = -1, -2 >> 1 = -1
-3 / 2 = -1, -3 >> 1 = -2
-4 / 2 = -2, -4 >> 1 = -2
-5 / 2 = -2, -5 >> 1 = -3
因此,如果要帮助编译器,请确保被除数中的变量或表达式是显式无符号的。
据我所知,在某些机器中,乘法可能需要多达16到32个机器周期。所以,是的,这取决于机器的类型,位移操作符比乘法/除法快。
但是,某些机器确实具有其数学处理器,其中包含用于乘法/除法的特殊指令。
在带符号整数和右移与除法的情况下,可以有所作为。对于负数,移位四舍五入为负无穷大,而除法四舍五入为零。当然,编译器会将除法更改为更便宜的方法,但是通常会将其更改为与除法具有相同舍入行为的方法,因为它要么无法证明变量不会为负数,要么不会关心。因此,如果您可以证明数字不会为负数,或者不关心数字的取整方式,则可以采用更有可能产生变化的方式进行优化。
unsigned
Python测试针对相同的随机数执行了1亿次相同的乘法。
>>> from timeit import timeit
>>> setup_str = 'import scipy; from scipy import random; scipy.random.seed(0)'
>>> N = 10*1000*1000
>>> timeit('x=random.randint(65536);', setup=setup_str, number=N)
1.894096851348877 # Time from generating the random #s and no opperati
>>> timeit('x=random.randint(65536); x*2', setup=setup_str, number=N)
2.2799630165100098
>>> timeit('x=random.randint(65536); x << 1', setup=setup_str, number=N)
2.2616429328918457
>>> timeit('x=random.randint(65536); x*10', setup=setup_str, number=N)
2.2799630165100098
>>> timeit('x=random.randint(65536); (x << 3) + (x<<1)', setup=setup_str, number=N)
2.9485139846801758
>>> timeit('x=random.randint(65536); x // 2', setup=setup_str, number=N)
2.490908145904541
>>> timeit('x=random.randint(65536); x / 2', setup=setup_str, number=N)
2.4757170677185059
>>> timeit('x=random.randint(65536); x >> 1', setup=setup_str, number=N)
2.2316000461578369
因此,在python中进行移位而不是乘以2的乘积/除法时,会略有改善(除法约为10%;乘法约为1%)。如果其非2的幂,则可能会出现相当大的减速。
同样,这些#号将根据您的处理器,您的编译器(或解释器-为简单起见在python中执行)而更改。
与其他所有人一样,请勿过早优化。编写易读的代码,如果速度不够快,则进行概要分析,然后尝试优化慢速部分。请记住,您的编译器在优化方面比您要好得多。
编译器无法进行某些优化,因为它们仅适用于减少的输入集。
下面是c ++示例代码,可以执行64位“乘以倒数”来更快地进行除法。分子和分母都必须低于某个阈值。请注意,它必须编译为使用64位指令才能比正常除法速度更快。
#include <stdio.h>
#include <chrono>
static const unsigned s_bc = 32;
static const unsigned long long s_p = 1ULL << s_bc;
static const unsigned long long s_hp = s_p / 2;
static unsigned long long s_f;
static unsigned long long s_fr;
static void fastDivInitialize(const unsigned d)
{
s_f = s_p / d;
s_fr = s_f * (s_p - (s_f * d));
}
static unsigned fastDiv(const unsigned n)
{
return (s_f * n + ((s_fr * n + s_hp) >> s_bc)) >> s_bc;
}
static bool fastDivCheck(const unsigned n, const unsigned d)
{
// 32 to 64 cycles latency on modern cpus
const unsigned expected = n / d;
// At least 10 cycles latency on modern cpus
const unsigned result = fastDiv(n);
if (result != expected)
{
printf("Failed for: %u/%u != %u\n", n, d, expected);
return false;
}
return true;
}
int main()
{
unsigned result = 0;
// Make sure to verify it works for your expected set of inputs
const unsigned MAX_N = 65535;
const unsigned MAX_D = 40000;
const double ONE_SECOND_COUNT = 1000000000.0;
auto t0 = std::chrono::steady_clock::now();
unsigned count = 0;
printf("Verifying...\n");
for (unsigned d = 1; d <= MAX_D; ++d)
{
fastDivInitialize(d);
for (unsigned n = 0; n <= MAX_N; ++n)
{
count += !fastDivCheck(n, d);
}
}
auto t1 = std::chrono::steady_clock::now();
printf("Errors: %u / %u (%.4fs)\n", count, MAX_D * (MAX_N + 1), (t1 - t0).count() / ONE_SECOND_COUNT);
t0 = t1;
for (unsigned d = 1; d <= MAX_D; ++d)
{
fastDivInitialize(d);
for (unsigned n = 0; n <= MAX_N; ++n)
{
result += fastDiv(n);
}
}
t1 = std::chrono::steady_clock::now();
printf("Fast division time: %.4fs\n", (t1 - t0).count() / ONE_SECOND_COUNT);
t0 = t1;
count = 0;
for (unsigned d = 1; d <= MAX_D; ++d)
{
for (unsigned n = 0; n <= MAX_N; ++n)
{
result += n / d;
}
}
t1 = std::chrono::steady_clock::now();
printf("Normal division time: %.4fs\n", (t1 - t0).count() / ONE_SECOND_COUNT);
getchar();
return result;
}
我认为,在一种情况下,您想要乘以2或乘以除法,即使使用编译器将它们转换为MUL / DIV,使用位移位运算符也不会出错,因为某些处理器使用了微代码(实际上是处理器宏),因此对于这些情况,您将获得改进,尤其是当移位大于1时。或更明确地讲,如果CPU没有位移位运算符,则无论如何它都将是MUL / DIV,但是如果CPU具有位移位运算符,您可以避免微代码分支,这少了几条指令。
我现在正在编写一些代码,由于它正在密集的二叉树上工作,因此需要大量加倍/减半运算,而且我怀疑还有一个比加法更优化的运算-左(二乘幂) )加班。如果移位比您要添加的位数大,则可以用左移和xor代替,简单的例子是(i << 1)^ 1,它将加一。这当然不适用于向右移位(二分的幂),因为只有向左移位(小尾数)填充了零的间隙。
在我的代码中,这两个乘/除法和两个运算的幂的使用非常密集,并且由于公式已经很短了,因此可以消除的每条指令都可以带来可观的收益。如果处理器不支持这些移位运算符,则不会发生任何增益,但也不会造成任何损失。
另外,在我正在编写的算法中,它们直观地表示发生的运动,因此从某种意义上说它们实际上更加清晰。二叉树的左侧较大,右侧较小。除此之外,在我的代码中,奇数和偶数具有特殊的意义,树中的所有左手子代都是奇数,而所有右手子代和根都是偶数。在某些情况下,我还没有遇到过,但是,哦,实际上,我什至没有想到这一点,与x%2相比,x&1可能是更理想的操作。偶数的x&1将产生零,而奇数将产生1。
不仅仅只是奇数/偶数标识,如果x&3得到零,我知道4是我们数字的因数,x%7等于8。我知道这些情况的实用程序可能很有限,但是很高兴知道您可以避免模运算,而可以使用按位逻辑运算,因为按位运算几乎总是最快的,并且对于编译器来说模棱两可。
我几乎是在发明密集的二叉树领域,所以我希望人们可能不会理解此注释的价值,因为人们很少只希望仅使用2的幂或仅使用2的乘/除幂来进行因式分解。
它是否实际上更快取决于实际使用的硬件和编译器。
如果在gcc编译器上比较x + x,x * 2和x << 1语法的输出,则在x86汇编中将得到相同的结果:https : //godbolt.org/z/JLpp0j
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov eax, DWORD PTR [rbp-4]
add eax, eax
pop rbp
ret
因此,您可以将gcc视为足够聪明的人,可以独立于键入的内容确定自己的最佳解决方案。
我也想看看我是否能击败众议院。对于任何数乘以任何数的乘法运算,这是更通用的按位运算。我制作的宏比普通*乘法快25%到两倍。正如其他人所说,如果它接近2的倍数或由2的几个倍数组成,则您可能会赢。如由(X << 4)+(X << 2)+(X << 1)+ X组成的X * 23会慢于由(X << 6)+ X组成的X * 65。
#include <stdio.h>
#include <time.h>
#define MULTIPLYINTBYMINUS(X,Y) (-((X >> 30) & 1)&(Y<<30))+(-((X >> 29) & 1)&(Y<<29))+(-((X >> 28) & 1)&(Y<<28))+(-((X >> 27) & 1)&(Y<<27))+(-((X >> 26) & 1)&(Y<<26))+(-((X >> 25) & 1)&(Y<<25))+(-((X >> 24) & 1)&(Y<<24))+(-((X >> 23) & 1)&(Y<<23))+(-((X >> 22) & 1)&(Y<<22))+(-((X >> 21) & 1)&(Y<<21))+(-((X >> 20) & 1)&(Y<<20))+(-((X >> 19) & 1)&(Y<<19))+(-((X >> 18) & 1)&(Y<<18))+(-((X >> 17) & 1)&(Y<<17))+(-((X >> 16) & 1)&(Y<<16))+(-((X >> 15) & 1)&(Y<<15))+(-((X >> 14) & 1)&(Y<<14))+(-((X >> 13) & 1)&(Y<<13))+(-((X >> 12) & 1)&(Y<<12))+(-((X >> 11) & 1)&(Y<<11))+(-((X >> 10) & 1)&(Y<<10))+(-((X >> 9) & 1)&(Y<<9))+(-((X >> 8) & 1)&(Y<<8))+(-((X >> 7) & 1)&(Y<<7))+(-((X >> 6) & 1)&(Y<<6))+(-((X >> 5) & 1)&(Y<<5))+(-((X >> 4) & 1)&(Y<<4))+(-((X >> 3) & 1)&(Y<<3))+(-((X >> 2) & 1)&(Y<<2))+(-((X >> 1) & 1)&(Y<<1))+(-((X >> 0) & 1)&(Y<<0))
#define MULTIPLYINTBYSHIFT(X,Y) (((((X >> 30) & 1)<<31)>>31)&(Y<<30))+(((((X >> 29) & 1)<<31)>>31)&(Y<<29))+(((((X >> 28) & 1)<<31)>>31)&(Y<<28))+(((((X >> 27) & 1)<<31)>>31)&(Y<<27))+(((((X >> 26) & 1)<<31)>>31)&(Y<<26))+(((((X >> 25) & 1)<<31)>>31)&(Y<<25))+(((((X >> 24) & 1)<<31)>>31)&(Y<<24))+(((((X >> 23) & 1)<<31)>>31)&(Y<<23))+(((((X >> 22) & 1)<<31)>>31)&(Y<<22))+(((((X >> 21) & 1)<<31)>>31)&(Y<<21))+(((((X >> 20) & 1)<<31)>>31)&(Y<<20))+(((((X >> 19) & 1)<<31)>>31)&(Y<<19))+(((((X >> 18) & 1)<<31)>>31)&(Y<<18))+(((((X >> 17) & 1)<<31)>>31)&(Y<<17))+(((((X >> 16) & 1)<<31)>>31)&(Y<<16))+(((((X >> 15) & 1)<<31)>>31)&(Y<<15))+(((((X >> 14) & 1)<<31)>>31)&(Y<<14))+(((((X >> 13) & 1)<<31)>>31)&(Y<<13))+(((((X >> 12) & 1)<<31)>>31)&(Y<<12))+(((((X >> 11) & 1)<<31)>>31)&(Y<<11))+(((((X >> 10) & 1)<<31)>>31)&(Y<<10))+(((((X >> 9) & 1)<<31)>>31)&(Y<<9))+(((((X >> 8) & 1)<<31)>>31)&(Y<<8))+(((((X >> 7) & 1)<<31)>>31)&(Y<<7))+(((((X >> 6) & 1)<<31)>>31)&(Y<<6))+(((((X >> 5) & 1)<<31)>>31)&(Y<<5))+(((((X >> 4) & 1)<<31)>>31)&(Y<<4))+(((((X >> 3) & 1)<<31)>>31)&(Y<<3))+(((((X >> 2) & 1)<<31)>>31)&(Y<<2))+(((((X >> 1) & 1)<<31)>>31)&(Y<<1))+(((((X >> 0) & 1)<<31)>>31)&(Y<<0))
int main()
{
int randomnumber=23;
int randomnumber2=23;
int checknum=23;
clock_t start, diff;
srand(time(0));
start = clock();
for(int i=0;i<1000000;i++)
{
randomnumber = rand() % 10000;
randomnumber2 = rand() % 10000;
checknum=MULTIPLYINTBYMINUS(randomnumber,randomnumber2);
if (checknum!=randomnumber*randomnumber2)
{
printf("s %i and %i and %i",checknum,randomnumber,randomnumber2);
}
}
diff = clock() - start;
int msec = diff * 1000 / CLOCKS_PER_SEC;
printf("MULTIPLYINTBYMINUS Time %d milliseconds", msec);
start = clock();
for(int i=0;i<1000000;i++)
{
randomnumber = rand() % 10000;
randomnumber2 = rand() % 10000;
checknum=MULTIPLYINTBYSHIFT(randomnumber,randomnumber2);
if (checknum!=randomnumber*randomnumber2)
{
printf("s %i and %i and %i",checknum,randomnumber,randomnumber2);
}
}
diff = clock() - start;
msec = diff * 1000 / CLOCKS_PER_SEC;
printf("MULTIPLYINTBYSHIFT Time %d milliseconds", msec);
start = clock();
for(int i=0;i<1000000;i++)
{
randomnumber = rand() % 10000;
randomnumber2 = rand() % 10000;
checknum= randomnumber*randomnumber2;
if (checknum!=randomnumber*randomnumber2)
{
printf("s %i and %i and %i",checknum,randomnumber,randomnumber2);
}
}
diff = clock() - start;
msec = diff * 1000 / CLOCKS_PER_SEC;
printf("normal * Time %d milliseconds", msec);
return 0;
}