Как привести тип bytea к двойной точности - PullRequest
2 голосов
/ 21 февраля 2012

У меня есть столбец базы данных, тип которого bytea.Он содержит числа с плавающей точкой, преобразованные в виде байтового массива (4 байта на одно число с плавающей запятой), а кодировка Escape.Я могу получить соответствующую строку байтов, используя функцию подстроки.

Мой вопрос заключается в том, как преобразовать строку байтов в плавающую внутри функции SQL.Ранее я перешел на плавание в сторону C #.Я использовал метод dataReader.getByte для извлечения байтов, а затем преобразовал их в плавающее с использованием метода BitConverter.ToSingle (.Net build in class).

Теперь я не могу использовать промежуточный компонент в качестве драйвера Npqsql.Я хочу, чтобы SQL напрямую конвертировал bytea в числа с плавающей точкой и возвращал соответствующее число при выполнении запроса из стороннего приложения.

Спасибо, Амила

Ответы [ 2 ]

3 голосов
/ 26 июля 2012

Для этой цели наилучшим из возможных решений является преобразование в байты с использованием стандарта IEEE754-1985 с использованием команд SQL.

Сначала необходимо проверить наличие особых случаев, определенных стандартом IEEE754-1985.Затем просто следуйте стандартному алгоритму для преобразования, если это не происходит в каких-либо особых случаях.Пример кода приведен ниже.

Входные данные bytea_value bytea, is_little_endian boolean, затем разделите их на 4 байта, как показано ниже:

  byte_array[0]:= get_byte(bytea_value, 0);
  byte_array[1]:= get_byte(bytea_value, 1);
  byte_array[2]:= get_byte(bytea_value, 2);
  byte_array[3]:= get_byte(bytea_value, 3);

Затем получите двоичное значение, учитывая младший или большой порядок байтов

IF is_little_endian THEN
        binary_value:= byte_array[0]::bit(8) || byte_array[1]::bit(8) || byte_array[2]::bit(8) || byte_array[3]::bit(8);
    ELSE
        binary_value:= byte_array[3]::bit(8) || byte_array[2]::bit(8) || byte_array[1]::bit(8) || byte_array[0]::bit(8); 
    END IF;

Теперь проверьте наличие особых случаев:

IF binary_value = '00000000000000000000000000000000' OR binary_value = '10000000000000000000000000000000' THEN -- IEEE754-1985 Zero
        return 0.0;
    END IF;

sign := substring(binary_value from 1 for 1);
    exponent := substring(binary_value from 2 for 8);
    mantissa := substring(binary_value from 10 for 23); 

    IF exponent = '11111111' THEN
        IF mantissa = '00000000000000000000000' THEN   -- IEEE754-1985 negative and positive infinity
            IF sign = '1' THEN                    
                return '-Infinity';                    
            ELSE                    
                return 'Infinity';  
            END IF;                  
        ELSE
          return 'NaN'; -- IEEE754-1985 Not a number
        END IF; 
    END IF;

Если оно не относится к каким-либо особым случаям, просто преобразуйте его, как показано ниже:

exp := exponent::int;

    IF exp > 126 THEN
     exp := exp - 127;
    ELSE
     exp:= -exp;
    END IF;

    WHILE mantissa_index < 24 LOOP
        IF substring(mantissa from mantissa_index for 1) = '1' THEN
            result := result + power(2, -(mantissa_index));
        END IF;
        mantissa_index = mantissa_index + 1;
    END LOOP;

    result := result * power(2, exp);

    IF(sign = '1') THEN
        result = -result;
    END IF;

    return result;
1 голос
/ 02 октября 2018

У меня та же задача по преобразованию данных из слоя HW, хранящихся в байтовом поле PostgreSQL, в число с плавающей точкой одинарной точности 32-битной ширины (IEEE 754) в дружественные PostgreSQL данные.Решено с помощью предыдущего ответа с небольшими исправлениями.

Поскольку я не нахожу лучшего рабочего решения, я публикую свой результат:

CREATE OR REPLACE FUNCTION public.get_bytea_to_double(b bytea, offs int)
  RETURNS double precision AS
$BODY$
DECLARE
barray0 bit(8);
barray1 bit(8);
barray2 bit(8);
barray3 bit(8);
binary_value bit(32);
sign character(1);
exponent bit(8);
exp smallint;
mantissa bit(23);
mantissa_index int;
result double precision;
BEGIN

barray0:= get_byte_n(b,offs+0)::bit(8);
barray1:= get_byte_n(b,offs+1)::bit(8);
barray2:= get_byte_n(b,offs+2)::bit(8);
barray3:= get_byte_n(b,offs+3)::bit(8);

--true endian assemble
binary_value:= barray3 || barray2 || barray1 || barray0;
--RAISE NOTICE 'BINVAL:%', binary_value;

IF binary_value = '00000000000000000000000000000000' OR binary_value = '10000000000000000000000000000000' THEN -- IEEE754-1985 Zero
   return 0.0;
END IF;

sign := substring(binary_value from 1 for 1);
exponent := substring(binary_value from 2 for 8);
mantissa := substring(binary_value from 10 for 23);

--RAISE NOTICE 'MANTISSA-BIT:%', mantissa;
--RAISE NOTICE 'EXP-BIT:%', exponent;

IF exponent = '11111111' THEN
   IF mantissa = '00000000000000000000000' THEN   -- IEEE754-1985 negative and positive infinity
      IF sign = '1' THEN
         return '-Infinity';
      ELSE
         return 'Infinity';
      END IF;
   ELSE
      return 'NaN'; -- IEEE754-1985 Not a number
   END IF;
END IF;

exp := exponent::int;
--RAISE NOTICE 'EXP:%', exp;

IF exp > 126 THEN
   exp := exp - 127;
ELSE
   exp:= -exp;
END IF;

result:=1.0;
mantissa_index:=1;
WHILE mantissa_index < 24 LOOP
   IF substring(mantissa from mantissa_index for 1) = '1' THEN
      result := result + power(2, -(mantissa_index))::double precision;
   END IF;
   mantissa_index = mantissa_index + 1;
END LOOP;

result := result * power(2, exp)::double precision;

IF (sign = '1') THEN
   result = -result;
END IF;

return result;

END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

И вам нужна еще одна функция для получения HEX-байтов изBYTEA по номеру:

CREATE OR REPLACE FUNCTION public.get_byte_n(
    bytea,
    integer)
  RETURNS integer AS
$BODY$
           declare r int;
           declare t text;            
           begin 
             t:=encode(substring($1 from (2*$2)+1 for 2),'escape');
             execute E'select x\''||t|| E'\'::integer' into r; 
             return r; 
           end
           $BODY$
  LANGUAGE plpgsql IMMUTABLE
  COST 100;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...