IEEE-754浮点标准[1]
直至20世纪70年代末, 实数(十进制数)被不同的计算机厂商表示成不同的二进制形式, 这使得许多程序与不同的机器不兼容. 1980年IEEE委员会将实数的浮点数据表示进行了标准化. 该标准大部分由Intel基于8087数学协处理器定制的. 认识到不同的程序需要不同的精度, 因此建立了单精度和双精度. 而今几乎所有的软件和硬件公司都遵循这些标准, 因此有必要做一些了解.
IEEE-754单精度浮点数
IEEE单精度浮点数使用32位数据表示 到范围内的正负实数. 转换为十进制大约是 到的正负实数. 这种单精度浮点数有时也叫短实数. 32位单精度形式的赋值如下图所示:
为了数学处理器的硬件设计更简单以及更少的晶体管消耗, 指数部分被加上一个常数作为偏置指数. 从实数转换为浮点有以下步骤:
实数转换为二进制形式.
科学计数法表示二进制: 1.xxx E yyy ().
第31位填充0(正数)或1(负数).
指数部分, yyy加上0x7F作为偏置指数, 填充第30~23位.
有效数字部分填充第22~0位.
例子
例1: 将转换成单精度(短实数)浮点.
= = 二进制科学计数法 1.00111 E 3
正数b31 = 0
偏置指数b30-23为1000 0010(3+7F=82H)
有效数字位b22-0为 001110000000000000000…00.
综合给出二进制形式, 下面一行是16进制值
0100 0001 0001 1100 0000 0000 0000 0000
4 1 1 C 0 0 0 0
例2: Convert decimal 15.575 to IEEE single-precision standard.
化成二进制形式
15 = 0xF = 0b1111
0.575 * 2 = 1.15 1
0.15 * 2 = 0.3 0
0.3 * 2 = 0.6 0
0.6 * 2 = 1.2 1
0.2 * 2 = 0.4 0
0.4*2 = 0.8 0
0.8*0.2 = 1.6 1
15.575 = 1111.100 1001 1001... = 1.111 1001 0011 0011 E 3
指数: 3+0x7F = 0x82 = 1000 0010
浮点: 0100 0001 0111 1001 0011 0011... = 0x41793333
例3: Convert decimal –0.00075 to IEEE single-precision standard.
使用16进制转换为二进制形式
0.00075 * 16 = 0.012 0
= 0.192 0
= 3.072 3
= 1.152 1
= 2.432 2
= 6.912 6
= 14.592 E
= 9.472 9
= 7.552 7
二进制形式: 0.00075 = 0x0.3126E97 E -2= 0b 1.1000100100110111010010111 E -11
指数: -0xB+0x7F = 0x74 = 0b 0111 0100
浮点: 1011 1010 0100 0100 1001 1011 1010 0110 = 0xBA449BA6
验证程序(c):
-
c版本
float x = -0.00075f; printf("0x%X\n", *(int*)&x); int y = 0xBA449BA6; printf("%f\n", *(float*)&y);
-
java版本
float x = -0.00075f; System.out.println(Integer.toHexString(Float.floatToIntBits(x)).toUpperCase());
应用
-
libminecraftpe.so某个函数的反汇编
; _DWORD ExperiencePotion::getThrowPower(ExperiencePotion *__hidden this) EXPORT _ZN16ExperiencePotion13getThrowPowerEv MOV.W R0, #0x3F000000 BX LR
我们来算一下这个浮点值(怎么确定这是个浮点?):
0x3F000000 = 0b 0011 1111 0000 ...0000 b31=0, 正数 指数: 0b011 1111 0 = 7E, 7E-7F=-1 有效数字:0 结果: 1.0 E -1(二进制) = 0.5(十进制)
-
下面代码的输出结果
float a = 0x7FFFFFFF; float b = 0x7FFFFFFE; System.out.println(a == b);
对于float类型加1不会产生任何影响的最小数是, 而对于 double 类型最小 数是 . 相邻的浮点数值之间的距离被称为一个ulp[3], 它是"最小单位 (unit in the last
place)"的缩写. java中引入了Math.ulp方法来计算float或 double数值的ulp。例如,
ulp(1.0f)=1.19xxE-7, ulp(0x7FFFFFC0) = 128.0
, 于是(float)0x7FFFFFC0 = 0x7FFFFFFF. 是否存在一个数a使得:
a==a+1 && a==a+2
为true?是否存在a,使得
a != a
?是否存在a,使得
a != a+0
?-
下面代码输出结果什么?
int start = 2_0000_0000; int count = 0; for (float f = start; f < start+20; f++){ count++; } System.out.println(count);
IEEE-754双精度浮点数
双精度浮点数(Intel称之为长实数)可以表示 到的正负实数. 52位有效数字, 11位指数, 第63位表示符号. 转换过程和单精度浮点一样, 首先表成1.xxx E yyy, 然后yyy加3FF得到偏置指数. 如下图所示:
例4: Convert decimal 152.1875 to double-precision FP.
152.1875 = 二进制10011000.0011 = 科学二进制1.00110000011 E 7
b63 = 0
偏置指数b62-53 = 10000000110 (7+3FF=406)
有效数字位b52-0 = 00110000011000…..000
0100 0000 0110 0011 0000 0110 0000 0000 0000 ... 0000
4 0 6 3 0 6 0 0 0 ... 0
参考资料
[1]. Muhammad Ali Mazidi etc. ARM Assembly Language Programming & Architecture [M]. section5.3
[2].IEEE_754