Конвертировать Delphi Real48 в C # double - PullRequest
7 голосов
/ 24 марта 2010

Мне нужно иметь возможность преобразовать Delphi Real48 в C # double.

У меня есть байты, которые мне нужно преобразовать, но я ищу элегантное решение. к проблеме.

Кто-нибудь должен был делать это раньше?

Мне нужно сделать преобразование в C #

Заранее спасибо

Ответы [ 5 ]

8 голосов
/ 24 марта 2010

Я немного поохотился и нашел немного кода на С ++, который выполнил эту работу, преобразовал его и, кажется, дает правильный ответ ... черт, если я все это понимаю, хотя:

    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

3 голосов
/ 24 марта 2010
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 голосов
/ 11 ноября 2011

Примите во внимание, что это старый пост, но также может быть полезно следующее для тех, кто хочет сделать это в 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, так как мне пришлось извлечь двоичный файл из середины двух других больших двоичных полей и смешать их вместе, так что он уже был как char12. Достаточно легко изменить на двоичный (6) ввод, если не нужно предварительно выполнять какие-либо манипуляции.

Кроме того, в сценарии, который я реализую, вариант T-SQL превосходит код C # CLR, поэтому приведенный выше код C # может быть лучше. Хотя не везде позволяет CLR-код в SQL Server, если вы можете, возможно, вам следует. Для получения дополнительной информации в статье http://www.simple -talk.com / sql / t-sql-программирование / clr-performance-Testing / проведено некоторое углубленное измерение, которое показывает некоторые существенные различия между T-SQL и CLR .

1 голос
/ 20 апреля 2012

Я проверял это и обнаружил ошибку (как заметили другие) с отрицательными значениями. Вот моя проверенная версия кода. Я проверил это с 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;
0 голосов
/ 24 марта 2010

Я изменил код, который вы разместили, в более читаемый формат, чтобы вы могли увидеть, как он работает:

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