【问题标题】:How to read/write full 32 or 64 bits of an int or bigint bitmasked field in TSQL如何在 TSQL 中读取/写入完整的 32 位或 64 位 int 或 bigint 位掩码字段
【发布时间】:2010-10-27 17:50:30
【问题描述】:

设置第 32 位和第 64 位很棘手。

32位解决方案:

我让它适用于 32 位字段。诀窍是在将 POWER 函数的返回值转换为 int 之前将其转换为 binary(4)。如果您尝试直接转换为 int,而不先转换为 binary(4),则在对第 32 位(索引 31)进行操作时会出现算术溢出异常。此外,您必须确保传递给 POWER 的表达式具有足够大的类型(例如 bigint)以存储最大返回值 (2^31),否则 POWER 函数将引发算术溢出异常。

CREATE FUNCTION [dbo].[SetIntBit] 
(
    @bitfieldvalue int,
    @bitindex int, --(0 to 31)
    @bit bit --(0 or 1)
)
RETURNS int
AS
BEGIN
    DECLARE @bitmask int = CAST(CAST(POWER(CAST(2 as bigint),@bitindex) as binary(4)) as int);
    RETURN
    CASE
        WHEN @bit = 1 THEN (@bitfieldvalue | @bitmask)
        WHEN @bit = 0 THEN (@bitfieldvalue & ~@bitmask)
        ELSE @bitfieldvalue --NO CHANGE
    END
END

64位问题:

我打算对 64 位字段使用类似的方法,但是我发现 POWER 函数返回的值不准确,尽管表达式/返回值使用了 decimal(38) 类型。 例如:“select POWER(CAST(2 as decimal(38)), 64)”返回 18446744073709552000(只有前 16 位数字是准确的)而不是 18446744073709551616 的正确值。即使我只将 2 提高到63次方,这个结果还是不准确的。

POWER 函数的文档指出“如果使用货币或数字数据类型,则内部转换为浮点数会导致精度损失。” (注意数字类型在功能上等同于十进制类型)。

我认为正确处理 64 位字段的唯一方法是对 32 位字段进行操作,但这需要对 @bitindex 属性进行额外检查,以查看我需要对哪一半进行操作。是否有任何内置函数或更好的方法来显式设置 TSQL 中 32 位和 64 位位掩码字段中的最终位?

【问题讨论】:

  • 我想得越多...一个更通用和可靠的解决方案是使用“二进制”类型列(而不是 int 或 bigint)作为位域,其函数对字节进行操作- 二进制字段的大小。
  • 再一次,是否可以在不读取和重写整个字段的情况下更改大型二进制字段中的单个位(或字节)?如果我正在存储位图并想要切换单个位或字节怎么办?编辑:显然是的,这是可能的......msdn.microsoft.com/en-us/library/a1904w6t%28VS.80%29.aspx

标签: tsql 64-bit math bit-fields bitmask


【解决方案1】:

64位解决方案:

到目前为止,对于我自己的问题,我能想到的最简单的解决方案是为第 64 位(即 2^63)位掩码的有问题的计算添加一个例外情况,其中位掩码值是硬编码的,因此它不必由 POWER 计算。据我所知,POWER 可以准确计算 2^62 和更小的值。

CREATE FUNCTION [dbo].[SetBigIntBit] 
(
    @bitfieldvalue bigint,
    @bitindex int, --(0 to 63)
    @bit bit --(0 or 1)
)
RETURNS bigint
AS
BEGIN
    DECLARE @bitmask bigint = case WHEN @bitindex = 63 THEN CAST(0x8000000000000000 as bigint)
    ELSE CAST(CAST(POWER(CAST(2 as bigint),@bitindex) as binary(8)) as bigint)
    RETURN
    CASE
        WHEN @bit = 1 THEN (@bitfieldvalue | @bitmask)
        WHEN @bit = 0 THEN (@bitfieldvalue & ~@bitmask)
        ELSE @bitfieldvalue --NO CHANGE
    END
END

编辑:这是一些测试上述功能的代码...

declare @bitfield bigint = 0;
print @bitfield;
declare @bitindex int;
set @bitindex = 0;
while @bitindex < 64
begin
  set @bitfield = tutor.dbo.SetBigIntBit(@bitfield,@bitindex,1);
  print @bitfield;  
  set @bitindex = @bitindex + 1;
end
set @bitindex = 0;
while @bitindex < 64
begin
  set @bitfield = tutor.dbo.SetBigIntBit(@bitfield,@bitindex,0);
  print @bitfield;  
  set @bitindex = @bitindex + 1;
end

【讨论】:

    猜你喜欢
    • 2021-07-24
    • 2012-10-26
    • 2011-12-15
    • 2019-01-07
    • 1970-01-01
    • 2011-10-24
    • 2018-08-04
    • 2011-08-14
    • 1970-01-01
    相关资源
    最近更新 更多