zoukankan      html  css  js  c++  java
  • 使用 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》

  • 相关阅读:
    start tag, end tag issues in IE7, particularly in xslt transformation
    用SandCastle为注释生成chm文档
    Firebug
    架构的重点
    Linux Shell常用技巧(十) 管道组合
    Linux JDK升级
    Linux Shell常用技巧(十二) Shell编程
    Packet Tracer 5.0实验(一) 交换机的基本配置与管理
    Linux Shell常用技巧(六) sort uniq tar split
    Linux Shell常用技巧(二) grep
  • 原文地址:https://www.cnblogs.com/jojo-feed/p/10163744.html
Copyright © 2011-2022 走看看