MySQL 中的 BIGINT 移位
嘿大家,我认为这可能是一个错误,但它正在杀了我。我在 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 为 0x7FFFFFFFFFFFFFFFF,这是有符号 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 之外什么也没做,我很累,而且我看这个东西越多,我就越沮丧,我对它的理解就越少。任何帮助,无论是解释这里发生的事情还是编写此函数的建议,以便它现在始终有效,如果这是一个错误,如果它在以后的版本中得到修复,将不胜感激!
Hey all, I think this might be a bug, but it's killing me. I am using MySQL 5.1.41 on an Ubuntu Linux server. I'm trying to write a function to create a random signed BIGINT value. Because the precision of RAND() is too small to generate the full range of possible BIGINT values, I decided to try to combine four 32-bit words using bit operators.
I fired up MySQL Workbench, and tried the following to see if bit shift operators work correctly with negative numbers:
SELECT HEX((0x1ACE - 0x8000) << 0x10);
0x1ACE - 0x8000 is -25906, so if I shift 16 bits to the left, I should be multiplying by 65536, right? The answer I got back was 0xFFFFFFFF9ACE0000, which is the signed representation of -1697775616, or -25906 * 65536. Wunderbar, it works!!!
So my plan was to use that to generate the first 32-bit word of the random signed BIGINT, and use a simple loop to add three more 32-bit words to the value, shifting the bits over four bytes at a time. Excitedly, I started out by putting the following code in my function, using a hard-coded value to test my plan with:
DECLARE x BIGINT;
SET x = (0x1ACE - 0x8000) << 0x10;
If I set the value so that the value being shifted is positive, everything works fine. However, after performing this calculation with a shifted negative value (-25906 in this case), I kept getting that x was 0x7FFFFFFFFFFFFFFF, which is the maximum positive value of a signed 64-bit integer. I'm totally baffled. The exact same operation is generating a totally different result depending on whether it is in a SET operation in a function or a SELECT statement.
So I started messing around with whether x was signed or unsigned and things got really weird. I tried making x unsigned and tried the following:
DECLARE x BIGINT UNSIGNED;
SET x = (0x1ACE - 0x8000);
When I did that, I got x equal to zero. Not surprising, since x is unsigned and the result is negative. However, on a lark, I tried this:
DECLARE x BIGINT UNSIGNED;
SET x = (0x1ACE - 0x8000) << 0;
To my surprise, x was set to 0xFFFFFFFFFFFF9ACE!
Can someone please help? I've been working for hours on a function that does nothing more than efficiently generates a random signed BIGINT, I'm tired, and the more I look at this stuff, the more frustrated I get and the less sense I can make of it. Any help, either in explaining what's going on here or advice in writing this function so that it consistently works now and, if this is a bug, in later version if it gets fixed, would be greatly appreciated!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我想我刚刚明白了。根据文档,该位移位运算符产生无符号 64 位整数。因此,当您尝试时:
发生的情况是为了 guid << 0 返回一个无符号整数,它试图将 guid 从负符号整数转换为无符号整数,结果为 0x7FFFFFFFFFFFFFFF,然后将其移至零位,这是一个恒等运算,结果相同0x7FFFFFFFFFFFFFFF。
然而,乘法 (*) 对于有符号数和无符号数似乎都能正确工作。我可以通过以下方式达到我想要的结果:
Okay, I think I just figured it out. According to the documentation, the bit shift operators result in unsigned 64-bit integers. So when you try:
What is happening is that in order for guid << 0 to return an unsigned integer, it is trying to convert guid from a negative signed integer to an unsigned integer, resulting in 0x7FFFFFFFFFFFFFFF, then shifting it over zero places, which is an identity operation resulting in the same 0x7FFFFFFFFFFFFFFF.
It appears that multiplication (*), however, works correctly on signed and unsigned numbers. I can achieve my desired result by the following: