二进制整数的乘除运算

二进制整数的乘除运算

前言

运算一直视程序执行其中一个重要的环节,而在二进制的运算过程其中,加法运算有时重中之重,他基本上奠定了二进制运算的基础.因此不管是减法还是乘法,都能够由加法运算来取代,只有除法不能取代.

 

了解了计算机运算的规律,能够有助于我们理解非常多程序代码上无法理解的内容能够.比方上一张提出的溢出问题,在了解了加法运算的原理之后,相信大家都能够轻松您的知道为何有些运算会得到意想不到的结果.

 

这里还须要提到一点,不同的处理器所採取的运算方式可能有细微的区别,因此也不能一概而论.因此我们大多数时候会尽量讨论运算的而抽象数据特性,抽象的东西大部分时候总是可靠的,这样的特性为跨平台提供了基础,只是也并不是是总是如此,毕竟本屌仅仅听说过浮点数运算标准,没听过整数运算标准.可能有,可是本屌没听过.

 

正因如此,我们了解一下运算的抽象性,会有助于我们理解程序代码级无法理解的东西.

 

 

无符号乘法

无符号的乘法与加法类似,他的运算方式是比較简单的,仅仅是也可能产生溢出.对于两个w位的无符号来说,他们的乘积范围在0(2w次方-1)的平方之间,因此可能须要2w位的二进制才干表示.因此因为位数的限制,如果两个w位的无符号数的真实乘积为pro,依据截断的规则,则实际得到的乘积为pro mod 2w次方

 

 

 

补码乘法

与加法运算类似,补码乘法也是家里在无符号的基础之上的,因此我们能够非常easy得到,对于两个w位的补码来说,如果他们的真实乘积为pro,则实际得到的乘积为U2Tw(pro mod 2w次方)

 

上面的式子我们有一个如果,就是如果对于w位的两个补码数来说,他们的乘积的低w位于无符号数乘积的低w位是一样的.这意味着计算机能够使用一个指令运行无符号和补码的乘法运算.

 

证明过程略过了,就是利用无符号编码和补码编码的关系.

 

 

 

乘法运算的优化

依据我们小学学的乘法运算,我们知道,如果两个w位的二进制数相乘,则须要进行w次与运算,然后进行w-1次加法运算才干得到结果.从此不难看出,乘法运算的时间周期是非常长的.因此计算机界的大神们出山了,他们相处了一个能够优化乘法运算的方法,就是使用移位运算和加法来取代乘法.

 

上述有花的前提是对于一个w位的二进制数来说,它与2

k次方的乘积,等同于这个二进制数左移k,在低位补k0.证明过程有兴趣的同学自己看看,我反正没兴趣.事实上还是主要应用了无符号编码的公式.

 

 

有了上面的基础,我们就能够使用移位和加法对乘法优化了.对于随意一个整数y,他总是能使用二进制序列表示(如果不超过二进制的表示范围),因此我们将xy乘积的二进制序列表示为例如以下形式:

 x * y = x * (yw-12w-1 + ... + y020) =  (x << w-1) * yw-1 +....+ (x << 0 ) * y0

举个样例,对于x*17,,我们能够计算x*16+x=(x<<4)+x,这样算下来的话,我们仅仅需一次移位一次加法就能够搞定了.而对于x*14来说,则能够计算x*8+x*4+x*2=(x<<3)+(x<<2)+(x<<1),更快的方式我们能够直接这样做:x*16-x*2=(x<<4)-(x<<1).

这里最后须要提一下的是,加法,减法和移位的速度并不会总快于乘法运算,因此是否要进行上面的优化就取决于二者的速度了.

 

 

 

无符号除法

除法和乘法不同,出发不满足假发的分配率,也就是设y=m+n,x/y!=x/m+x/n,更不幸的是,它有时候会比乘法运算更慢,可是我们仅仅探讨仅仅能针对除数可表示为2k次方的除法运算进行优化,转换为算术右移或者逻辑右移k位的运算(无符号数为逻辑右移,为正数时,逻辑右移与算术右移效果一样).

由于是除法,因此我们会涉及到舍入的问题.

 

先看案例:

            int a = 17;

            int b = 8;

            int c = a / b;

            Console.WriteLine("a:" + Convert.ToString(a,2));

            Console.WriteLine("b:" + Convert.ToString(b, 2));

            Console.WriteLine("c:" + Convert.ToString(c, 2));

            Console.WriteLine("a >> 3:" + Convert.ToString(a>>3, 2));

还是C#代码,这段程序的结果能够看出a/b的结果就是a右移3位的结果,就是结果等于a>>3

 

 

无符号数除以2k次方等价于右移k.证明过程不说了,记住结论.

 

不知道你发现了没有,乘法是左移,乘法是右移.

 

 

 

补码除法

哟与刚才我们的程序使用的都是正数,因此尽管C#中没有无符号数,只是我们能够模拟出无符号数的结果.也能够觉得,补码除法在被除数为正数的情况下,与无符号除法是一样的效果(暂别考虑除数为负数的情况了,由于被除数与除数的符号位能够相互抵消,下面也一样),只是当被除数为负数时就不同了.这里在介绍补码除法之前,我们先来看一下,a为负数时的结果,也就是此时会採用补码编码.

