Воссоздание побитовой обработки JS в Python3 - PullRequest
3 голосов
/ 08 марта 2019

Мне нужно перевести хэш-функцию с JavaScript на Python.

Функция выглядит следующим образом:

function getIndex(string) {
        var length = 27;
        string = string.toLowerCase();
        var hash = 0;
        for (var i = 0; i < string.length; i++) {
                hash = string.charCodeAt(i) + (hash << 6) + (hash << 16) - hash;
        }
        var index = Math.abs(hash % length);
        return index;
}

console.log(getIndex(window.prompt("Enter a string to hash")));

Эта функция объективно верна ™. Это само совершенство. Я не могу изменить это, я просто должен воссоздать это. Что бы он ни выводил, мой скрипт на Python также должен выводить.

Однако - у меня есть пара проблем, и я думаю, что все это связано с тем, как два языка обрабатывают целые числа со знаком.

Битовые операторы JS обрабатывают свои операнды как последовательность из 32 битов. Python, однако, не имеет концепции ограничения битов и просто продолжает действовать как абсолютный безумный. Я думаю, что это одно существенное различие между двумя языками.

Я могу ограничить длину hash в Python, маскируя ее до 32 битов с помощью hash & 0xFFFFFFFF.

Я также могу отрицать hash, если он выше 0x7FFFFFFF с hash = hash ^ 0xFFFFFFFF (или hash = ~hash - они оба, кажется, делают одно и то же). Я считаю, что это имитирует отрицательные числа.

Я применяю оба эти ограничения к хешу с помощью функции t.

Вот мой код на Python:

def nickColor(string):
    length = 27

    def t(x):
        x = x & 0xFFFFFFFF
        if x > 0x7FFFFFFF:
            x = x ^ 0xFFFFFFFF
        return x

    string = string.lower()
    hash = t(0)
    for letter in string:
        hash = t(hash)
        hash = t(t(ord(letter)) + t(hash << 6) + t(hash << 16) - t(hash))
    index = hash % length
    return index

Кажется, он работает до тех пор, пока хэш не должен стать отрицательным, после чего два сценария расходятся. Это обычно происходит около 4 букв в строке.

Я предполагаю, что моя проблема заключается в воссоздании отрицательных чисел JS в Python. Как я могу сказать пока этой проблеме?

1 Ответ

4 голосов
/ 13 марта 2019

Вот рабочий перевод:

def nickColor(string):
    length = 27

    def t(x):
        x &= 0xFFFF_FFFF
        if x > 0x7FFF_FFFF:
            x -= 0x1_0000_0000
        return float(x)

    bytes = string.lower().encode('utf-16-le')
    hash = 0.0
    for i in range(0, len(bytes), 2):
        char_code = bytes[i] + 256*bytes[i+1]
        hash = char_code + t(int(hash) << 6) + t(int(hash) << 16) - hash
    return int(hash % length if hash >= 0 else abs(hash % length - length))

Дело в том, что только сдвиги (<<) вычисляются как 32-разрядные целочисленные операции, их результат конвертируется обратно в двойное до ввода сложений и вычитаний.Я не знаком с правилами представления чисел с плавающей запятой двойной точности на двух языках, но можно предположить, что на всех персональных вычислительных устройствах и веб-серверах он одинаков для обоих языков, а именно IEEE двойной точности754 .Для очень длинных строк (тысячи символов) хеш может потерять некоторые биты точности, что, конечно, влияет на конечный результат, но в JS точно так же, как и в Python (не то, что предполагал автор функции Objectively Correct ™, нотак оно и есть…).Последняя строка исправляет различные определения оператора % для отрицательных операндов в JavaScript и Python .

Более того (спасибо Марку Рэнсому за напоминание об этом), чтобы полностью эмулировать JavaScript, также необходимо учитывать его кодировку UTF-16, но с суррогатными парами , обработанными так, как если бы они состояли из 2 символов.Кодируя строку как utf-16-le, вы гарантируете, что первый байт в каждом 16-разрядном «слове» является наименее значимым, плюс вы не получаете BOM , который вы получили бы, если бы использовалиutf-16 Tout Court (спасибо, Мартин Питерс).

...