【问题标题】:Calculating the negabinary representation of a given number without loops计算没有循环的给定数字的负数表示
【发布时间】:2016-10-04 21:38:11
【问题描述】:

您能否提供一个令人信服的解释或数学证明来解释为什么以下函数会计算给定数字的negabinary 表示?

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

【问题讨论】:

  • 你注意到维基百科页面上的版本(C 版本也是)有实际的 cmets,至少部分解释了它。你为什么删除那些?这是个谜吗?
  • @GolezTrol 我觉得那里的 cmets 并没有真正解释这是如何工作的。我正在寻找更详细的解释。

标签: algorithm binary language-agnostic bit-manipulation base-conversion


【解决方案1】:

"0xAAAAAAAA" 是其中一个包含 10(二进制)模式序列的幻数。这被用作掩码,因为您正在执行按位 XOR 操作。当您将数字添加到此掩码并执行 XOR 时,结果将仅受数字提供的那些位的影响,结果中的其余部分将为 0。 [由于两个相同位的 XOR 为 0]。最后,toString(2) 将结果转换为二进制

示例: -> 考虑 3 是你的号码。将 3 添加到 2863311530 [这是 0xAAAAAAAA 的十进制表示]。 -> 将总和(掩码 + 3)与 0xAAAAAAAA 进行异或运算,即 ....101010101101 ^ ....10101010。这给了你 111(因为掩码中的最后 3 个对应位和总和不同) ->将111转换为二进制,即7

【讨论】:

  • 对不起,但我在你的回答中找不到令人信服的论据来解释为什么给定函数计算负数(而不是其他东西)。
  • 你为什么不赞成这个答案?您的问题最初没有要求任何数学证明。我解释了函数“如何”计算给定数字的负数表示。没有理由拒绝投票,因为我没有提到任何错误
【解决方案2】:

负数符号

负二进制表示法使用基数 -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 相加然后异或(一个掩码,表示在负二进制符号中具有负值的奇数位置),例如对于值 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),然后与其原始值的负数组合(通过与掩码进行异或),因此值为2的位n 被 2n+1 - 2n 取代。

因此您可以将快速转换方法简单地想象为:“将每 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 × (2n-1) → 1/3 × (2n-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 × (2n-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 范围,因此需要进行溢出检查。)

【讨论】:

  • 哇!感谢您提供如此详细而彻底的答案。我真的很感谢你的努力!您的答案的开头非常简单且令人信服,但 consecutive set bits 部分并不像我希望的那样直截了当。还是谢谢!
  • @MishaMoroshko 最基本的解释是“对于每 2 或 8 或 ... 即将变成 -2 或 -8 或 ... 4 或 16 或 ...相加,所以 2 变成 4-2,8 变成 16-8,……”。 “连续位”部分只是表明可以将多个位相加,例如8+4+2 变成 16-8 + 4 + 4-2,然后两个 4 结转变成 8,然后 -8 和 8 相互抵消,剩下 16-2 .为了清楚起见,也许我应该添加这个十进制版本的解释?
  • 我觉得现在好多了。再次感谢所有的努力!
猜你喜欢
  • 1970-01-01
  • 2018-05-07
  • 1970-01-01
  • 2017-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-19
相关资源
最近更新 更多