Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Delphi Real48 to C# double

Tags:

c#

double

delphi

I need to be able to convert from a Delphi Real48 to C# double.

I've got the bytes I need to convert but am looking for an elegant solution. to the problem.

Anybody out there had to do this before?

I'm needing to do the conversion in C#

Thanks in advance

like image 216
mat-mcloughlin Avatar asked Mar 24 '10 10:03

mat-mcloughlin


3 Answers

I've done some hunting around and I found some C++ code to do the job, converted it and it seems to be giving the right answer... damned if I understand it all though :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);
    }

If somebody can explain it that would be great :D

like image 192
mat-mcloughlin Avatar answered Oct 19 '22 01:10

mat-mcloughlin


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);
}
like image 28
Darin Dimitrov Avatar answered Oct 19 '22 01:10

Darin Dimitrov


Appreciate this is an old post, but also the following may be useful for those looking to do this in T-SQL (which I was).

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

Confirmation

0.125 is 0x 0000 0000 007E (or 0x 7E00 0000 0000 reversed)

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

The input is a char12 as I had to extract the binary from the middle of 2 other larger binary fields and shunt them together so had it already as char12. Easy enough to change to be binary(6) input if don't need to do any manipulation beforehand.

As an aside, in the scenario I'm implementing into, the T-SQL variant is outperformed by C# CLR code so the C# code above may be better. Whilst not everywhere allows CLR code into SQL Server if you can then maybe you should. For more background an article at http://www.simple-talk.com/sql/t-sql-programming/clr-performance-testing/ does some in depth measurement which shows some dramatic differences between T-SQL and CLR.

like image 42
Simon Molloy Avatar answered Oct 18 '22 23:10

Simon Molloy