我正在try 将float转换为varbinary,但当我试图将其取回时,我无法获得正确的值.

例如:

select 
    convert(varbinary(max), convert(float, '11000000000000')) -> "0x42A402462F600000"

select 
    convert(varchar(max), convert(varbinary(max), convert(float, '11000000000000'))) -> "B¤F/`"

我做错了什么?

我怎样才能得到我的浮点数转换成二进制后?

推荐答案

Updated. See my recommended solution in Addendum 2 below.

我觉得奇怪的是,SQL Server将允许您将内部浮点表示形式保存为二进制,而不允许您undo撤消该操作.(对我来说没有任何意义).它允许您与几乎所有其他类型进行双向二进制转换,包括各种日期/时间类型.唯一的例外是Text/nText,但它们是过时的指向文本的指针存储类型.

我猜您正在将它与二进制字符串中的其他数据组合在一起,并且需要能够将所有内容映射回其确切的原始值.

我可以提供的唯一替代方法是(1)使用无损转换格式-CONVERT(VARCHAR(MAX), value, 3)转换为VARCHAR,从那里转换为二进制,然后反转该过程以恢复原始浮点值,或者(2)转换为Geometry::Point的X坐标,从那里转换为二进制,并反转该过程(使用geometter.STX属性)以恢复原始浮点值.这两种方法产生的二进制表示比直接转换为二进制要长几倍.

看来你需要 Select "best bad option."

this db<>fiddle章一个Demo

ADDENDUM:

我发现了一种可能更好的方法,即执行从浮点数到二进制到浮点数的往返.

在判断了GEOMETRY::Point(value, 0, 0)的二进制表示之后,我发现它的二进制表示是0x00000000010C...0000000000000000,而...是浮点数的反向二进制表示.使用这些知识,可以获取原始浮点数的二进制表示,构造GEOMETRY::Point的二进制表示,转换为GEOMETRY,并提取原始浮点数值.

以下函数封装了该技术.

CREATE FUNCTION ConvertBinaryToFloat(@FloatBinary VARBINARY(MAX)) RETURNS FLOAT
AS
BEGIN
    IF (@FloatBinary IS NULL OR LEN(@FloatBinary) <> 8) RETURN NULL

    DECLARE @PointBinary VARBINARY(MAX) =
        0x00000000010c
        + CAST(REVERSE(@FloatBinary) as VARBINARY(MAX))
        + 0x0000000000000000
    DECLARE @Point GEOMETRY = @PointBinary
    RETURN @Point.STX
END
-- Float -> binary -> float (using custom function)
DECLARE @F1 FLOAT = ACOS(-1)
DECLARE @B VARBINARY(MAX) = CONVERT(VARBINARY(MAX), @F1)
DECLARE @F2 FLOAT = dbo.ConvertBinaryToFloat(@B)

SELECT @F1, @B, @F2
SELECT CONVERT(VARBINARY(MAX), @F1), CONVERT(VARBINARY(MAX), @F2) -- Confirm identical

这正是CONVERT(FLOAT, @Binary)应该做的事情,如果它受SQL Server支持的话.

this db<>fiddle,它演示了以前的方法和这个新方法.显示了直接编码的转换和封装该转换的函数.

我再次强调,这是在深入研究未记录的表示法,但我认为这些表示法永远不会改变,因为它会 destruct 存储几何图形的所有现有数据库.

ADDENDUM2:

我发现了另一种方法,它直接将二进制文件分解为组件,并使用它们来构建原始的浮点值. 这依赖于IEEE 754双精度值的文档表示.

