如果电脑的处理器支持进位加法,那么还可以采用第三种思路 。
这时,如果寄存器大小为n位,那么两个n位的无符号整数的和就可以理解为n+1位,通过RCR(带进位循环右移)指令,就可以得到正确的平均值,且不损失溢出的位 。
文章图片
带进位循环右移
// x86-32 mov eax, a add eax, b ; Add, overflow goes into carry bit rcr eax, 1 ; Rotate right one place through carry // x86-64 mov rax, a add rax, b ; Add, overflow goes into carry bit rcr rax, 1 ; Rotate right one place through carry // 32-bit ARM (A32) mov r0, a adds r0, b ; Add, overflow goes into carry bit rrx r0 ; Rotate right one place through carry // SH-3 clrt ; Clear T flag mov a, r0 addc b, r0 ; r0 = r0 + b + T, overflow goes into T bit rotcr r0 ; Rotate right one place through carry那如果处理器不支持带进位循环右移操作呢?
也可以使用内循环(rotation intrinsic):
unsigned average(unsigned a, unsigned b) { #if defined(_MSC_VER) unsigned sum; auto carry = _addcarry_u32(0, a, b, &sum); sum = (sum & ~1) | carry; return _rotr(sum, 1); #elif defined(__clang__) unsigned carry; sum = (sum & ~1) | carry; auto sum = __builtin_addc(a, b, 0, &carry); return __builtin_rotateright32(sum, 1); #else #error Unsupported compiler. #endif }结果是,x86架构下的代码生成没有发生什么变化,MSCver架构下的代码生成变得更糟,而arm-thumb2的clang 的代码生成更好了 。
// _MSC_VER mov ecx, a add ecx, b ; Add, overflow goes into carry bit setc al ; al = 1 if carry set and ecx, -2 ; Clear bottom bit movzx ecx, al ; Zero-extend byte to 32-bit value or eax, ecx ; Combine ror ear, 1 ; Rotate right one position ; Result in eax // __clang__ mov ecx, a add ecx, b ; Add, overflow goes into carry bit setc al ; al = 1 if carry set shld eax, ecx, 31 ; Shift left 64-bit value // __clang__ with ARM-Thumb2 movs r2, #0 ; Prepare to receive carry adds r0, r0, r1 ; Calculate sum with flags adcs r2, r2 ; r2 holds carry lsrs r0, r0, #1 ; Shift sum right one position lsls r1, r2, #31 ; Move carry to bit 31 adds r0, r1, r0 ; Combine微软大神的思考们Raymond Chen1992年加入微软,迄今为止已任职25年,做UEX-Shell,也参与Windows开发,Windows系统的很多最初UI架构就是他搞起来的 。
文章图片
他在MSDN 上建立的blogThe Old New Thing也是业内非常出名的纯技术向产出网站 。
这篇博客的评论区们也是微软的各路大神出没,继续深入探讨 。
有人提出了新方法,在MIPS ASM共有36个循环:
unsigned avg(unsigned a, unsigned b { return (a & b) + (a ^ b) / 2; } // lw $3,8($fp) # 5 // lw $2,12($fp) # 5 // and $3,$3,$2 # 4 // lw $4,8($fp) # 5 // lw $2,12($fp) # 5 // xor $2,$4,$2 # 4 // srl $2,$2,1 # 4 // addu $2,$3,$2 # 4有人针对2016年专利法表示,与其用(a / 2) + (b / 2) + (a & b & 1)的方法,为啥不直接把 (a & 1) & ( b & 1 ) ) 作为进位放入加法器中计算呢?
还有人在评论区推荐了TopSpeed编译器,能够通过指定合适的代码字节和调用约定来定义一个内联函数,以解决“乘除结果是16位,中间计算值却不是”的情况 。
只能说,学无止境啊 。
文章图片
原文:https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
参考链接:https://news.ycombinator.com/item?id=30252263
- 微软|Windows系统以后更新得收费?别急,咱们细品
- ide|微软:旧版 Visual Studio 将停止支持,2012/2017/2019 都受影响
- iPad|比iPad香!微软Surface平板打六折:完美蹭情人节热度
- 洗衣机|洗衣机滚筒好还是波轮好?看完终于搞清楚了
- Surface|微软Surface Laptop Studio预售:史上最强大Surface笔记本 12188元起
- 微软|微软动真格了:不符合条件强行升级Win11的用户将收到警告
- 微软|有人说浏览器内核有上千万行代码,那它真的有这么复杂吗?
- 曝《007黄金眼》高清复刻版下周公布 或将由微软宣布
- 字节跳动|OPPO亮利刃:7.02英寸超级屏+6050mAh,看完后很满意
- 微软最强笔记本 Surface Laptop Studio 国行正式开启预售