IEEE75四、VAX、IBM浮點型介紹和.NET中互相轉換

【題外話】php

最近在作C3D文件的解析,好奇怪的是文件中居然存儲了CPU的類型,本來不覺得然,結果後來讀取一個文件發現浮點數所有讀取錯誤。查了下發現雖然在上世紀80年代就提出了IEEE754要統一浮點數標準,可是到如今仍然有計算機採用不一樣方式存儲浮點數。在某些非IEEE754標準的計算機產生的二進制文件中,若是拿到其餘計算機中讀取,若是不進行專門的轉換,可能致使數據錯誤等問題。html

 

【文章索引】express

  1. IEEE754標準浮點數字的存儲詳解
  2. VAX及IBM浮點數字的存儲和轉換
  3. 雙精度浮點數的處理

 

【1、IEEE754標準浮點數字的存儲詳解】數組

對於x86等常見的CPU,都是採用IEEE754存儲和計算浮點型的,固然在.NET平臺中浮點型也是IEEE754標準的。首先回顧下本科時學過的計算機組成原理,查了下課本發現是以下介紹IEEE754浮點數的存儲的(唐朔飛版課本233頁):oracle

其中,S爲數符,它表示浮點數的正負,但與其有效位(尾數)是分開的。階碼用移碼錶示,階碼的真值都被加上一個常數(偏移量),如短實數、長實數和臨時實數的偏移量用十六進制表示分別爲7FH、3FFH和3FFFH。尾數部分一般都是規格化表示,即非「0」的有效位最高位老是1。ide

以單精度浮點數爲例,若是字節查看應該是以下這個樣子的,數符佔第1字節的第1位,階碼佔第1字節的後7位及第二字節的第1位,其他都是尾數。this

SEF      S        EEEEEEEE        FFFFFFF        FFFFFFFF        FFFFFFFF
bits     1        2      9        10                                    32
bytes    byte1           byte2                   byte3           byte4

若是設數符爲S,階碼爲E,尾數的小數部分爲F,那麼能夠經過位運算獲得這三位:spa

Double S = (byte1 & 0x80) >> 7;
Double E = ((byte1 & 0x7F) << 1) + ((byte2 & 0x80) >> 7);
Double F = ((byte2 & 0x7F) << 16) + (byte3 << 8) + byte4;

因爲階碼用移碼錶示,因此真實的階碼則是E - 0x7F。而尾數因爲是規格化的表示,即將尾數規範化爲(1.FFFFF……FF)2,但只存小數點以後的部分。因爲1 / 2 + 1 / 4 + 1 / 8 + ... + 1 / n = 1 - 1 / 2n,因此可知尾數M(M = 1.0 + F)的範圍爲1 <= M <= 2 - 1 / 223code

因此可經過以下的公式來計算浮點數的值,其中,C是尾數規範化後減去的常量,B是移碼的偏移量,可知A、B、C分別爲A = 二、B = 0x7F以及C = 1.0。orm

V = (-1)^S * (F + C) * A^(E - B)

可見,浮點數就不存在0的概念了,因此只能用極限小來表示,同時爲了表示無窮大,規定E取值範圍爲0 < E < 0xFF,即-0x7F < (E - B) < 0x80。

因此,當E = 0xFF時,指數最大,規定F = 0時爲無窮值,其中又有S = 0爲正無窮以及S = 1爲負無窮;而F != 0時爲無效數字(NaN)。

當E = 0時,指數最小,規定F = 0時爲0,其中又有S = 0爲正0以及S = 1時爲-0。

不過表示很是小的數字,容許當E = 0時非規範化的尾數存在。即當E = 0且F !=0時,V = (-1)^S * F * A^-126。

二進制表示 十六進制表示 含義 十進制表示
0 11111111 00000000000000000000000 7F 80 00 00 正無窮 +∞ 
1 11111111 00000000000000000000000 FF 80 00 00 負無窮 -∞ 
0 00000000 00000000000000000000000 00 00 00 00 +0 0
1 00000000 00000000000000000000000 80 00 00 00 -0 0
0 00000000 00000000000000000000001 00 00 00 01  最小正數  1.401298E-45
0 11111110 11111111111111111111111 7F 7F FF FF 最大值 3.402823E+38
1 11111110 11111111111111111111111 FF 7F FF FF 最小值 -3.402823E+38
0 01111111 00000000000000000000000
3F 80 00 00
+1 1

而二進制小數轉十進制小數的計算能夠直接按整數的轉換來作,而後除以2n便可,n在這裏其實就是尾數的長度,爲23。

