将Delphi Real48转换为C#double

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

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

以前有人不得不这样做吗?

我需要在C#中进行转换

提前致谢

我已经做了一些狩猎,我找到了一些C ++代码来完成这项工作,转换它并且它似乎正在给出正确的答案……如果我理解了这一切虽然:该死的:

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

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

感谢这是一篇旧post,但对于那些希望在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 -- eg 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之间的一些显着差异。

我一直在测试这个并且发现了一个错误(正如其他人注意到的)带有负值。 这是我测试的代码版本。 我测试了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; 

我已经将您发布的代码更改为更易读的格式,以便您可以看到它的工作原理:

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