(此解决方案改编自this question的答案,它对单精度(SQL Server实数)值使用了类似的计算.

CREATE FUNCTION ConvertBinaryToFloat(@Binary VARBINARY(MAX)) RETURNS FLOAT
AS
BEGIN
    -- IEEE 754 Double Precision binary to float
    -- Layout:
    --   64 bits total
    --   1 bit sign
    --   11 bits exponent (excess 1023)
    --   52 bits fractional mantissa  (with implicit leading 1) = 1.xxxxx
    -- Does not support IEEE NaN, +Inf, or -Inf values. 

    IF (@Binary IS NULL OR LEN(@Binary) <> 8) RETURN NULL
    IF (@Binary = 0x0000000000000000) RETURN 0
    IF (@Binary = 0x8000000000000000) RETURN -0e0 -- IEEE Negative 0

    DECLARE @Int64 BIGINT = CAST(@Binary AS BIGINT)
    DECLARE @One FLOAT = 1
    DECLARE @Two FLOAT = 2
    DECLARE @Mantissa FLOAT = @One + (@Int64 & 0x000FFFFFFFFFFFFF) * POWER(@Two, -52)
    DECLARE @Exponent INT = (@Int64 & 0x7ff0000000000000) / 0x0010000000000000 - 1023

    IF (@Exponent = 1024) RETURN NULL -- Unsupported special: Inf, -Inf, NaN
  
    DECLARE @Result FLOAT =  SIGN(@Int64) * @Mantissa * POWER(@Two, @Exponent)
    RETURN @Result
END

从二进制到实数(IEEE单精度)的等效转换为:

CREATE FUNCTION ConvertBinaryToReal(@Binary VARBINARY(MAX)) RETURNS REAL
AS
BEGIN
    -- IEEE 754 Single Precision binary to float
    -- Layout:
    --   32 bits total
    --   1 bit sign
    --   8 bits exponent (excess 127)
    --   23 bits fractional mantissa  (with implicit leading 1) = 1.xxxxx
    -- Does not support IEEE NaN, +Inf, or -Inf values. 

    IF (@Binary IS NULL OR LEN(@Binary) <> 4) RETURN NULL
    IF (@Binary = 0x00000000) RETURN 0
    IF (@Binary = 0x80000000) RETURN -0e0 -- IEEE Negative 0

    DECLARE @Int32 INT = CAST(@Binary AS INT)
    DECLARE @One REAL = 1
    DECLARE @Two REAL = 2
    DECLARE @Mantissa REAL = @One + (@Int32 & 0x007FFFFF) * POWER(@Two, -23)
    DECLARE @Exponent INT = (@Int32 & 0x7f800000) / 0x00800000 - 127
    DECLARE @Result REAL =  SIGN(@Int32) * @Mantissa * POWER(@Two, @Exponent)

    IF (@Exponent = 128) RETURN NULL -- Unsupported special: Inf, -Inf, NaN

    RETURN @Result
END

测试结果(双精度/ SQL Server浮点数):

Value Binary Result ResultBinary Compare CompareBinary
0 0x0000000000000000 0 0x0000000000000000 Equal Identical
0.5 0x3FE0000000000000 0.5 0x3FE0000000000000 Equal Identical
1 0x3FF0000000000000 1 0x3FF0000000000000 Equal Identical
11000000000000 0x42A402462F600000 11000000000000 0x42A402462F600000 Equal Identical
0.333333333333333 0x3FD5555555555555 0.333333333333333 0x3FD5555555555555 Equal Identical
142857142857143 0x42E03DB0A81DB6DB 142857142857143 0x42E03DB0A81DB6DB Equal Identical
1.4142135623731 0x3FF6A09E667F3BCD 1.4142135623731 0x3FF6A09E667F3BCD Equal Identical
3.14159265358979 0x400921FB54442D18 3.14159265358979 0x400921FB54442D18 Equal Identical
2.71828182845905 0x4005BF0A8B145769 2.71828182845905 0x4005BF0A8B145769 Equal Identical
0 0x8000000000000000 0 0x8000000000000000 Equal Identical

测试结果(单精度/SQL Server REAL):

Value Binary Result ResultBinary Compare CompareBinary
0 0x00000000 0 0x00000000 Equal Identical
0.5 0x3F000000 0.5 0x3F000000 Equal Identical
1 0x3F800000 1 0x3F800000 Equal Identical
1.1E+13 0x55201231 1.1E+13 0x55201231 Equal Identical
0.3333333 0x3EAAAAAB 0.3333333 0x3EAAAAAB Equal Identical
1.428571E+14 0x5701ED85 1.428571E+14 0x5701ED85 Equal Identical
1.414214 0x3FB504F3 1.414214 0x3FB504F3 Equal Identical
3.141593 0x40490FDB 3.141593 0x40490FDB Equal Identical
2.718282 0x402DF854 2.718282 0x402DF854 Equal Identical
0 0x80000000 0 0x80000000 Equal Identical

请看这个db<>fiddle的演示.

Sql相关问答推荐

Oracle中的分层查询

为什么postgres中CURRENT_TIMESTAMP的日期与CURRENT_DATE不同?

Android房间fts4匹配语法AND OR

从依赖于其他表的值的XREF表中的值分组获得正确的计数?

通过之前的连接-这是Oracle的错误吗?

按分隔符和总和分析字符串

snowflake/SQL嵌套的JSON对象和数组

正在try 从SQL获取最新的ID和一个唯一名称

带日期函数的复合索引不允许只扫描索引吗?

如何在Postgres中为单值输入多行?

使用递归CTE在BigQuery中获取文件路径

无法访问级联删除导致的触发器中已删除的外键记录

从重复值中获取最新值

在 postgres 中插入或更新 jsonb 数组的对象

使用多个数据库调用重载 CQRS 模式

SQL获取两个日期范围之间的计数

PostgreSQL - 从同一张表中获取值

sql count distinct by column 和 sum false 和 true

从多个连接返回 1 行到同一个表 - SQL Server

为什么这是 AND,OR with NULL 的真值表?