【问题标题】:Negative Int XOR with Hexidecimal Integer Constant and Decimal Integer will produce Different Results?Negative Int XOR with Hexidecimal Integer Constant 和 Decimal Integer 会产生不同的结果吗?
【发布时间】:2020-09-03 21:41:43
【问题描述】:

我尝试创建一个表“MyTable”并添加一个小 int 类型的字段“field1”。

然后我将负数 -12289 添加到 field1。

然后我执行下面的 SQL 查询:

select field1 ^ 0xcfff from MyTable

结果为零。

但是如果我将十六进制整数常量替换为十进制整数常量,如下:

select field1 ^ 53247 from MyTable

结果是-65536。

为什么?

唯一的线索在https://docs.microsoft.com/en-us/sql/t-sql/data-types/int-bigint-smallint-and-tinyint-transact-sql?view=sql-server-ver15

大于 2,147,483,647 的整数常量被转换为十进制数据类型,而不是 bigint 数据类型。

但是 0xcfff 和 53247 都比 2,147,483,647 小得多。为什么它们会产生不同的结果?

更新:

据我了解,这个问题的关键是我们可以将 0xcfff 转换为 small int,如下所示:

select cast(0xcfff as smallint)

但我们不能将 53247 转换为 small int,以下行会导致溢出:

select cast(53247 as smallint)

这与 C/C++ 不同。在 C/C++ 中,两种转换都可以。

【问题讨论】:

  • 您正在处理不同的数据类型并看到由于类型转换而导致的问题。 0xcfff 不是十六进制常量 - 它是 binary(2) 值。 smallint 数据类型的范围是 -32768 到 32767,因此当 0xcfff 转换为 smallint 时,其十进制等效值为 -12289... 和 -12289 ^ -12289 = 0。值53247 超出smallint 范围,因此至少转换为int,这导致field1 也转换为int 以进行异或运算。将field153247 都转换为int 后,异或结果为-65536
  • @AlwaysLearning,为什么 0xcfff 作为 binary(2) 将被转换为 smallint(-12289),而不是 int,而 53247 将被转换为 int,而不是 smallint(-12289) 当另一个运算符是smallint?
  • 因为smallint 是有符号的16 位值,而0xcfff 是两个字节(16 位)。 53247 不能表示为带符号的 16 位值,因此被强制转换为带符号的 32 位 int。
  • SQL Server 没有无符号数据类型 - 也许这就是您遗漏的线索?
  • @AlwaysLearning,我知道 SQL Server 没有无符号数据类型,但最初认为它支持将 53247 转换为 smallint,就像 C++ 一样。但事实并非如此。请参阅我对帖子的“更新”。

标签: sql-server int hex decimal xor


【解决方案1】:

这是因为您的数据长度。 执行位运算符时应使用相同的字节长度:

1100 1111 1111 1111 <--  -12289 as smallint (word)
1100 1111 1111 1111 <--  CFFF (-12289) (word)
0000 0000 0000 0000 <--  XOR result = 0 (word)

1111 1111 1111 1111 1100 1111 1111 1111 <--  -12289 as int (double word)
0000 0000 0000 0000 1100 1111 1111 1111 <--  53247 (0000CFFF) (double word)
1111 1111 1111 1111 0000 0000 0000 0000 <--  XOR result = -65536 (double word)

你可以尝试改变长度:

select cast(-12289 as int) ^ 0x00cfff, -12289 ^ cast(0x00cfff as int)

【讨论】:

    【解决方案2】:

    您在这里缺少的鲜为人知的是Data Type Precedence。以下是您可以检查它的方法:

    declare @t table (
        Id smallint not null
    );
    
    insert into @t (Id)
    select -12289;
    
    select sq.*,
        sql_variant_property(sq.XBin, 'BaseType') as [BinType],
        sql_variant_property(sq.XDec, 'BaseType') as [DecType]
    from (
        select t.Id,
            t.Id ^ 0xcfff as [XBin],
            t.Id ^ 53247 as [XDec]
        from @t t
    ) sq;
    

    二进制文字 0xcfff 占用 2 个字节,因此可以隐式转换为列本身具有的 smallint 类型。然而,十进制文字被解释为 int(不是因为它需要超过 2 个字节,而是因为 2^32-1 下的 SQL Server always interprets 整数文字具有这种数据类型,而大于 interpreted 的所有内容都为decimal)。这意味着现在必须将列隐式转换为优先级高于smallintint,并且在转换过程中保留其符号。

    【讨论】:

    • 非常感谢。
    猜你喜欢
    • 2017-04-04
    • 1970-01-01
    • 2014-06-15
    • 1970-01-01
    • 1970-01-01
    • 2020-01-24
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    相关资源
    最近更新 更多