1

设置第 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 位位掩码字段中的最终位?

4

1 回答 1

0

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
于 2010-10-27T18:01:25.123 回答