将Delphi Real48转换为C#double

时间:2010-03-24 10:41:50

标签: c# delphi double

我需要能够从Delphi Real48转换为C#double。

我有需要转换的字节,但我正在寻找一个优雅的解决方案。问题。

以前有人必须这样做吗?

我需要在C#中进行转换

提前致谢

5 个答案:

答案 0 :(得分:8)

我已经做了一些狩猎,我发现了一些C ++代码来完成这项工作,转换它并且它似乎正在给出正确的答案......如果我理解了这一切,该死的:S

    private static double Real48ToDouble(byte[] real48)
    {

        if (real48[0] == 0)
            return 0.0; // Null exponent = 0

        double exponent = real48[0] - 129.0;
        double mantissa = 0.0;

        for (int i = 1; i < 5; i++) // loop through bytes 1-4
        {
            mantissa += real48[i];
            mantissa *= 0.00390625; // mantissa /= 256
        }


        mantissa += (real48[5] & 0x7F);
        mantissa *= 0.0078125; // mantissa /= 128
        mantissa += 1.0;

        if ((real48[5] & 0x80) == 0x80) // Sign bit check
            mantissa = -mantissa;

        return mantissa * Math.Pow(2.0, exponent);
    }

如果有人可以解释那会很棒:D

答案 1 :(得分:3)

static double GetDoubleFromBytes(byte[] bytes)
{
    var real48 = new long[6];
    real48[0] = bytes[0];
    real48[1] = bytes[1];
    real48[2] = bytes[2];
    real48[3] = bytes[3];
    real48[4] = bytes[4];
    real48[5] = bytes[5];

    long sign = (real48[0] & 0x80) >> 7;

    long significand = 
        ((real48[0] % 0x80) << 32) + 
         (real48[1] << 24) + 
         (real48[2] << 16) + 
         (real48[3] << 8) + 
         (real48[4]);

    long exponent = bytes[5];

    if (exponent == 0)
    {
        return 0.0;
    }

    exponent += 894;
    long bits = (sign << 63) + (exponent << 52) + (significand << 13);
    return BitConverter.Int64BitsToDouble(bits);
}

答案 2 :(得分:2)

感谢这是一篇旧文章,但对于那些希望在T-SQL中实现这一目标的人来说,以下内容可能也很有用。(

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ifn_HexReal48ToFloat]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
    drop function [dbo].[ifn_HexReal48ToFloat]
go

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

create function [dbo].[ifn_HexReal48ToFloat]
(
    @strRawHexBinary    char(12),       -- NOTE. Do not include the leading 0x
@bitReverseBytes    bit 
)
RETURNS FLOAT
AS
BEGIN

-- Reverse bytes if required
-- e.g. 3FF4 0000 0000 is stored as
--      0000 0000 F43F
declare @strNewValue    varchar(12)
if @bitReverseBytes = 1
begin   
    set @strNewValue='' 
    declare @intCounter int
    set @intCounter = 6

    while @intCounter>=0
    begin
        set @strNewValue = @strNewValue + substring(@strRawHexBinary, (@intCounter * 2) + 1,2) 
        set @intCounter = @intCounter - 1
    end 
end

-- Convert the raw string into a binary
declare @binBinaryFloat binary(6)
set @binBinaryFloat = convert(binary(6),'0x' + isnull(@strNewValue, @strRawHexBinary),1)

-- Based on original hex to float conversion at http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=81849
-- and storage format documented at 
-- http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/internaldataformats_xml.html
-- Where, counting from the left
-- Sign         = bit 1
-- Exponent     = bits 41 - 48      with a bias of 129
-- Fraction     = bits 2 - 40


return

    SIGN
    (
        CAST(@binBinaryFloat AS BIGINT)
    )
    * 
    -- Fraction part. 39 bits. From left 2 - 40. 
    (
        1.0 + 
        (CAST(@binBinaryFloat AS BIGINT) & 0x7FFFFFFFFF00) * POWER(CAST(2 AS FLOAT), -47)
)
* 
    -- Exponent part. 8 bits. From left bits 41 -48
    POWER
    (
        CAST(2 AS FLOAT), 
        (
            CAST(@binBinaryFloat AS BIGINT) & 0xff
            - 129 
        ) 
    )

end

确认

0.125是0x 0000 0000 007E(或0x 7E00 0000 0000反转)

select dbo.ifn_HexReal48ToFloat('00000000007E', 0)
select dbo.ifn_HexReal48ToFloat('7E0000000000', 1) 

输入是一个char12,因为我必须从其他2个较大的二进制字段中间提取二进制文件并将它们分流在一起,所以它已经作为char12。如果不需要事先进行任何操作,则很容易变为二进制(6)输入。

顺便说一句,在我实现的场景中,T-SQL变体的性能优于C#CLR代码,因此上面的C#代码可能更好。虽然不是在任何地方都允许CLR代码进入SQL Server,如果你可以,那么也许你应该。有关更多背景信息,http://www.simple-talk.com/sql/t-sql-programming/clr-performance-testing/的文章进行了一些深度测量,显示了T-SQL和CLR之间的一些显着差异。

答案 3 :(得分:1)

我一直在测试这个并且发现了一个错误(正如其他人注意到的)带有负值。这是我测试的代码版本。我用120,530个不同的随机值测试了这个值,范围从11,400,000.00到-2,000,000.00

 //This seems to be the layout of the Real48 bits where
        //E = Exponent
        //S = Sign bit
        //F = Fraction

        //EEEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF SFFFFFFF
        //12345678 12345678 12345678 12345678 12345678 12345678


        Double exponentbase = 129d;  // The exponent is offest by 129
        Double exponent = real48[0] - exponentbase; // deduct the offest. 

        // Calculate the mantissa 
        Double mantissa = 0.0;
        Double value = 1.0;

        // For Each Byte. 
        for (int iByte = 5; iByte >= 1; iByte--)
        {
            int startbit = 7;
            if (iByte == 5)
            { startbit = 6; } //skip the sign bit. 

            //For Each Bit 
            for (int iBit = startbit; iBit >= 0; iBit--)
            {
                value = value / 2;// Each bit is worth half the next bit but we're going backwards. 
                if (((real48[iByte] >> iBit) & 1) == 1) //if this bit is set. 
                {
                    mantissa += value; // add the value. 
                }

            }
        }

        if (mantissa == 1.0 && real48[0] == 0) // Test for null value 
            return 0.0;

        double result;

        result = (1 + mantissa) * Math.Pow(2.0, exponent);

        if ((real48[5] & 0x80) == 0x80) // Sign bit check 
            result = -result;

        return result;

答案 4 :(得分:0)

我已将您发布的代码更改为更易读的格式,以便您了解其工作原理:

        Double exponentbase = 129d;
        Double exponent = real48[0] - exponentbase; // The exponent is offest so deduct the base.

        // Now Calculate the mantissa
        Double mantissa = 0.0;
        Double value = 1.0;
        // For Each Byte.
        for (int i = 5; i >= 1; i--)
        {
            int startbit = 7;
            if (i == 5)
            { startbit = 6; } //skip the sign bit.

            //For Each Bit
            for (int j = startbit; j >= 0; j--)
            {
                value = value / 2;// Each bit is worth half the next bit but we're going backwards.
                if (((real48[i] >> j) & 1) == 1) //if this bit is set.
                {
                    mantissa += value; // add the value.
                }

            }
        }

        if (mantissa == 1.0 && real48[0] == 0) // Test for null value
            return 0.0;

        if ((real48[5] & 0x80) == 1) // Sign bit check
            mantissa = -mantissa;

        return (1 + mantissa) * Math.Pow(2.0, exponent);