因此,有了以上的這些信息,咱們就能夠將浮點數字與字節數組相互轉換了(本文假定給定的字節數組都是Litten-Endian):

 1 Single ToSingle(Byte[] data)
 2 {
 3     Double a = 2.0;
 4     Double b = 127.0;
 5     Double c = 1.0;
 6     Double d = -126.0;
 7 
 8     Byte byte1 = data[3];
 9     Byte byte2 = data[2];
10     Byte byte3 = data[1];
11     Byte byte4 = data[0];
12 
13     Double s = (byte1 & 0x80) >> 7;
14     Double e = ((byte1 & 0x7F) << 1) + ((byte2 & 0x80) >> 7);
15     Double f = ((byte2 & 0x7F) << 16) + (byte3 << 8) + byte4;
16     Double m = f / Math.Pow(2, 23);
17 
18     if (e == 0xFF && f == 0) return (s == 0 ? Single.PositiveInfinity : Single.NegativeInfinity);
19     if (e == 0xFF && f != 0) return Single.NaN;
20     if (e == 0x00 && f == 0) return 0;
21     if (e == 0x00 && f != 0) return (Single)((s == 0 ? 1.0 : -1.0) * m * Math.Pow(a, d));
22 
23     return (Single)((s == 0 ? 1.0 : -1.0) * (c + m) * Math.Pow(a, e - b));
24 }
25 
26 Byte[] GetBytes(Single num)
27 {
28     Double a = 2.0;
29     Double b = 127.0;
30     Double c = 1.0;
31     Double d = Math.Log(2);
32 
33     Int32 s = (num >= 0 ? 0 : 1);
34 
35     Double v = Math.Abs(num);
36     Int32 e = (Int32)(Math.Log(v) / d + b);
37 
38     Double m = (v / Math.Pow(a, e - b)) - c;
39     Int32 f = (Int32)(m * Math.Pow(2, 23));
40 
41     Byte[] data = new Byte[4];
42     data[3] = (Byte)((s << 7) + ((e & 0xFE) >> 1));
43     data[2] = (Byte)(((e & 0x01) << 7) + ((f & 0x007F0000) >> 16));
44     data[1] = (Byte)((f & 0x0000FF00) >> 8);
45     data[0] = (Byte)(f & 0x000000FF);
46 
47     return data;
48 }

上述的浮點數轉字節數組不能支持NaN和非規範化的狀況,固然也能夠本身判斷下。固然了,上邊說了這麼多仍是爲了介紹下邊兩種浮點數作鋪墊。若是實現系統浮點數與字節數組轉換的話,用上邊這種方法轉換就不如用System.BitConverter來的方便了。

 

【2、VAX及IBM浮點數字的存儲和轉換】

首先仍是按字節看下VAX和IBM浮點型的存儲:

VAX單精度浮點:

SEF         S        EEEEEEEE        FFFFFFF        FFFFFFFF        FFFFFFFF
bits        1        2      9        10                                    32
bytes       byte2           byte3                   byte0           byte1

IBM單精度浮點:

SEF         S        EEEEEEE        FFFFFFFF        FFFFFFFF        FFFFFFFF
bits        1        2     8        9                                      32
bytes       byte1                   byte2           byte3           byte4

很是有意思的是,VAX存儲的結構並非按順序存儲的,而是採用了一種叫作Middle-Endian的存儲方式來存儲(並不是字節序):對於四字節而言其順序就是2301,八字節爲23016745,十六字節爲23016745AB89EFCD。不過整體來講,VAX浮點型與IEEE754仍是很相似的,好比VAX也要進行規範化,可是其規範化爲(0.1FFFFF..FF)2,因此上述的C就爲0.5,其尾數M的範圍即爲1/2 <= M <= 1 - 1 / 224;而同時其也並無規定無窮大,不須要單獨爲無限大留出最大的階碼,因此上述的B爲0x80。

而IBM單精度浮點則與上述兩種差異更大。首先其階碼並非8位,而是7位,因爲仍是使用移碼存儲的階碼,因此其減去的不能是127或者128,而是64,因此其與VAX同樣,也沒有無窮值的表示。除此以外,其也不是以2爲底計算階碼的,而是以16爲底,而且其沒有規範化尾數的要求(固然這也與其以16爲底有關),因此不須要對尾數進行加減運算,其範圍爲1/16 <= M <= 1- 1 / 224

