在C中使用移位运算符的乘法和除法实际上更快吗?

| 例如,可以使用位运算符来实现乘法和除法
i*2 = i<<1
i*3 = (i<<1) + i;
i*10 = (i<<3) + (i<<1)
等等。 使用say1ѭ乘以10实际上比直接使用faster2ѭ要快吗?是否存在无法以这种方式相乘或除法的输入?     
已邀请:
        简短答案:不太可能。 长答案: 您的编译器中包含一个优化器,该优化器知道如何以目标处理器体系结构所能达到的速度快速进行乘法。最好的选择是清楚地告诉编译器您的意图(即i * 2而不是i << 1),然后让它决定最快的汇编/机器码序列。处理器本身甚至可能已将乘法指令实现为一系列移位和微码加法运算。 最重要的是-不要花很多时间担心这个问题。如果您要转移,那就转移。如果您打算乘,乘。做语义上最清晰的事情-您的同事稍后会感谢您。或者更有可能的是,如果不这样做,稍后再诅咒您。     
        只是一个具体的测量点:很多年前,我以两个为基准 我的哈希算法的版本:
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 \'ish的原因是因为它已经超过15年了。 我显然今天会使用
std::string
和迭代器。)     
        除了这里的所有其他好答案之外,让我指出在表示除法或乘法时不使用移位的另一个原因。我从未见过有人通过忘记乘法和加法的相对优先级来引入错误。当维护程序员忘记通过移位进行“乘法”在逻辑上是乘法,但在语法上却没有与乘法相同的优先级时,我已经看到了引入的错误。
x * 2 + z
x << 1 + z
有很大的不同! 如果您正在处理数字,请使用算术运算符,例如
+ - * / %
。如果您正在处理位数组,请使用像
& ^ | >>
这样的位旋转运算符。不要混在一起;一个既有点摇摆又有算术的表达式是一个等待发生的错误。     
        这取决于处理器和编译器。一些编译器已经以这种方式优化代码,而另一些则没有。 因此,您需要每次检查需要以这种方式优化代码的时间。 除非您迫切需要优化,否则我不会为了保存汇编指令或处理器周期而对源代码进行加扰。     
使用(i << 3)+(i << 1)乘以10真的比直接使用i * 10更快吗? 它可能会或可能不会在您的计算机上-如果您在乎,请衡量实际使用情况。 案例研究-从486到酷睿i7 基准测试很难进行有意义的工作,但是我们可以看看一些事实。从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年代就被认真对待的优化琐事-像位移与ѭ10versus-现已被淘汰。位移仍然比较快,但是对于非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)通常可以使用直接乘法替换一系列移位和算术运算,因此即使不修复代码也可以帮助重新编译,但这不能保证。 实现乘法或除法的奇怪的移位代码无法充分表达您在概念上想要实现的目标,因此其他开发人员将对此感到困惑,并且困惑的程序员更有可能引入错误或删除一些必要的东西以进行恢复看起来很理智。如果您仅在非显而易见的事情真正受益时才做,然后很好地记录下来(但无论如何也不要记录其他直观的事情),那么每个人都会更加快乐。 一般解决方案与部分解决方案 如果您有一些额外的知识,例如您的ѭ17really实际上仅存储值
x
y
和ѭ20may,那么您可能能够得出一些适用于这些值的指令,并且比编译器更快地得到结果。 \'s没有这种洞察力,需要一个适用于所有
int
值的实现。例如,考虑您的问题:   可以使用位运算符来实现乘法和除法... 您说明了乘法,但是除法又如何呢?
int x;
x >> 1;   // divide by 2?
根据C ++标准5.8:   -3- E1 >> E2的值是E1右移E2位的位置。如果E1具有无符号类型,或者E1具有带符号类型且非负值,则结果的值是E1商的整数部分除以乘以幂E2的数量2。如果E1具有带符号的类型和负值,则结果值是实现定义的。 因此,当
x
为负数时,您的移位具有实现定义的结果:在不同的机器上,它的工作方式可能不同。但是,
/
的工作原理更可预测。 (这也可能不是完全一致的,因为不同的机器可能具有不同的负数表示形式,因此即使在组成表示的位数相同的情况下,范围也不同。) 您可能会说:“我不在乎...ѭ17正在存储员工的年龄,它永远不会是负数”。如果您有这种特殊的见识,那么可以-编译器可能会将您的
>>
安全优化传递给您,除非您在代码中明确进行了优化。但是,这是冒险的,并且很少会有用,因为在很多时候您不会有这种洞察力,并且其他使用同一代码工作的程序员不会知道您将赌注押在了一些不寻常的地方对您将要处理的数据的期望……由于“优化”,看起来完全安全的更改可能适得其反。   是否存在无法以这种方式相乘或除法的输入? 是的...如上所述,当通过位移位“除”时,负数具有实现定义的行为。     
刚尝试在我的机器上编译:
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
该版本比纯手工移位和加法的手动优化代码要快。 您实际上永远都不知道编译器将要提出什么,因此最好编写一个普通的乘法并让他优化他想要的方式,除非在非常精确的情况下,您知道编译器无法优化。     
        移位通常比指令级的乘法快很多,但您可能会浪费时间进行过早的优化。编译器可以在编译时很好地执行这些优化。自己执行此操作会影响可读性,并且可能对性能没有影响。如果您已经剖析并发现这是一个瓶颈,那么做这样的事情可能仅是值得的。 实际上,被称为“魔术师”的分裂技巧实际上可以产生巨大的回报。同样,您应该首先进行概要分析以查看是否需要。但是,如果您确实使用它,则周围会有一些有用的程序可以帮助您弄清楚相同的划分语义需要哪些指令。这是一个示例: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