案例:

            int a = -17;

            int b = 8;

            int c = a / b;

            Console.WriteLine("a:" + Convert.ToString(a,2));

            Console.WriteLine("b:" + Convert.ToString(b, 2));

            Console.WriteLine("c:" + Convert.ToString(c, 2));

            Console.WriteLine("a >> 3:" + Convert.ToString(a>>3, 2));

            Console.WriteLine("c:"+c);

            Console.WriteLine("a >> 3:" + Convert.ToString(a>>3));

结果有点出人意料:

a:11111111111111111111111111101111

b:1000

c:11111111111111111111111111111110

a >> 3:11111111111111111111111111111101

c:-2

a >> 3:-3

 

 

这次为了便于观看,我们将ca>>3的整数值打印了出来,发现移位运算的结果是-3,a/b的结果为-2.我们能够看出a/b的结果是我们所期望的,能够移位运算的结果在舍入的出现了问题.为啥?

事实上这个问题的解决办法非常easy,补码编码与无符号的编码类似,对于位表示同样.只是此时因为是负数,所以採用了向下舍入.

 

此时能够记住一个结论:当有舍入发生的时候,降一个负数右移k位不等价与把它除以2k次方.

 

 

除法的补数

 

既然在舍入时,一个负数右移k位不等价于把它们除以2k次方.那么为了使用这样的优化,计算机界的大神们有出马了,于是他们又想出了一个办法,”偏置”这个值.

 

在上面的基础上,”偏置”的含义是啥?

看个案例:

            int a = -17;

            int b = 8;

            int c = a / b;

            Console.WriteLine("a:" + Convert.ToString(a,2));

            Console.WriteLine("b:" + Convert.ToString(b, 2));

            Console.WriteLine("c:" + Convert.ToString(c, 2));

            Console.WriteLine("a >> 3:" + Convert.ToString((a+b-1)>>3, 2));

            Console.WriteLine("c:"+c);

            Console.WriteLine("a >> 3:" + Convert.ToString((a + b - 1) >> 3));

 

此处我们将a”偏置”,也就是加上b-1的偏移量,结果例如以下:

a:11111111111111111111111111101111

b:1000

c:11111111111111111111111111111110

a >> 3:11111111111111111111111111111110

c:-2

a >> 3:-2

可以看出,在偏置之后,在负结果舍入时,移位运算的结果将会是我们期望得到的,这样我们便可以使用这一技巧进行优化了,什么歌意思呢?我们在做了将a+b-1这个处理之后,结果会在原来的基础上加1.这就是偏置的含义所在,它会将舍入偏置到向上舍入.

 

小结

二进制整数的运算较重要吗?不是,是非常重要,也较难,多花点力气没错.

转载于:https://www.cnblogs.com/lxjshuju/p/7001665.html

