Как читать / записывать полные 32 или 64 бита поля int или bigint в TSQL - PullRequest
1 голос
/ 27 октября 2010

Установка 32-го и 64-го битов сложная задача.

32-битный Решение:

Я получил его для работы с 32-битными полями.Хитрость заключается в приведении возвращаемого значения функции POWER к двоичному (4) перед преобразованием его в int.Если вы попытаетесь привести непосредственно к int, без предварительного преобразования к двоичному (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 возвращает неточные значения, несмотря на использование десятичного числа (38) тип для выражения / возвращаемого значения.Например: «select POWER (CAST (2 as decimal (38)), 64)» возвращает 18446744073709552000 (точны только первые 16 цифр), а не правильное значение 18446744073709551616. И даже если бы я поднял только 2 до63-й степени, этот результат все еще неточен.

В документации функции POWER указано, что «внутреннее преобразование в число с плавающей запятой может привести к потере точности, если используются денежные или числовые типы данных».(обратите внимание, что числовой тип функционально эквивалентен десятичному типу).

Я думаю, что единственный способ правильно обработать 64-битные поля - это работать с их 32-битными половинками, но это требует дополнительной проверки @Свойство bitindex, чтобы увидеть, с какой половиной мне нужно работать.Существуют ли какие-либо встроенные функции или более эффективные способы явной установки этих конечных битов в 32-битных и 64-битных битовых полях в TSQL?

1 Ответ

0 голосов
/ 27 октября 2010

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
...