1

32番目と64番目のビットの設定には注意が必要です。

32ビットソリューション:

32ビットフィールドで動作するようになりました。秘訣は、POWER関数の戻り値をintにキャストする前にbinary(4)にキャストすることです。最初にbinary(4)にキャストせずに、intに直接キャストしようとすると、32番目のビット(インデックス31)で操作するときに算術オーバーフロー例外が発生します。また、POWERに渡される式が、最大戻り値(2 ^ 31)を格納するのに十分な大きさの型(bigintなど)であることを確認する必要があります。そうしないと、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ビットフィールドにも同様のアプローチを使用するつもりでしたが、式/戻り値にdecimal(38)タイプを使用しているにもかかわらず、POWER関数が不正確な値を返していることがわかりました。例: "select POWER(CAST(2 as decimal(38))、64)"は、正しい値18446744073709551616ではなく、18446744073709552000(最初の16桁のみが正確)を返します。 63乗、その結果はまだ不正確です。

POWER関数のドキュメントには、「お金または数値データ型のいずれかが使用されている場合、floatへの内部変換によって精度が低下する可能性がある」と記載されています。(数値型は機能的に10進数型と同等であることに注意してください)。

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 に答える