1

大家好,我认为这可能是一个错误,但它正在杀死我。我在 Ubuntu Linux 服务器上使用 MySQL 5.1.41。我正在尝试编写一个函数来创建一个随机签名的 BIGINT 值。因为 RAND() 的精度太小,无法生成所有可能的 BIGINT 值,所以我决定尝试使用位运算符组合四个 32 位字。

我启动了 MySQL Workbench,并尝试了以下操作以查看移位运算符是否可以正确处理负数:

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

0x1ACE - 0x8000 是 -25906,所以如果我向左移动 16 位,我应该乘以 65536,对吗?我得到的答案是 0xFFFFFFFF9ACE0000,它是 -1697775616 或 -25906 * 65536 的有符号表示。Wunderbar,它有效!

所以我的计划是使用它来生成随机符号 BIGINT 的第一个 32 位字,并使用一个简单的循环将另外三个 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,我很累,我越看这些东西,我就越沮丧,我对它的理解就越少. 任何帮助,无论是解释这里发生了什么,还是建议编写这个函数,以便它现在可以始终如一地工作,如果这是一个错误,如果它得到修复,在以后的版本中,将不胜感激!

4

1 回答 1

2

好吧,我想我刚刚想通了。根据文档,位移运算符产生无符号的 64 位整数。所以当你尝试:

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

发生的事情是,为了让 guid << 0 返回一个无符号整数,它试图将 guid 从负符号整数转换为无符号整数,导致 0x7FFFFFFFFFFFFFFF,然后将其移到零位,这是一个身份操作导致相同的 0x7FFFFFFFFFFFFFFF。

但是,乘法 (*) 似乎可以在有符号和无符号数上正常工作。我可以通过以下方式达到我想要的结果:

DECLARE guid BIGINT;        -- signed BIGINT
SET guid = -25924;          -- = 0xFFFFFFFFFFFF9ABC
SET guid = guid * 0x10000;  -- = Result: 0xFFFFFFFF9ABC0000 Woot! \o/
于 2011-06-03T03:58:55.923 回答