【问题标题】:How to cast type bytea to double precision如何将类型 bytea 转换为双精度
【发布时间】:2012-02-21 08:51:42
【问题描述】:

我有一个类型为 bytea 的数据库列。它包含转换为字节数组的浮点数(每个浮点数 4 个字节),编码为 Escape。我可以使用 substring 函数检索相应的 bytea 字符串。

我的问题是如何将 bytea 字符串转换为在 SQL 函数中浮动。早些时候,我在 C# 端转换为浮动。我使用 dataReader.getByte 方法检索字节,然后使用 BitConverter.ToSingle(.Net 内置类)方法转换为浮点数。

现在我不能使用中间组件作为 Npqsql 驱动程序。我希望 SQL 在从 3rd 方应用程序执行查询时直接将 bytea 转换为浮点数并返回相应的数字。

谢谢 阿米拉

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    为此,最好的解决方案是使用 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;
    

    【讨论】:

      【解决方案2】:

      我有相同的任务将存储在 PostgreSQL bytea 字段中的 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;
      

      你还需要一个函数来通过它的数字从 BYTEA 获取 HEX 字节:

      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;
      

      【讨论】:

        猜你喜欢
        • 2018-03-02
        • 1970-01-01
        • 1970-01-01
        • 2015-02-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多