BIGINT сдвиг в MySQL

Привет всем, я думаю, это может быть баг, но он меня убивает. Я использую 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 или нет, и все стало действительно странно. Я попытался сделать x без знака и попробовал следующее:

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

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

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

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

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


person King Skippus    schedule 30.05.2011    source источник
comment
Я думаю, что проблема здесь может заключаться в том, что MySql может использовать другой формат данных для внутренних операций (вычитание и смещение), попробуйте писать операции шаг за шагом: вы можете изменить SET x = (0x1ACE - 0x8000) ‹< 0x10; установить x = 0x1ACE; УСТАНОВИТЬ x = x-0x8000; УСТАНОВИТЬ x = x ‹< 0x10; или что-то в этом роде. Или попробуйте ВЫБРАТЬ (0x1ACE - 0x8000) ‹---------------- 0x10 INTO x; синтаксис dev.mysql.com/doc/refman/5.0 /en/select-into-statement.html.   -  person XzKto    schedule 30.05.2011
comment
Никаких кубиков. Когда я разделил его на построчную операцию, все будет правильно, пока я не установлю x = x ‹---------------- 0x10; Он по-прежнему дает мне 0x7FFFFFFFFFFFFFFF. Если я ВЫБЕРАЮ x ‹< 0x10 INTO x; это дает мне тот же результат. Это так неприятно!   -  person King Skippus    schedule 03.06.2011
comment
Хорошо, теперь я просто злюсь. Я просто попробовал выполнить SET guid = 0x1ABC - 0x8000 и получил 0xFFFFFFFFFFFF9ABC. Все идет нормально. Затем, если я выполняю SET guid = guid ‹---------------- 0, я получаю 0x7FFFFFFFFFFFFFFF. Это явно неправильно, и за кулисами творится что-то не кошерное.   -  person King Skippus    schedule 03.06.2011


Ответы (1)


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

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

Что происходит, так это то, что для того, чтобы guid ‹< 0 возвратил целое число без знака, он пытается преобразовать guid из отрицательного целого числа со знаком в целое число без знака, что приводит к 0x7FFFFFFFFFFFFFFF, а затем сдвигает его на ноль. Places, что является операцией идентификации, приводящей к тому же 0x7FFFFFFFFFFFFFFF.

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

DECLARE guid BIGINT;        -- signed BIGINT
SET guid = -25924;          -- = 0xFFFFFFFFFFFF9ABC
SET guid = guid * 0x10000;  -- = Result: 0xFFFFFFFF9ABC0000 Woot! \o/
person King Skippus    schedule 03.06.2011
comment
Спасибо, что опубликовали решение, я подумал об этом, но тогда я не понимаю, почему SELECT HEX ((0x1ACE - 0x8000) ‹---------------- 0x10); работает. - person XzKto; 03.06.2011