十六进制整数常量和十进制整数的负整数异或会产生不同的结果吗?

Negative Int XOR with Hexidecimal Integer Constant and Decimal Integer will produce Different Results?

我尝试创建一个 table 'MyTable' 并添加一个字段 "field1" with small int type.

然后我给field1加上一个负数-12289。

然后我执行以下 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

Integer constants greater than 2,147,483,647 are converted to the decimal data type, not the bigint data type.

但 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++ 中,两个转换都可以。

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

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)

您在这里遗漏的鲜为人知的一点是 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 个字节,而是因为 SQL 服务器 always interprets integer literals under 2^32-1 as having this data type, and everything greater interpreteddecimal)。这意味着现在该列必须隐式转换为优先级高于 smallintint,并且在转换期间保留其符号。