因此,如果要帮助编译器,请确保被除数中的变量或表达式是显式无符号的。     
        它完全取决于目标设备,语言,目的等。 显卡驱动程序中的像素失真?很有可能,是的! 您部门的.NET业务应用程序?绝对没有理由对此进行调查。 对于用于移动设备的高性能游戏,可能值得研究,但是只有在执行了更轻松的优化之后。     
        除非绝对必要,否则不要这样做,并且代码意图需要移位而不是乘法/除法。 在通常的一天中,您可能会节省几个机器周期(或宽松,因为编译器知道如何优化),但是成本却不值得-您花时间在次要的细节上,而不是实际的工作上,维护代码变得更加困难您的同事会诅咒您。 对于高负载计算,您可能需要这样做,其中每个保存的周期意味着几分钟的运行时间。但是,您应该一次优化一个位置,并且每次都要进行性能测试,以查看是否确实使速度更快或破坏了编译器逻辑。     
        据我所知,在某些机器中,乘法可能需要多达16到32个机器周期。所以是的,根据机器类型的不同,移位运算符比乘法/除法运算要快。 但是,某些机器确实具有其数学处理器,其中包含用于乘法/除法的特殊指令。     
        我同意德鲁·霍尔的明确回答。答案可以使用一些其他注释。 对于绝大多数软件开发人员来说,处理器和编译器不再与此问题相关。我们大多数人都远远超出了8088和MS-DOS。它可能仅与那些仍在为嵌入式处理器开发的人有关。 在我的软件公司,所有数学都应使用Math(add / sub / mul / div)。 在数据类型之间进行转换时,应使用Shift键。 ushort字节为n >> 8,而不是n / 256。     
在带符号整数和右移与除法的情况下,可以有所作为。对于负数,移位四舍五入为负无穷大,而除法四舍五入为零。当然,编译器会将除法更改为更便宜的方法,但通常会将其更改为与除法具有相同舍入行为的方法,因为它要么无法证明变量不会为负数,要么无法证明\不在乎。 因此,如果可以证明数字不会为负数,或者不关心数字的取整方式,则可以采用更可能产生影响的方式进行优化。     
        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;
}
    

要回复问题请先登录注册