IEEE754、VAX、IBM浮点型介绍和.NET中互相转换
【题外话】
最近在做C3D文件的解析,好奇怪的是文件中竟然存储了CPU的类型,原本不以为然,结果后来读取一个文件发现浮点数全部读取错误。查了下发现虽然在上世纪80年代就提出了IEEE754要统一浮点数标准,但是到现在仍然有计算机采用不同方式存储浮点数。在某些非IEEE754标准的计算机产生的二进制文件中,如果拿到其他计算机中读取,如果不进行专门的转换,可能导致数据错误等问题。
【文章索引】
【一、IEEE754标准浮点数字的存储详解】
对于x86等常见的CPU,都是采用IEEE754存储和计算浮点型的,当然在.NET平台中浮点型也是IEEE754标准的。首先回顾下本科时学过的计算机组成原理,查了下课本发现是如下介绍IEEE754浮点数的存储的(唐朔飞版课本233页):
其中,S为数符,它表示浮点数的正负,但与其有效位(尾数)是分开的。阶码用移码表示,阶码的真值都被加上一个常数(偏移量),如短实数、长实数和临时实数的偏移量用十六进制表示分别为7FH、3FFH和3FFFH。尾数部分通常都是规格化表示,即非“0”的有效位最高位总是1。
以单精度浮点数为例,如果字节查看应该是如下这个样子的,数符占第1字节的第1位,阶码占第1字节的后7位及第二字节的第1位,其余都是尾数。
SEF S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
bits
bytes byte1 byte2 byte3 byte4
如果设数符为S,阶码为E,尾数的小数部分为F,那么可以通过位运算得到这三位:
Double S = (byte1 & 0x80) >> ;
Double E = ((byte1 & 0x7F) << ) + ((byte2 & 0x80) >> );
Double F = ((byte2 & 0x7F) << ) + (byte3 << ) + 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 / 223。
所以可通过如下的公式来计算浮点数的值,其中,C是尾数规范化后减去的常量,B是移码的偏移量,可知A、B、C分别为A = 2、B = 0x7F以及C = 1.0。
V = (-)^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):
Single ToSingle(Byte[] data)
{
Double a = 2.0;
Double b = 127.0;
Double c = 1.0;
Double d = -126.0; Byte byte1 = data[];
Byte byte2 = data[];
Byte byte3 = data[];
Byte byte4 = data[]; Double s = (byte1 & 0x80) >> ;
Double e = ((byte1 & 0x7F) << ) + ((byte2 & 0x80) >> );
Double f = ((byte2 & 0x7F) << ) + (byte3 << ) + byte4;
Double m = f / Math.Pow(, ); if (e == 0xFF && f == ) return (s == ? Single.PositiveInfinity : Single.NegativeInfinity);
if (e == 0xFF && f != ) return Single.NaN;
if (e == 0x00 && f == ) return ;
if (e == 0x00 && f != ) return (Single)((s == ? 1.0 : -1.0) * m * Math.Pow(a, d)); return (Single)((s == ? 1.0 : -1.0) * (c + m) * Math.Pow(a, e - b));
} Byte[] GetBytes(Single num)
{
Double a = 2.0;
Double b = 127.0;
Double c = 1.0;
Double d = Math.Log(); Int32 s = (num >= ? : ); Double v = Math.Abs(num);
Int32 e = (Int32)(Math.Log(v) / d + b); Double m = (v / Math.Pow(a, e - b)) - c;
Int32 f = (Int32)(m * Math.Pow(, )); Byte[] data = new Byte[];
data[] = (Byte)((s << ) + ((e & 0xFE) >> ));
data[] = (Byte)(((e & 0x01) << ) + ((f & 0x007F0000) >> ));
data[] = (Byte)((f & 0x0000FF00) >> );
data[] = (Byte)(f & 0x000000FF); return data;
}
上述的浮点数转字节数组不能支持NaN和非规范化的情况,当然也可以自己判断下。当然了,上边说了这么多还是为了介绍下边两种浮点数做铺垫。如果实现系统浮点数与字节数组转换的话,用上边这种方法转换就不如用System.BitConverter来的方便了。
【二、VAX及IBM浮点数字的存储和转换】
首先还是按字节看下VAX和IBM浮点型的存储:
VAX单精度浮点:
SEF S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
bits
bytes byte2 byte3 byte0 byte1
IBM单精度浮点:
SEF S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF
bits
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浮点字节数组与系统浮点数字相互转化的类:
using System; namespace DotMaysWind.Numerics
{
/// <summary>
/// VAX单精度浮点数字
/// </summary>
/// <remarks>
/// SEF S EEEEEEEE FFFFFFF FFFFFFFF FFFFFFFF
/// bits 1 2 9 10 32
/// bytes byte2 byte1 byte4 byte3
/// </remarks>
public struct VAXSingle
{
#region 常量
private const Int32 LENGTH = ;
private const Double BASE = 2.0;
private const Double EXPONENT_BIAS = 128.0;
private const Double MANTISSA_CONSTANT = 0.5;
private const Double E24 = 16777216.0;
#endregion #region 字段
private Byte[] _data;
#endregion #region 构造方法
/// <summary>
/// 初始化新的VAX单精度浮点数字
/// </summary>
/// <param name="data">VAX单精度浮点数字字节数组</param>
/// <param name="startIndex">数据起始位置</param>
public VAXSingle(Byte[] data, Int32 startIndex)
{
this._data = new Byte[VAXSingle.LENGTH];
Array.Copy(data, startIndex, this._data, , VAXSingle.LENGTH);
} /// <summary>
/// 初始化新的VAX单精度浮点数字
/// </summary>
/// <param name="num">系统标准的单精度浮点数字</param>
public VAXSingle(Single num)
{
Int32 s = (num >= ? : ); Double v = Math.Abs(num);
Int32 e = (Int32)(Math.Log(v) / Math.Log(2.0) + 1.0 + VAXSingle.EXPONENT_BIAS); Double m = (v / Math.Pow(VAXSingle.BASE, e - VAXSingle.EXPONENT_BIAS)) - VAXSingle.MANTISSA_CONSTANT;
Int32 f = (Int32)(m * VAXSingle.E24); this._data = new Byte[VAXSingle.LENGTH];
this._data[] = (Byte)((s << ) + ((e & 0xFE) >> ));
this._data[] = (Byte)(((e & 0x01) << ) + ((f & 0x007F0000) >> ));
this._data[] = (Byte)((f & 0x0000FF00) >> );
this._data[] = (Byte)(f & 0x000000FF);
}
#endregion #region 方法
/// <summary>
/// 获取系统标准的单精度浮点数字
/// </summary>
/// <returns>系统标准的单精度浮点数字</returns>
public Single ToSingle()
{
Byte b1 = this._data[];
Byte b2 = this._data[];
Byte b3 = this._data[];
Byte b4 = this._data[]; Double s = (b1 & 0x80) >> ;
Double e = ((b1 & 0x7F) << ) + ((b2 & 0x80) >> );
Double f = ((b2 & 0x7F) << ) + (b3 << ) + b4;
Double m = f / VAXSingle.E24; if (e == && s == ) return ;
if (e == && s == ) return Single.NaN; return (Single)((s == ? 1.0 : -1.0) * (VAXSingle.MANTISSA_CONSTANT + m) * Math.Pow(VAXSingle.BASE, e - VAXSingle.EXPONENT_BIAS));
} /// <summary>
/// 获取VAX单精度浮点数据字节数组
/// </summary>
/// <returns>字节数组</returns>
public Byte[] ToArray()
{
Byte[] data = new Byte[VAXSingle.LENGTH]; Array.Copy(this._data, data, VAXSingle.LENGTH); return data;
}
#endregion
}
}
以下是实现IBM浮点字节数组与系统浮点数字相互转化的类:
using System; namespace DotMaysWind.Numerics
{
/// <summary>
/// IBM单精度浮点数字
/// </summary>
/// <remarks>
/// SEF S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF
/// bits 1 2 8 9 32
/// bytes byte1 byte2 byte3 byte4
/// </remarks>
public struct IBMSingle
{
#region 常量
private const Int32 LENGTH = ;
private const Double BASE = 16.0;
private const Double EXPONENT_BIAS = 64.0;
private const Double E24 = 16777216.0;
#endregion #region 字段
private Byte[] _data;
#endregion #region 构造方法
/// <summary>
/// 初始化新的IBM单精度浮点数字
/// </summary>
/// <param name="data">IBM单精度浮点数字字节数组</param>
/// <param name="startIndex">数据起始位置</param>
public IBMSingle(Byte[] data, Int32 startIndex)
{
this._data = new Byte[IBMSingle.LENGTH];
Array.Copy(data, startIndex, this._data, , IBMSingle.LENGTH);
} /// <summary>
/// 初始化新的IBM单精度浮点数字
/// </summary>
/// <param name="num">系统标准的单精度浮点数字</param>
public IBMSingle(Single num)
{
Int32 s = (num >= ? : ); Double v = Math.Abs(num);
Int32 e = (Int32)(Math.Log(v) / Math.Log(2.0) / 4.0 + 1.0 + IBMSingle.EXPONENT_BIAS); Double m = (v / Math.Pow(IBMSingle.BASE, e - IBMSingle.EXPONENT_BIAS));
Int32 f = (Int32)(m * IBMSingle.E24); this._data = new Byte[IBMSingle.LENGTH];
this._data[] = (Byte)(s + e);
this._data[] = (Byte)((f & 0x00FF0000) >> );
this._data[] = (Byte)((f & 0x0000FF00) >> );
this._data[] = (Byte)(f & 0x000000FF);
}
#endregion #region 方法
/// <summary>
/// 获取系统标准的单精度浮点数字
/// </summary>
/// <returns>系统标准的单精度浮点数字</returns>
public Single ToSingle()
{
Byte b1 = this._data[];
Byte b2 = this._data[];
Byte b3 = this._data[];
Byte b4 = this._data[]; Double s = (b1 & 0x80) >> ;
Double e = (b1 & 0x7F);
Double f = (b2 << ) + (b3 << ) + b4;
Double m = f / IBMSingle.E24; if (e == && f == && s == ) return ; return (Single)((s == ? 1.0 : -1.0) * m * Math.Pow(IBMSingle.BASE, e - IBMSingle.EXPONENT_BIAS));
} /// <summary>
/// 获取IBM单精度浮点数据字节数组
/// </summary>
/// <returns>字节数组</returns>
public Byte[] ToArray()
{
Byte[] data = new Byte[IBMSingle.LENGTH]; Array.Copy(this._data, data, IBMSingle.LENGTH); return data;
}
#endregion
}
}
【三、双精度浮点数的处理】
双精度浮点数与单精度浮点数类似,只不过会扩大阶码和尾数的范围罢了。对于IEEE754的双精度浮点而言,不仅尾数的位数增加,还会增加阶码的尾数,字节存储如下:
SEF S EEEEEEE EEEE FFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits
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
bytes byte2 byte3 byte0 byte1 byte6 byte7 byte4 byte5
IBM双精度浮点:
SEF S EEEEEEE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
bits
bytes byte1 byte2 byte3 byte4 byte5 byte6 byte7 byte8
【相关链接】
- 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
- 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
- IEEE Arithmetic:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html
- Floating-Point:http://andromeda.rutgers.edu/~dupre/231/lecture13.doc
- IBM Floating Point Architecture:http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture
- VAX floating point to Decimal:http://arstechnica.com/civis/viewtopic.php?f=20&t=171682
IEEE754、VAX、IBM浮点型介绍和.NET中互相转换的更多相关文章
- Linux: 介绍make menuconfig中的每个选项含义【转】
转自:http://blog.csdn.net/gaoyuanlinkconcept/article/details/8810468 介绍make menuconfig中的每个选项含义 Linux 2 ...
- 使用程序获取整型数据和浮点型数据在内存中的表示---gyy整理
使用程序获取整型数据和浮点型数据在内存中的表示. C++中整型(int).短整型(short int).单精度浮点数(float).双精度浮点数(double)在内存中所占字节数不同,因此取值范围也不 ...
- 介绍MFC框架中涉及到的设计模式(二)
接着上一篇<介绍MFC框架中涉及到的设计模式(一)>介绍 单例模式(Singleton Pattern) 单例模式是一种经常使用的软件设计模式.在它的核心结构中仅仅包括一个被称为单例类的特 ...
- 介绍下Shell中的${}、##和%%使用范例,本文给出了不同情况下得到的结果。
介绍下Shell中的${}.##和%%使用范例,本文给出了不同情况下得到的结果.假设定义了一个变量为:代码如下:file=/dir1/dir2/dir3/my.file.txt可以用${ }分别替换得 ...
- C#中字符转换问题详解
C# 出来也有些日子了,最近由于编程的需要,对 C# 的类型转换做了一些研究,其内容涉及 C# 的装箱/拆箱/别名.数值类型间相互转换.字符的 ASCII 码和 Unicode 码.数值字符串和数值之 ...
- python模块介绍- binascii 二进制和ASCII转换
python模块介绍-binascii二进制和ASCII转换 目录 项目简介 简介: Uu编码 Binhex编码 Base64编码 QP码 CRC校验和 二进制转换 其他实例 项目简介 Python中 ...
- VC++使用CImage在内存中Jpeg转换Bmp图片
VC++中Jpeg与Bmp图片格式互转应该是会经常遇到,Jpeg相比Bmp在图片大小上有很大优势. 本文重点介绍使用现有的CImage类在内存中进行转换,不需要保存为文件,也不需要引入第三方库. Li ...
- Java中byte转换int时与0xff进行与运算的原因
http://w.baike.com/LGAdcWgJBBQxRAHUf.html 转帖 java中byte转换int时为何与0xff进行与运算 在剖析该问题前请看如下代码 public static ...
- SQL Server中行列转换 Pivot UnPivot
SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PI ...
随机推荐
- windows和linux平台下的通用时间测试函数
Time.cpp ////////////////////////////////////////////////////////////////////////////// // Timer.cpp ...
- Beginning Scala study note(9) Scala and Java Interoperability
1. Translating Java Classes to Scala Classes Example 1: # a class declaration in Java public class B ...
- Redis设置认证密码 Redis使用认证密码登录 在Redis集群中使用认证密码
Redis默认配置是不需要密码认证的,也就是说只要连接的Redis服务器的host和port正确,就可以连接使用.这在安全性上会有一定的问题,所以需要启用Redis的认证密码,增加Redis服务器的安 ...
- ssh 使用
svn 删除所有的 .svn文件 find . -name .svn -type d -exec rm -fr {} \; linux之cp/scp命令+scp命令详解 注意:本篇以后设涉及到的@后面 ...
- SOAPUI使用教程-REST请求工作
双击一个REST请求在导航打开的REST请求编辑器窗口: 就像相应的SOAP请求编辑器,这个窗口有以下几部分组成: 工具栏在顶部有标准动作的和端口的下拉菜单轻松修改服务端口 请求编辑器左侧有相应编辑视 ...
- unity 改变场景
public class GameManager : MonoBehaviour { public void OnStartGame(int sceneName) { SceneManager.Loa ...
- 时代杂志发文:2017 AR/MR将变得比VR更加重要
每到年末都有很多企业或高管分析科技产业明年趋势.近日,时代杂志网页版刊登了2017年科技行业的五大趋势和热点话题的预测.该本作者TimBajarin,是硅谷市场研究公司CreativeStrategi ...
- linux菜鸟日记(4)
使用一个简单的for循环和if判断语句实现某个网段内所有ping所有客户机的shell程序: ..} do >&; then echo " ${i}通" else e ...
- linux安装oracle11g
准备oracle安装文件 Oracle11gR2包含两个文件linux_11gR2_database_1of2.zip和linux_11gR2_database_2of2.zip,将这两个文件通过SS ...
- STM32之DAC君
如花说得好:呃呃呃.是俗话说得好:有了ADC,怎可少了DAC..我觉得奇怪.今天我开头就直奔主题了.我想了想,总结了一句话:孙悟空纵然有七十二变.无论是变成猫也好,变成狗也罢.始终还是会变回他本身.所 ...