一尘不染

计算给定数字的无负表示

algorithm

您能否提供令人信服的解释或数学证明,为什么以下函数计算给定数字的负整数表示?

function quickNegabinary(number) {
  var mask = 0xAAAAAAAA;
  return ((number + mask) ^ mask).toString(2);
}

阅读 242

收藏
2020-07-28

共1个答案

一尘不染

负符号

负数表示法使用-2的基数。这意味着,就像在每个具有负基数的数字系统中一样,每隔一位都有一个负值:

position:    ...     7    6    5    4    3    2    1    0

binary:      ...   128   64   32   16    8    4    2    1  
negabinary:  ...  -128  +64  -32  +16   -8   +4   -2   +1

快速转换方法

快速二进制→负数转换方法先加上一个数字,然后用0xAAAAAAAA或或二进制数...10101010(表示以负数表示负值的奇数位的掩码)进行xor运算,例如,取值为82:

binary:               01010010  =  82 (binary: 64 + 16 + 2)  
mask:                 10101010  = 170  
bin+mask              11111100  = 252  
(bin+mask) XOR mask:  01010110  =  82 (negabinary: 64 + 16 + 4 - 2)

工作原理:一位

如果以二进制表示法中只有一个置位的数字为例,则很容易看出该方法的工作原理。如果设置的位在偶数位置,则没有任何变化:

binary:               00000100  =   4 (binary: 4)  
mask:                 10101010  = 170  
bin+mask              10101110  = 174  
(bin+mask) XOR mask:  00000100  =   4 (negabinary: 4)

但是,如果设置的位在奇数位置:

binary:               00001000  =   8 (binary: 8)  
mask:                 10101010  = 170  
bin+mask              10110010  = 178  
(bin+mask) XOR mask:  00011000  =   8 (negabinary: 16 - 8)

将设置的位向左移动(通过向其加1),然后将其与原始值的负数组合(通过与掩码进行XOR运算),以便将值为2 n的位替换为2 n + 1 -2 n。

因此,您可以将快速转换方法简单地视为:“将每2乘4-2,每8乘16-8,每32乘64-32替换,依此类推”。

运作方式:多个设定位

当用几个置位转换数字时,如上所述,用一个置位转换数字的结果可以简单地加在一起。结合例如4和8的单个置位示例(参见上文)以得出12:

binary:               00001100  =  12 (binary: 8 + 4)  
mask:                 10101010  = 170  
bin+mask              10110110  = 182  
(bin+mask) XOR mask:  00011100  =  12 (negabinary: 16 - 8 + 4)

或者,对于更复杂的示例,其中携带一些数字:

binary:               00111110  =  62 (binary: 32 + 16 + 8 + 4 + 2)  
mask:                 10101010  = 170  
bin+mask              11101000  = 232  
(bin+mask) XOR mask:  01000010  =  62 (negabinary: 64 - 2)

这里发生的是在描述二进制数的总和中:

32 + 16 + 8 + 4 + 2

32转换为64-32,8转换为16-8,2转换为4-2,因此总和为:

64-32 + 16 + 16-8 + 4 + 4-2

然后两个16变成32,两个4变成8:

64-32 + 32-8 + 8-2

和-32和+32互相抵消,而-8和+8互相抵消,得出:

64-2

或者,使用负算术:

          +1    +1                 (carry)
     0  1 -1  0  0  0  0  0  =  32 (negabinary: 64 - 32)  
     0  0  0  1  0  0  0  0  =  16 (negabinary: 16)  
     0  0  0  1 -1  0  0  0  =   8 (negabinary: 16 - 8)  
     0  0  0  0  0  1  0  0  =   4 (negabinary: 4)  
  +  0  0  0  0  0  1 -1  0  =   2 (negabinary: 4 - 2)  
     ----------------------  
  =  0  1  0  0  0  0 -1  0  =  62 (negabinary: 64 - 2)

负值

快速转换方法也适用于二进制补码表示法的负数,例如:

binary:                       11011010  =    -38 (two's complement)  
mask:                         10101010  =    -86 (two's complement)  
bin+mask                      10000100  =   -124 (two's complement)  
(bin+mask) XOR mask:          00101110  =    -38 (negabinary: -32 - 8 + 4 - 2)



binary:              11111111 11011010  =    -38 (two's complement)  
mask:                10101010 10101010  = -21846 (two's complement)  
bin+mask             10101010 10000100  = -21884 (two's complement)   
(bin+mask) XOR mask: 00000000 00101110  =    -38 (negabinary: -32 - 8 + 4 - 2)

范围和溢出

具有n位(其中n是偶数)的负整数的范围是:

-2/3×(2 Ñ -1)→1/3×(2 Ñ -1)

或者,对于常见的位深度:

 8-bit:            -170  ~             85  
16-bit:         -43,690  ~         21,845  
32-bit:  -2,863,311,530  ~  1,431,655,765  
64-bit:       -1.23e+19  ~       6.15e+18  
80-bit:       -8.06e+23  ~       4.03e+23

此范围低于具有相同位深度的有符号和无符号标准整数表示形式,因此有符号和无符号整数都可能太大而无法以相同的位深度用负号表示。

尽管对于低于-1/6×(2 n -4)的负值,快速转换方法似乎会溢出,但是转换结果仍然正确:

binary:                       11010011 =    -45 (two's complement)  
mask:                         10101010 =    -86 (two's complement)  
bin+mask             11111111 01111101 =   -131 (two's complement)  
overflow truncated:           01111101 =    125 (two's complement)  
(bin+mask) XOR mask:          11010111 =    -45 (negabinary: -128 + 64 + 16 + 4 - 2 + 1)



binary:              11111111 11010011 =    -45 (two's complement)  
mask:                10101010 10101010 = -21846 (two's complement)  
bin+mask             10101010 01111101 = -21891 (two's complement)  
(bin+mask) XOR mask: 00000000 11010111 =    -45 (negabinary: -128 + 64 + 16 + 4 - 2 + 1)

倒车功能

通过反转加法和与掩码的异或,可以将负整数转换回标准整数值,例如:

uint64_t negabinary(int64_t bin) {
    if (bin > 0x5555555555555555) throw std::overflow_error("value out of range");
    const uint64_t mask = 0xAAAAAAAAAAAAAAAA;
    return (mask + bin) ^ mask;
}

int64_t revnegabin(uint64_t neg) {
    // const uint64_t even = 0x2AAAAAAAAAAAAAAA, odd = 0x5555555555555555;
    // if ((neg & even) > (neg & odd)) throw std::overflow_error("value out of range");
    const uint64_t mask = 0xAAAAAAAAAAAAAAAA;
    return (mask ^ neg) - mask;
}

(如果仅对由negabinary()函数创建的负整数调用反向函数,则没有溢出的风险。但是,其他来源的64位负整数可能具有小于int64_t范围的值,因此,溢出检查将变为必要。)

2020-07-28