float数据和IEEE754标准

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了float数据和IEEE754标准相关的知识,希望对你有一定的参考价值。

十进制数据107.1597824到十六进制42D651CF(modbus收到的)之间是怎么转换的

Hex数据42D651CF换成32位二进制得,
0100 0010 1101 0110 0101 0001 1100 1111
符号位: 0,正数
指数位:100 0010 1 十进制133 ,减去127得指数 6
小数位:101 0110 0101 0001 1100 1111,添加首位隐藏位1.得
1.101 0110 0101 0001 1100 1111, 右移指数位6位,得
1101 011.0 0101 0001 1100 1111,逐位求2的乘方
1*(2^6)+1*(2^5)+0*(2^4)+1*(2^3)+0*(2^2)+1*(2^1)+1*(2^0)+小数点+0*(2^-1)+0*(2^-2)+1*(2^-3)+0*(2^-4)+1*(2^-5)+0*(2^-6)+0*(2^-7)+0*(2^-8)+1*(2^-9)+1*(2^-10)+1*(2^-11)+0*(2^-12)+0*(2^-13)+1*(2^-14)+1*(2^-15)+1*(2^-16)+1*(2^-17)
=107.1597824追问

十进制数据107.1597824被采集到cpu中是一个float型数据,十六进制42D651CF又是如何认得到的?

追答

电脑只能记录二进制的数据。其他进制都是转换后的表现形式。
42D651CF这样的十六进制编码,是对二进制字节的一种可打印的缩写描述。
每个数位代表4个位,2个数位代表一个字节,42 D6 51 CF代表4个字节或32bit的数据。
这4个字节具体代表什么,由IEEE754上面的规则和算法决定,上贴已经分解展示,这4个字节是如何代表浮点数值107.1597824 的。
你自己该做的的是理解浮点数是什么。

参考技术A   float浮点型变量在计算机内存中占用4字节(Byte),即32-bit。遵循IEEE-754格式标准。
  IEEE 754 标准是IEEE浮点数算术标准(IEEE Standard for Floating-Point Arithmetic)的标准编号 ,等同于国际标准ISO/IEC/IEEE 60559[2] 。该标准由美国电气电子工程师学会(IEEE)计算机学会旗下的微处理器标准委员会(Microprocessor Standards Committee, MSC)发布。
  

使用 float 存储小数?

很多程序员就会使用 float 类型来存储小数。sql 的 float 类型和其他大多数编程语言的 float 类型一样,
根据IEEE 754 标准使用二进制格式编码实数数据。

但是很多程序员并不清楚浮点类型的特性:并不是所有十进制中描述的信息都能使用二进制存储。

oracle 使用 float 类型表示的是一个精确值,而 BINARY_FLOAT 类型是一个非精确值,使用的是 IEEE 754 标准

十进制小数在二进制的表达方式是完全不同的
比如 十进制的59.95 ,它存储了二进制表示中最接近 59.95 的值,用十进制表示是 59.950000762639

下我们来看看奇迹的时刻

首先,我们新建一张表 rate

表很简单,表设计如下

CREATE TABLE `rate` (
    `tid`  int(11) NOT NULL AUTO_INCREMENT ,
    `money`  float NOT NULL ,
    PRIMARY KEY (`tid`)
);

插入 59.95的数据

INSERT INTO rate(tid,money) VALUES (‘1‘,‘59.95‘);

查询值

有些数据库能够通过某种方式弥补数据的不精确性,输出我们所期待的值

SELECT money FROM rate where tid = 1;

如在 MySQL 中这条语句的返回值是 59.95

但 FLOAT 类型的列中实际存储的数据可能并不完全等于它的值。将这个值扩大 10 倍,就能看到其中的区别

SELECT money * 10 FROM rate where tid = 1;

你可能希望上面那个扩大的查询返回的结果应该是 599.5 。但实际上返回的却是 599.5000076293945

在这个例子中,取整后的值与原值的误差为 千万分之一,对于大多数的运算来说已经足够精确了。

然而,对于某些运算来说这样的错误还是不可容忍的。

  • 最简单的例子就是用 FLOAT 进行比较操作
SELECT * FROM rate where money = 59.95;

Result: empty set: no row match

通常的变通方案是将浮点数视作"近似相等"

SELECT * FROM rate where ABS(money - 59.95) < 0.0000001 ;
  • 另一个由于使用非精确的 FLOAT 造成误差的情况,是使用合计函数计算很多值的时候。比如,如果使用 SUM() 函数计算一列中的所有值。

解决方法

使用 NUMERIC 或 DECIMAL类型

使用 SQL 中的 NUMERIC 或 DECIMAL 类型来代替 FLOAT 及其他类似的数据类型进行固定精度的小数存储。

这些数据类型精确地根据你定义时制定的精度来存储数据。NUMERIC 和 DECIMAL 的优势在于,它们不会像 FLOAT 类型那样对存储的有理数进行舍入操作。假设你输入59.95,就可以确信实际存储的数据就是59.95。

  • MySQL中,CPU不支持 DECIMAL 的直接计算,MySQL服务器自身事先了DECIMAL的高精度计算 DECIMAL。相对而言,CPU 直接支持原生浮点计算,所以浮点运算明显更快。
  • MySQL 5.0 和更高的版本中 DECIMAL 类型允许最多 65 个数字。然而,这些版本实际上不能在计算中使用这么大的数字,因为 DECIMAL 只是一种存储格式,在计算中 DECIMAL 会转换为 DOUBLE 类型。

可以考虑使用BIGINT

将需要存储的货币单位根据小数的位数乘以相应的倍数即可。假设要存储财务数据精确到万分之一,则可以把所有金额乘以一百万,然后将结果存储在 BIGINT 里,这样可以同时避免浮点存储计算不精确和精确计算代价高的问题。

合理适用 FLOAT

一般小数存储 FLOAT 精度其实是足够的,但是如果是金融、财务数据等需要小数精度比较高的数据的话,最好就不要适用 FLOAT 类型。因为需要额外的空间和计算开销,所以应该尽量在只对小数进行精确计算时才适用DEICAL。但在数据量比较大的时候,可以适用 BIGINT 代替 DEICAL 。

参考资料

《sql反模式》
《高性能MySQL》





以上是关于float数据和IEEE754标准的主要内容,如果未能解决你的问题,请参考以下文章

如何理解IEEE 754标准对Java中float值和double值的规定

ieee754单精度浮点数 表示方法

IEEE754是啥

IEEE754表示浮点数

IEEE 754标准如何转换?

IEEE 754的简介