2010-03-24 86 views
6

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

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

那里的任何人都必须这样做吗?

,我需要做的转换在C#

在此先感谢

+2

人们仍在使用Real48?为什么?! – 2010-03-24 10:44:11

+0

你需要将它们转换成什么?在Delphi程序中? – 2010-03-24 10:45:29

+3

@Ignacio:想到向后兼容性。 – 2010-03-24 10:46:19

回答

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

+0

字节1到5以科学计数法表示数字的小数部分:1.x * 2^e'。尾数是1.x。 * for *循环和下面两行生成* x *。假设字节1是0xa5。在二进制中,这是10100101.添加到'尾数'得到'尾数== 0xa5'。然后*将这些字节向下移动到小数部分以获得二进制值0.10100101。移位8除以256.重复字节2到4.字节5是特殊的,因为我们只需要7位 - 第八位是符号位 - 所以用128来代替。最后加1,因为那部分是* implicit *(不存储在任何地方)。 – 2010-03-24 19:45:41

+0

字节0是指数。这是一个无符号数字,但它有129偏高,所以首先要做的是正确的偏见。正如在前面的评论中提到的那样,数字的形式是'1.x * 2^e',其中'1.x'存储在'尾数'中,'e'存储在'exponent'中。最后一行代码简单地将该值计算为double。 – 2010-03-24 19:49:49

+2

对未来读者的注意:我相当确信这段代码有错误。首先,它忽略了real48的字节值[4]。建议谨慎。 – 2011-02-05 18:39:50

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); 
} 
+0

从Delphi基础知识:“Real48:Obsolete - 具有最高容量和精度的浮点类型。”在Delphi的扩展版本(10字节) – 2010-03-24 11:00:01

+0

@Darin的现代版本中,恐怕这似乎没有给出正确的答案 – 2010-03-24 11:10:22

+0

事实上,似乎有什么问题。我会再检查一次。 – 2010-03-24 11:18:02

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); 
+0

此代码至少引入一个错误。你忘了用负面的输入来测试它。 – 2010-03-24 19:34:37

+0

他正等着你为他测试Rob。谢谢! – Pauk 2010-03-25 10:34:06

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之间的一些显着差异。

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;