如下是實現VAX浮點字節數組與系統浮點數字相互轉化的類:

 1 using System;
 2 
 3 namespace DotMaysWind.Numerics
 4 {
 5     /// <summary>
 6     /// VAX單精度浮點數字
 7     /// </summary>
 8     /// <remarks>
 9     /// SEF         S        EEEEEEEE        FFFFFFF        FFFFFFFF        FFFFFFFF
10     /// bits        1        2      9        10                                    32          
11     /// bytes       byte2           byte1                   byte4           byte3
12     /// </remarks>
13     public struct VAXSingle
14     {
15         #region 常量
16         private const Int32 LENGTH = 4;
17         private const Double BASE = 2.0;
18         private const Double EXPONENT_BIAS = 128.0;
19         private const Double MANTISSA_CONSTANT = 0.5;
20         private const Double E24 = 16777216.0;
21         #endregion
22 
23         #region 字段
24         private Byte[] _data;
25         #endregion
26 
27         #region 構造方法
28         /// <summary>
29         /// 初始化新的VAX單精度浮點數字
30         /// </summary>
31         /// <param name="data">VAX單精度浮點數字字節數組</param>
32         /// <param name="startIndex">數據起始位置</param>
33         public VAXSingle(Byte[] data, Int32 startIndex)
34         {
35             this._data = new Byte[VAXSingle.LENGTH];
36             Array.Copy(data, startIndex, this._data, 0, VAXSingle.LENGTH);
37         }
38 
39         /// <summary>
40         /// 初始化新的VAX單精度浮點數字
41         /// </summary>
42         /// <param name="num">系統標準的單精度浮點數字</param>
43         public VAXSingle(Single num)
44         {
45             Int32 s = (num >= 0 ? 0 : 1);
46 
47             Double v = Math.Abs(num);
48             Int32 e = (Int32)(Math.Log(v) / Math.Log(2.0) + 1.0 + VAXSingle.EXPONENT_BIAS);
49 
50             Double m = (v / Math.Pow(VAXSingle.BASE, e - VAXSingle.EXPONENT_BIAS)) - VAXSingle.MANTISSA_CONSTANT;
51             Int32 f = (Int32)(m * VAXSingle.E24);
52 
53             this._data = new Byte[VAXSingle.LENGTH];
54             this._data[1] = (Byte)((s << 7) + ((e & 0xFE) >> 1));
55             this._data[0] = (Byte)(((e & 0x01) << 7) + ((f & 0x007F0000) >> 16));
56             this._data[3] = (Byte)((f & 0x0000FF00) >> 8);
57             this._data[2] = (Byte)(f & 0x000000FF);
58         }
59         #endregion
60 
61         #region 方法
62         /// <summary>
63         /// 獲取系統標準的單精度浮點數字
64         /// </summary>
65         /// <returns>系統標準的單精度浮點數字</returns>
66         public Single ToSingle()
67         {
68             Byte b1 = this._data[1];
69             Byte b2 = this._data[0];
70             Byte b3 = this._data[3];
71             Byte b4 = this._data[2];
72 
73             Double s = (b1 & 0x80) >> 7;
74             Double e = ((b1 & 0x7F) << 1) + ((b2 & 0x80) >> 7);
75             Double f = ((b2 & 0x7F) << 16) + (b3 << 8) + b4;
76             Double m = f / VAXSingle.E24;
77 
78             if (e == 0 && s == 0) return 0;
79             if (e == 0 && s == 1) return Single.NaN;
80 
81             return (Single)((s == 0 ? 1.0 : -1.0) * (VAXSingle.MANTISSA_CONSTANT + m) * Math.Pow(VAXSingle.BASE, e - VAXSingle.EXPONENT_BIAS));
82         }
83 
84         /// <summary>
85         /// 獲取VAX單精度浮點數據字節數組
86         /// </summary>
87         /// <returns>字節數組</returns>
88         public Byte[] ToArray()
89         {
90             Byte[] data = new Byte[VAXSingle.LENGTH];
91 
92             Array.Copy(this._data, data, VAXSingle.LENGTH);
93 
94             return data;
95         }
96         #endregion
97     }
98 }
View Code

