2010-03-24 77 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;