2010-03-24 42 views
6

Tôi cần để có thể chuyển đổi từ một Delphi Real48 thành C# double.Chuyển đổi Delphi Real48 thành C# double

Tôi có các byte tôi cần chuyển đổi nhưng tôi đang tìm kiếm giải pháp thanh lịch. cho vấn đề.

Có ai ngoài kia phải làm điều này trước đây không?

Tôi cần phải làm việc chuyển đổi trong C#

Cảm ơn trước

+2

Mọi người vẫn đang sử dụng Real48? TẠI SAO?! –

+0

Bạn cần chuyển đổi chúng ở đâu? Trong một chương trình Delphi? –

+3

@Ignacio: Khả năng tương thích ngược sẽ xuất hiện trong đầu bạn. –

Trả lời

8

tôi đã thực hiện một số săn bắn xung quanh và tôi tìm thấy một số C++ code để thực hiện công việc, chuyển đổi nó và nó dường như được đưa ra câu trả lời đúng ... nguyền rủa nếu tôi hiểu nó tất cả mặc dù: 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); 
    } 

Nếu ai đó có thể giải thích nó sẽ là tuyệt vời: D

+0

Byte 1 đến 5 thể hiện phần phân số của một số trong ký hiệu khoa học: '1.x * 2^e'. Mantissa là '1.x'. Vòng lặp * for * và hai dòng sau tạo * x *. Giả sử byte 1 là 0xa5. Trong nhị phân, đó là 10100101. Thêm vào 'mantissa' để nhận' mantissa == 0xa5'. Sau đó, * chuyển * các byte đó xuống phần phân số để nhận giá trị nhị phân 0.10100101. Sự dịch chuyển 8 được chia cho 256. Lặp lại các byte từ 2 đến 4. Byte 5 là đặc biệt vì chúng ta chỉ muốn 7 bit - bit thứ tám là bit dấu - do đó chia cho 128 để thay thế. Cuối cùng thêm 1 vì phần đó là * ngầm * (không được lưu trữ ở bất kỳ đâu). –

+0

Byte 0 là số mũ. Đó là một con số chưa được ký, nhưng nó bị thiên vị cao 129, vì vậy điều đầu tiên cần làm là chính xác cho sự thiên vị đó. Như đã đề cập trong chú thích trước, con số này ở dạng '1.x * 2^e', trong đó' 1.x' được lưu trong 'mantissa' và' e' được lưu trong 'số mũ'. Dòng cuối cùng của mã chỉ đơn giản là tính toán giá trị đó dưới dạng gấp đôi. –

+2

Lưu ý cho người đọc trong tương lai: Tôi khá chắc chắn mã này có lỗi. Đối với một điều, nó bỏ qua giá trị byte tại real48 [4]. Thận trọng. –

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

Từ Delphi Khái niệm cơ bản: "Real48: lỗi thời - Loại điểm nổi có công suất và độ chính xác cao nhất." Trong các phiên bản hiện đại của Delphi đó là một mở rộng (10 Bytes) –

+0

@Darin, tôi sợ điều này dường như không đưa ra câu trả lời đúng –

+0

Thực ra có vẻ như có điều gì đó sai trái. Tôi sẽ kiểm tra lại. –

0

Tôi đã thay đổi mã bạn đã đăng sang một định dạng có thể đọc được nhiều hơn như vậy bạn có thể thấy cách hoạt động:

 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

Mã này giới thiệu ít nhất một lỗi. Bạn quên thử nghiệm điều này với đầu vào tiêu cực. –

+0

Anh ta đang chờ bạn thử nghiệm nó cho anh ta Rob. Cảm ơn! – Pauk

2

Coi trọng này là một bài cũ, nhưng cũng sau đây có thể hữu ích cho những người tìm kiếm để làm điều này trong T-SQL (mà tôi đã).

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 

Chứng nhận

0,125 là 0x 0000 0000 007E (hoặc 0x 7E00 0000 0000 đảo ngược)

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

Đầu vào là một char12 như tôi đã phải trích xuất các nhị phân từ giữa 2 các trường nhị phân lớn hơn và shunt chúng lại với nhau vì vậy đã có nó như là char12. Dễ dàng đủ để thay đổi thành đầu vào nhị phân (6) nếu không cần thực hiện bất kỳ thao tác nào trước đó.

Là một sang một bên, trong kịch bản mà tôi đang triển khai, biến thể T-SQL được cải thiện tốt hơn mã C# CLR để mã C# ở trên có thể tốt hơn. Trong khi không phải ở khắp mọi nơi cho phép mã CLR vào SQL Server nếu bạn có thể thì có lẽ bạn nên. Để biết thêm chi tiết, một bài báo ở số http://www.simple-talk.com/sql/t-sql-programming/clr-performance-testing/ thực hiện một số phép đo chiều sâu cho thấy một số khác biệt đáng kể giữa T-SQL và CLR.

1

Tôi đã thử nghiệm điều này và đã tìm thấy lỗi (như những người khác đã nhận thấy) với các giá trị âm. Đây là phiên bản thử nghiệm của tôi về mã. Tôi đã thử nghiệm điều này với 120.530 giá trị ngẫu nhiên khác nhau dao động từ 11,400,000 đến -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; 
Các vấn đề liên quan