如下是實現IBM浮點字節數組與系統浮點數字相互轉化的類:

 1 using System;
 2 
 3 namespace DotMaysWind.Numerics
 4 {
 5     /// <summary>
 6     /// IBM單精度浮點數字
 7     /// </summary>
 8     /// <remarks>
 9     /// SEF         S        EEEEEEE        FFFFFFFF        FFFFFFFF        FFFFFFFF
10     /// bits        1        2     8        9                                      32
11     /// bytes       byte1                   byte2           byte3           byte4
12     /// </remarks>
13     public struct IBMSingle
14     {
15         #region 常量
16         private const Int32 LENGTH = 4;
17         private const Double BASE = 16.0;
18         private const Double EXPONENT_BIAS = 64.0;
19         private const Double E24 = 16777216.0;
20         #endregion
21 
22         #region 字段
23         private Byte[] _data;
24         #endregion
25 
26         #region 構造方法
27         /// <summary>
28         /// 初始化新的IBM單精度浮點數字
29         /// </summary>
30         /// <param name="data">IBM單精度浮點數字字節數組</param>
31         /// <param name="startIndex">數據起始位置</param>
32         public IBMSingle(Byte[] data, Int32 startIndex)
33         {
34             this._data = new Byte[IBMSingle.LENGTH];
35             Array.Copy(data, startIndex, this._data, 0, IBMSingle.LENGTH);
36         }
37 
38         /// <summary>
39         /// 初始化新的IBM單精度浮點數字
40         /// </summary>
41         /// <param name="num">系統標準的單精度浮點數字</param>
42         public IBMSingle(Single num)
43         {
44             Int32 s = (num >= 0 ? 0 : 1);
45 
46             Double v = Math.Abs(num);
47             Int32 e = (Int32)(Math.Log(v) / Math.Log(2.0) / 4.0 + 1.0 + IBMSingle.EXPONENT_BIAS);
48 
49             Double m = (v / Math.Pow(IBMSingle.BASE, e - IBMSingle.EXPONENT_BIAS));
50             Int32 f = (Int32)(m * IBMSingle.E24);
51 
52             this._data = new Byte[IBMSingle.LENGTH];
53             this._data[3] = (Byte)(s + e);
54             this._data[2] = (Byte)((f & 0x00FF0000) >> 16);
55             this._data[1] = (Byte)((f & 0x0000FF00) >> 8);
56             this._data[0] = (Byte)(f & 0x000000FF);
57         }
58         #endregion
59 
60         #region 方法
61         /// <summary>
62         /// 獲取系統標準的單精度浮點數字
63         /// </summary>
64         /// <returns>系統標準的單精度浮點數字</returns>
65         public Single ToSingle()
66         {
67             Byte b1 = this._data[3];
68             Byte b2 = this._data[2];
69             Byte b3 = this._data[1];
70             Byte b4 = this._data[0];
71 
72             Double s = (b1 & 0x80) >> 7;
73             Double e = (b1 & 0x7F);
74             Double f = (b2 << 16) + (b3 << 8) + b4;
75             Double m = f / IBMSingle.E24;
76 
77             if (e == 0 && f == 0 && s == 0) return 0;
78 
79             return (Single)((s == 0 ? 1.0 : -1.0) * m * Math.Pow(IBMSingle.BASE, e - IBMSingle.EXPONENT_BIAS));
80         }
81 
82         /// <summary>
83         /// 獲取IBM單精度浮點數據字節數組
84         /// </summary>
85         /// <returns>字節數組</returns>
86         public Byte[] ToArray()
87         {
88             Byte[] data = new Byte[IBMSingle.LENGTH];
89 
90             Array.Copy(this._data, data, IBMSingle.LENGTH);
91 
92             return data;
93         }
94         #endregion
95     }
96 }
View Code

 

【3、雙精度浮點數的處理】

雙精度浮點數與單精度浮點數相似,只不過會擴大階碼和尾數的範圍罷了。對於IEEE754的雙精度浮點而言,不只尾數的位數增長,還會增長階碼的尾數,字節存儲以下:

SEF    S     EEEEEEE EEEE  FFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits   1     2          12 13                                                       64
bytes  byte1         byte2      byte3    byte4    byte5    byte6    byte7    byte8

可見,其階碼增長了3位,即最大值是原來翻了3翻,爲1024。而爲了保證能表示無窮值,因此B爲1023。除此以外只須要多讀取後邊增長的尾數便可,步驟與單精度基本相同。

而對於VAX和IBM的雙精度浮點,更是沒有擴大階碼的範圍,而只是擴大了尾數的範圍,使得只要多讀取增長的4位尾數便可,而常數A、B、C更是無需修改。二者字節存儲以下:

VAX雙精度浮點:

SEF    S     EEEEEEEE     FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits   1     2      9     10                                                          64
bytes  byte2        byte3         byte0    byte1    byte6    byte7    byte4    byte5

IBM雙精度浮點:

SEF    S     EEEEEEE  FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits   1     2     8  9                                                            64
bytes  byte1          byte2    byte3    byte4    byte5    byte6    byte7    byte8

 

【相關連接】

  1. Transform between IEEE, IBM or VAX floating point number formats and bytes expressions:http://www.codeproject.com/Articles/12363/Transform-between-IEEE-IBM-or-VAX-floating-point-n
  2. VAX F_FLOAT and D_FLOAT to IEEE T_FLOAT and S_FLOAT (double):http://yaadc.blogspot.com/2013/01/vax-ffloat-and-dfloat-to-ieee-tfloat.html
  3. IEEE Arithmetic:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html
  4. Floating-Point:http://andromeda.rutgers.edu/~dupre/231/lecture13.doc
  5. IBM Floating Point Architecture:http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture
  6. VAX floating point to Decimal:http://arstechnica.com/civis/viewtopic.php?f=20&t=171682
相關文章
相關標籤/搜索