很多程序员就会使用 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 存储小数?的更多相关文章

  1. STM32内部flash存储小数——别样的C语言技巧

    今天在进行STM32内部falsh存储的时候,发现固件库历程的函数原型是这样的: 第一个是地址,在我的STM32中是2K一页的,第二个是要写入的数据. 问题就来了,存储一个小数该怎么办呢?固件库给的是 ...

  2. java用double和float进行小数计算精度不准确

    java用double和float进行小数计算精度不准确 大多数情况下,使用double和float计算的结果是准确的,但是在一些精度要求很高的系统中或者已知的小数计算得到的结果会不准确,这种问题是非 ...

  3. js取float型小数点后两位数的方法

    四舍五入以下处理结果会四舍五入:' var num =2.446242342; num = num.toFixed(2); // 输出结果为 2.45   不四舍五入以下处理结果不会四舍五入:第一种, ...

  4. Js验证 :只能输入数字和小数点 验证是否是数字 js取float型小数点后两位

    JS判断只能是数字和小数点 1.文本框只能输入数字代码(小数点也不能输入)<input onkeyup="this.value=this.value.replace(/\D/g,'') ...

  5. js取float型小数点后x位数的方法

    js中取小数点后两位方法最常用的就是四舍五入函数了,前面我介绍过js中四舍五入一此常用函数,这里正好用上,下面我们一起来看取float型小数点后两位一些方法总结 以下我们将为大家介绍 JavaScri ...

  6. js(javascript)取float型小数点后两位数的方法

    以下我们将为大家介绍 JavaScript 保留两位小数的实现方法:四舍五入以下处理结果会四舍五入: ? 1 2 var num =2.446242342; num = num.toFixed(2); ...

  7. redis中存储小数

    在做一个活动的需求时,需要往redis中有序的集合中存储一个小数,结果发现取出数据和存储时的数据不一致 zadd test_2017 1.1 tom (integer) zrevrange test_ ...

  8. float存储

    浮点型变量在计算机内存中占用4字节(Byte),即32-bit.遵循IEEE-754格式标准. 一个浮点数由2部分组成:底数m 和 指数e.                          ±man ...

  9. mysql存储小数

    线下不知道什么版本的古董了,经本人亲测,varchar类型的数据,可以直接执行mysql的sum函数. ________________________________________________ ...

随机推荐

  1. SpringBoot配置Cors解决跨域请求问题

    一.同源策略简介 同源策略[same origin policy]是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源. 同源策略是浏览器安全的基石. 什么是源 源[or ...

  2. 【Android基础】Fragment 详解之Fragment介绍

    Fragment在Android 3.0( API 11)引入,是为了支持在大屏上显示更加动态.灵活的UI,比如在平板和电视上.Fragment可以看作是嵌套的Activity,类似ActivityG ...

  3. Mybatis学习(四)————— 高级映射,一对一,一对多,多对多映射

    一.单向和双向 包括一对一,一对多,多对多这三种情况,但是每一种又分为单向和双向,在hibernate中我们就详细解析过这单向和双向是啥意思,在这里,在重复一遍,就拿一对多这种关系来讲,比如有员工和部 ...

  4. UnicodeDecodeError: 'ascii' codec can't decode byte

    这个问题遇到过很多次了,但是每次都没记住,用完就忘了,这次记录下. 通过关键词谷歌一下: 解决方案: # encoding=utf8 import sys reload(sys) sys.setdef ...

  5. Perl的do语句块结构

    do语句块结构如下: do {...} do语句块像是匿名子程序一样,没有名称,给定一个语句块,直接执行.且和子程序一样,do语句块的返回值都是最后一个执行的语句的返回值. 例如,将使用if-elsi ...

  6. Perl和操作系统交互(二):fork

    fork + exec fork是低层次的系统调用,通过复制父进程来创建子进程. fork的行为 fork用来拷贝当前进程,生成一个基本完全一样的子进程. my $pid=fork(); 如果fork ...

  7. mysql数据库中的数据导入与导出

    需求: 在本地电脑上的mysql中创建了一个数据库,并且在该数据库中创建了很多表,表中数据比较多: 现在想换一台电脑,但是又不想重新建数据库.建表.造数据. 解决方案: 利用mysql提供的命令,在本 ...

  8. Redis中的执行命令的过程

    在redis.c的initServerConfig()方法中,通过调用dictCreate方法初始化server端的命令表.这个命令表是一个hashtable,可以通过key找到相关的命令: /* C ...

  9. JQuery官方学习资料(译):操作元素

      获取和设置元素的信息     有很多种方式可以改变现有的元素,最常见的是改变HTML内容或者元素的属性.JQuery提供了简单的夸浏览器的方法来帮助你实现元素信息的获取和设置. .html():获 ...

  10. 各种官网系统镜像文件(Windows 7 ,Windows 10,Ubuntu 18.6,Centos 6.8 ,Centos 7.6 )

    在以前的刚进去计算机行业的时候,学的第一件事就是装系统,在网上苦于找不到正版的系统,这些是一直以来,见识的比较稳定的,有些是从官网下载的系统,给大家分享一哈.大家如果有用到其他好的系统,可以给我留言或 ...