微软大神“玩”出新花样,求平均值代码还能这样写?( 二 )


unsignedaverage( unsigneda, unsignedb)
{
// Suppose "unsigned" is a 32-bit type and
// "unsigned long long" is a 64-bit type.
return(( unsignedlonglong)a + b) / 2;
}
但是只要涉及的转换就又要针对不同架构的处理器进行特殊处理了 , 比如x86的64位处理器在进行32位整形转换为64位长整形时会自动将高32位的值填为0:
// x86-64: Assume ecx = a, edx = b, upper 32 bits unknown
mov eax, ecx ; rax = ecx zero-extended to 64-bit value
mov edx, edx ; rdx = edx zero-extended to 64-bit value
addrax, rdx ; 64-bit addition: rax = rax + rdx
shr rax, 1; 64-bit shift: rax = rax >> 1
; result iszero-extended
; Answer ineax
// AArch64 (ARM 64-bit): Assume w0 = a, w1 = b, upper 32 bits unknown
uxtw x0, w0 ; x0 = w0 zero-extended to 64-bit value
uxtw x1, w1 ; x1 = w1 zero-extended to 64-bit value
addx0, x1 ; 64-bit addition: x0 = x0 + x1
ubfx x0, x0, 1, 32; Extract bits 1through 32fromresult
; (shift + zero-extend inone instruction)
; Answer inx0
Mips64等架构则会将32位的整形转换为有符号扩展的类型 。 这时候就需要增加rldicl等删除符号的指令做特殊处理 。
// Alpha AXP: Assume a0 = a, a1 = b, both in canonical form
insll a0, #0, a0 ; a0 = a0 zero-extended to 64-bit value
insll a1, #0, a1 ; a1 = a1 zero-extended to 64-bit value
addq a0, a1, v0 ; 64-bit addition: v0 = a0 + a1
srl v0, #1, v0 ; 64-bit shift: v0 = v0 >> 1
addl zero, v0, v0 ; Force canonical form
; Answer inv0
// MIPS64: Assume a0 = a, a1 = b, sign-extended
dext a0, a0, 0, 32; Zero-extend a0 to 64-bit value
dext a1, a1, 0, 32; Zero-extend a1 to 64-bit value
daddu v0, a0, a1 ; 64-bit addition: v0 = a0 + a1
dsrl v0, v0, #1 ; 64-bit shift: v0 = v0 >> 1
sll v0, #0, v0 ; Sign-extend result
; Answer inv0
// Power64: Assume r3 = a, r4 = b, zero-extended
addr3, r3, r4 ; 64-bit addition: r3 = r3 + r4
rldicl r3, r3, 63, 32; Extract bits 63through 32fromresult
; (shift + zero-extend inone instruction)
; result inr3
不过这种向更高位类型转换的方案也有一定问题 , 那就是空间的浪费 , 因为我原本只需要1位去处理溢出就好了 , 但是做了转换之后我却用了白白消费了31位的空间没有利用 。
利用进位处理溢出的改进版本
在现代CPU当中大多都带有Carry bit(这里指进位位 , 不是C位的意思)功能 。 通过读取Carry bit的信息 , 就能达到在不浪费空间的情况下处理溢出的问题 。 比如在X86-32位处理器的代码如下:
// x86-32
mov eax, a
addeax, b ; Add, overflow goes intocarry bit
rcr eax, 1; Rotate right one place through carry
// x86-64
mov rax, a
addrax, b ; Add, overflow goes intocarry bit
rcr rax, 1; Rotate right one place through carry
// 32-bit ARM (A32)
mov r0, a
adds r0, b ; Add, overflow goes intocarry 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 intoT bit
rotcr r0 ; Rotate right one place through carry
而对于那些没有Carry bit功能的处理器来说 , 也可以通过自定义carry bit变量的方式来解决这个问题 。 如下:
unsignedaverage( unsigneda, unsignedb)
{
# ifdefined(_MSC_VER)
unsignedsum;
autocarry = _addcarry_u32( 0, a, b, &sum);
return_rotr1_carry(sum, carry); // missing intrinsic!
# elifdefined(__clang__)
unsignedcarry;
autosum = _builtin_adc(a, b, 0, &carry);
return_builtin_rotateright1throughcarry(sum, carry); // missing intrinsic!
# elifdefined(__GNUC__)

特别声明:本站内容均来自网友提供或互联网,仅供参考,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。