БОЛЬШОЙ сдвиг в MySQL - PullRequest
1 голос
/ 30 мая 2011

Привет всем, я думаю, что это может быть ошибкой, но это убивает меня. Я использую MySQL 5.1.41 на сервере Ubuntu Linux. Я пытаюсь написать функцию для создания случайного значения со знаком BIGINT. Поскольку точность RAND () слишком мала для генерации полного диапазона возможных значений BIGINT, я решил попытаться объединить четыре 32-битных слова с помощью битовых операторов.

Я запустил MySQL Workbench и попробовал следующее, чтобы проверить, правильно ли работают операторы сдвига битов с отрицательными числами:

SELECT HEX((0x1ACE - 0x8000) << 0x10);

0x1ACE - 0x8000 - это -25906, поэтому, если я сдвину 16 бит влево, я должен умножиться на 65536, верно? Ответ, который я получил, был 0xFFFFFFFF9ACE0000, который является подписанным представлением -1697775616 или -25906 * 65536. Wunderbar, это работает !!!

Так что мой план состоял в том, чтобы использовать это для генерации первого 32-битного слова со случайным знаком BIGINT и использовать простой цикл для добавления к значению еще трех 32-битных слов, сдвигая биты на четыре байта за раз , Воодушевленно, я начал с добавления следующего кода в мою функцию, используя жестко запрограммированное значение для проверки моего плана:

DECLARE x BIGINT;
SET x = (0x1ACE - 0x8000) << 0x10;

Если я установлю значение так, чтобы смещаемое значение было положительным, все работает нормально. Однако после выполнения этого вычисления со смещенным отрицательным значением (в данном случае -25906) я продолжал получать, что x было 0x7FFFFFFFFFFFFFFF, что является максимальным положительным значением 64-разрядного целого числа со знаком. Я полностью сбит с толку. точно такая же операция генерирует совершенно другой результат в зависимости от того, находится ли она в операции SET в функции или в инструкции SELECT.

Поэтому я начал возиться с тем, подписан ли х или нет, и все стало действительно странно. Я попытался сделать x без знака и попробовал следующее:

DECLARE x BIGINT UNSIGNED;
SET x = (0x1ACE - 0x8000);

Когда я это сделал, я получил х, равный нулю. Не удивительно, поскольку x без знака и результат отрицательный. Однако, на жаворонке, я попробовал это:

DECLARE x BIGINT UNSIGNED;
SET x = (0x1ACE - 0x8000) << 0;

К моему удивлению, для x было установлено значение 0xFFFFFFFFFFFF9ACE!

Может кто-нибудь помочь, пожалуйста? Я часами работал над функцией, которая ничего не делает, кроме как эффективно генерирует случайный подписанный BIGINT, я устал, и чем больше я смотрю на это, тем больше я расстраиваюсь и тем меньше смысла в этом могу понять , Будем весьма благодарны за любую помощь, либо в объяснении того, что здесь происходит, либо в советах по написанию этой функции, чтобы она работала согласованно, и, если это ошибка, в более поздней версии, если она будет исправлена, будет принята с благодарностью!

1 Ответ

2 голосов
/ 03 июня 2011

Хорошо, я думаю, что только что понял это. Согласно документации операторы сдвига битов приводят к беззнаковым 64-разрядным целым числам. Поэтому, когда вы пытаетесь:

DECLARE guid BIGINT;   -- signed BIGINT
SET guid = -25924;     -- = 0xFFFFFFFFFFFF9ABC
SET guid = guid << 0;  -- Result: 0x7FFFFFFFFFFFFFFF

Происходит следующее: для того, чтобы guid << 0 возвратил целое число <strong>без знака , он пытается преобразовать guid из целого числа с отрицательным знаком в целое число без знака, в результате чего получается 0x7FFFFFFFFFFFFFFFF, а затем смещает его через ноль мест, что является идентичной операцией, приводящей к тому же 0x7FFFFFFFFFFFFFFF.

Похоже, что умножение (*) работает правильно для чисел со знаком и без знака. Я могу достичь желаемого результата с помощью следующего:

DECLARE guid BIGINT;        -- signed BIGINT
SET guid = -25924;          -- = 0xFFFFFFFFFFFF9ABC
SET guid = guid * 0x10000;  -- = Result: 0xFFFFFFFF9ABC0000 Woot! \o/
...