6

Не могли бы вы дать убедительное объяснение или математическое доказательство того, почему следующая функция вычисляет представление заданного числа negabinary?Вычисление отрицательного представления заданного числа без петель

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

Вы заметили, что версии (версия C тоже) на странице Википедии имеют фактические комментарии, что хотя бы частично объясняет это. Почему вы их удалили? Это загадка? – GolezTrol

+0

@GolezTrol Я чувствовал, что комментарии там не совсем объясняют, как это работает. Я ищу более подробное объяснение. –

ответ

14

Negabinary обозначения

Negabinary нотация использует десятичную -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 

метод быстрого преобразования

→ метод быстрой двоичной negabinary преобразования добавляет, а затем исключающее это номер с 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 к это) и затем объединяется с отрицательным значением его исходного значения (посредством XOR-ing с маской), так что бит со значением 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 ca мена друг друга, и -8 и +8 компенсируют друг друга, чтобы дать:

64 - 2

Или, используя negabinary арифметику:

  +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) 

Диапазон и переполнения

Диапазон из числа negabinary с п битов (где п четное число) составляет:

-2/3 × (2 n -1) → 1/3 × (2 n -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 п -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) 

Обратных функции

Negabinary номер может быть преобразован обратно в стандартные целочисленных значения путем изменения и дополнения XOR-кий с маской, например:

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 номеров, созданных с помощью функции negabinary(), не существует никакого риска переполнения. Однако 64-битные отрицательные числа из других источников могут иметь значение ниже диапазона int64_t, поэтому проверка переполнения становится необходимой.)

+0

Вау! Спасибо за такой подробный и тщательный ответ. Я очень ценю ваши усилия! Начало вашего ответа действительно простое и убедительное, но ** последовательный набор бит ** раздел не так прямо, как я надеялся. Спасибо, в любом случае! –

+1

@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 , Может быть, я должен добавить эту десятичную версию объяснения для ясности? – m69

+0

Я думаю, что сейчас лучше. Еще раз спасибо за все усилия! –

-1

"0xAAAAAAAA" - одно из тех магических чисел, которое содержит последовательность из 10 (двоичных) узоров. Это используется как маска, потому что вы выполняете побитовое XOR-операцию. Когда вы добавляете номер в эту маску и выполняете XOR, на результат будут влиять только те биты, которые предоставляются номером, остальное будет 0 в результате. [Поскольку XOR двух одинаковых битов равен 0]. Наконец, toString (2) преобразует результат в двоичный код

Пример: -> Рассмотрим 3 - ваш номер. Добавить 3 до 2863311530 [который является десятичным представлением 0xAAAAAAAA]. -> XOR сумма (маска + 3) с 0xAAAAAAAA, которая является ... 10101010^.... 10101010. Это дает 111 (так как последние 3 соответствующих битов в маске, а сумма разные) -> Преобразование 111 в двоичной, что 7

+0

Извините, но я не смог найти убедительного аргумента в вашем ответе на вопрос, почему данная функция вычисляет negabinary (а не что-то еще). –

+0

Зачем вы отвечаете за ответ? Вначале ваш вопрос не требовал каких-либо математических доказательств. Я объяснил, «как» функция вычисляет отрицательное представление заданного числа. Не вижу причины для downvoting, поскольку я не упоминал ничего плохого. –