<think>首先,用户的问题是:“我想理解或执行二进制位移运算的方法 请问二进制位移运算的原理及实现方式”。这是一个关于二进制位移运算的查询。 根据系统级指令: - 所有行内数学表达式必须使用 `$...$` 格式。 - 独立公式必须使用 `$$...$$` 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 用户可见层指令: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考站内引用: - 引用 [1]:关于位移运算用于乘法优化。 - 引用 [2]:详细解释位移运算,包括正数和负数的例子。 - 引用 [3]:介绍位逻辑运算和位移运算。 - 引用 [4]:关于Java中的二进制运算、位移运算和逻辑运算。 我需要基于这些引用构建回答,因为系统指令说“请尽可能多的参考用户上一次的问题和你的回答”,但这是第一个问题,所以我主要参考提供的引用。 核心内容:二进制位移运算的原理及实现方式。 原理: - 位移运算包括左移(<<)和右移(>>)。 - 左移:将二进制数向左移动n位,等价于乘以2的n次方。 - 右移:将二进制数向右移动n位,等价于除以2的n次方,但需要注意奇数和负数的处理。 - 对于负数,右移时高位补1;对于正数,高位补0。 - 位移运算在计算机中高效,因为它直接操作位。 实现方式: - 在编程语言中,如Java,使用 << 和 >> 操作符。 - 例如,在引用 [1] 中,有Java代码实现位移运算。 - 在引用 [2] 中,有具体例子展示正数和负数的位移效果。 回答结构: 1. **介绍原理**:解释位移运算的基本概念。 2. **详细说明左移和右移**:包括正负数的处理。 3. **实现方式**:提供代码示例(如Java代码)。 4. **应用和注意事项**:讨论为什么使用位移运算,以及潜在问题。 5. **生成相关问题**:在回答末尾添加相关问题。 使用中文回答。 确保数学表达式正确: - 行内:如 $2^n$。 - 独立公式:如果需要,使用 $$...$$。 引用标识: - 在引用内容末尾添加 [^1], [^2] 等。 最终回答结构: - 清晰分段。 - 逐步解释。 - 代码块(如果适用)。 - 相关问题。 相关问题生成: - 基于主题,生成3-5个相关问题。 现在,构建回答大纲: 1. **引言**:简要介绍二进制位移运算。 2. **原理**: - 左移的原理:$x << n = x \times 2^n$ - 右移的原理:$x >> n = x \div 2^n$(近似) - 讨论正负数的差异。 3. **实现方式**: - 在编程中的操作符。 - 代码示例(如Java)。 4. **注意事项**: - 对于奇数和负数的右移问题。 - 效率优势。 5. **总结**。 6. **相关问题**。 代码示例:从引用 [1] 中提取Java代码。</think>### 二进制位移运算的原理及实现方式 二进制位移运算是一种在计算机底层直接操作二进制位的技术,主要包括左移(`<<`)和右移(`>>`)操作。它通过移动二进制数的位来实现高效的数值计算,常用于优化乘除法、数据压缩或位掩码处理。下面我将逐步解释其原理和实现方式,确保内容清晰易懂。 #### 1. **位移运算的基本原理** 位移运算基于二进制数的位级操作: - **左移(<<)**:将二进制数的所有位向左移动指定位数(n),右侧空位补0。左移n位等价于将原数乘以 $2^n$。例如,二进制数 $101_2$(十进制5)左移2位后变为 $10100_2$(十进制20),即 $5 \times 2^2 = 20$。数学上表示为 $x << n = x \times 2^n$[^1]。 - **右移(>>)**:将二进制数的所有位向右移动指定位数(n),左侧空位根据符号位填充(正数补0,负数补1)。右移n位近似等价于将原数除以 $2^n$(向下取整)。例如,二进制数 $1100_2$(十进制12)右移2位后变为 $0011_2$(十进制3),即 $12 \div 2^2 = 3$。但需注意:对于奇数和负数,结果可能不精确,因为右移会丢弃低位(如二进制数 $101_2$(5)右移1位得 $10_2$(2),而非2.5)[^2]。 对于正数和负数,位移运算的处理不同: - **正数**:位移操作直接对应乘除运算,左移增大数值,右移减小。 - **负数**:在补码表示下(计算机内部存储方式),右移时高位补1(而非0),以保持符号位不变。例如,-35(二进制补码 $11011101_2$)右移1位得 $11101110_2$(十进制-18),而非精确除以2[^2][^4]。 位移运算的高效性源于它直接在硬件层面操作位,避免了复杂的算术逻辑,因此常用于性能敏感的代码中(如加密算法或嵌入式系统)[^1][^2]。 #### 2. **实现方式** 在编程语言中,位移运算通过操作符实现(如Java、C/C++)。以下是基于引用内容的Java实现示例,展示如何用位移运算代替乘除法。 ##### Java代码示例 以下代码演示了左移和右移的实现: ```java public class BitShiftExample { // 左移实现乘法:m << n 等价于 m * 2^n public static int leftShiftMultiply(int m, int n) { return m << n; // 左移n位 } // 右移实现除法:m >> n 近似等价于 m / 2^n(整数除法) public static int rightShiftDivide(int m, int n) { return m >> n; // 右移n位 } public static void main(String[] args) { // 示例:5 * 4 = 20(左移2位) System.out.println("5 左移 2 位: " + leftShiftMultiply(5, 2)); // 输出 20 // 示例:12 / 4 = 3(右移2位) System.out.println("12 右移 2 位: " + rightShiftDivide(12, 2)); // 输出 3 // 负数示例:-35 右移 1 位(近似除以2) System.out.println("-35 右移 1 位: " + rightShiftDivide(-35, 1)); // 输出 -18 } } ``` **代码说明**: - **左移**:`m << n` 直接实现乘法优化,效率高于常规乘法指令[^1]。 - **右移**:`m >> n` 用于除法优化,但需注意整数截断(如奇数的右移结果向下取整)和负数的符号位处理[^2][^4]。 - **注意事项**:位移运算仅适用于整数类型(如Java中的`int`或`long`)。对于浮点数,需先转换为整数操作。此外,右移负数时,高位补1可能导致结果不直观(如-35右移得-18),建议在代码中添加注释以提高可读性[^2][^4]。 #### 3. **注意事项和应用场景** - **优势**:位移运算比标准乘除法快得多(尤其在嵌入式系统或高频交易中),因为它只需单条CPU指令[^1][^2]。 - **局限**: - 右移不能精确处理奇数的除法(如5右移1位得2,而非2.5)。 - 负数右移时,由于补码规则,结果可能与预期不符(引用[2]中的例子显示-99右移1位得-50)[^2]。 - 位移位数(n)不能超过数据类型位数(如32位int的n<32),否则行为未定义。 - **常见应用**: - **性能优化**:在乘法/除法场景中替换为位移(如`x * 8` 用 `x << 3`)[^1]。 - **数据编码**:用于位掩码(如提取RGB颜色值)。 - **加密算法**:在哈希计算(如SHA)中用于位操作[^3]。 总之,二进制位移运算是一种高效但需谨慎使用的工具。掌握其原理后,您可以在代码中灵活应用以提升性能。如果涉及负数和边界情况,建议先测试或使用库函数确保正确性[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值