您现在的位置是:首页 > 正文

Java二进制小数表示_《Java编程的逻辑》笔记9--小数的二进制表示

2024-02-01 00:37:29阅读 2

34fa7938e8013bd1165962d0e0e47ed9.png

小数计算为什么会出错?

简要答案

实际上,不是运算本身会出错,而是计算机根本就不能精确的表示很多数,比如0.1这个数。

计算机是用一种二进制格式存储小数的,这个二进制格式不能精确表示0.1,它只能表示一个非常接近0.1但又不等于0.1的一个数。

数字都不能精确表示,在不精确数字上的运算结果不精确也就不足为奇了。

0.1怎么会不能精确表示呢?在十进制的世界里是可以的,但在二进制的世界里不行。在说二进制之前,我们先来看下熟悉的十进制。

实际上,十进制也只能表示那些可以表述为10的多少次方和的数,比如12.345,实际上表示的:110+21+30.1+40.01+5*0.001,与整数的表示类似,小数点后面的每个位置也都有一个位权,从左到右,依次为 0.1,0.01,0.001,…即10^(-1), 10^(-2), 10^(-3)。

很多数,十进制也是不能精确表示的,比如1/3, 保留三位小数的话,十进制表示是0.333,但无论后面保留多少位小数,都是不精确的,用0.333进行运算,比如乘以3,期望结果是1,但实际上却是0.999。

二进制是类似的,但二进制只能表示哪些可以表述为2的多少次方和的数,来看下2的次方的一些例子:

9aa14008c9370683eb41d97a789c3d14.png

可以精确表示为2的某次方之和的数可以精确表示,其他数则不能精确表示。

为什么一定要用二进制呢?

为什么就不能用我们熟悉的十进制呢?在最最底层,计算机使用的电子元器件只能表示两个状态,通常是低压和高压,对应0和1,使用二进制容易基于这些电子器件构建硬件设备和进行运算。如果非要使用十进制,则这些硬件就会复杂很多,并且效率低下。

#为什么有的小数计算是准确的

如果你编写程序进行试验,你会发现有的计算结果是准确的。比如,我用Java写:

System.out.println(0.1f+0.1f);

System.out.println(0.1f*0.1f);

第一行输出0.2,第二行输出0.010000001。按照上面的说法,第一行的结果应该也不对啊?

其实,这只是Java语言给我们造成的假象,计算结果其实也是不精确的,但是由于结果和0.2足够接近,在输出的时候,Java选择了输出0.2这个看上去非常精简的数字,而不是一个中间有很多0的小数。

在误差足够小的时候,结果看上去是精确的,但不精确其实才是常态。

怎么处理计算不精确

计算不精确,怎么办呢?大部分情况下,我们不需要那么高的精度,可以四舍五入,或者在输出的时候只保留固定个数的小数位。

如果真的需要比较高的精度,一种方法是将小数转化为整数进行运算,运算结束后再转化为小数,另外的方法一般是使用十进制的数据类型,这个没有统一的规范,在Java中是BigDecimal,运算更准确,但效率比较低,本节就不详细说了。

二进制表示

我们之前一直在用"小数"这个词表示float和double类型,其实,这是不严谨的,“小数"是在数学中用的词,在计算机中,我们一般说的是"浮点数”。float和double被称为浮点数据类型,小数运算被称为浮点运算。

为什么要叫浮点数呢?这是由于小数的二进制表示中,表示那个小数点的时候,点不是固定的,而是浮动的。

我们还是用10进制类比,10进制有科学表示法,比如123.45这个数,直接这么写,就是固定表示法,如果用科学表示法,在小数点前只保留一位数字,可以写为1.2345E2即1.2345*(10^2),即在科学表示法中,小数点向左浮动了两位。

二进制中为表示小数,也采用类似的科学表示法,形如 m*(2^e)。m称为尾数,e称为指数。指数可以为真,也可以为负,负的指数表示哪些接近0的比较小的数。在二进制中,单独表示尾数部分和指数部分,另外还有一个符号位表示正负。

几乎所有的硬件和编程语言表示小数的二进制格式都是一样的,这种格式是一个标准,叫做IEEE 754标准,它定义了两种格式,一种是32位的,对应于Java的float,另一种是64位的,对应于Java的double。

32位格式中,1位表示符号,23位表示尾数,8位表示指数。64位格式中,1位表示符号,52位表示尾数,11位表示指数。

在两种格式中,除了表示正常的数,标准还规定了一些特殊的二进制形式表示一些特殊的值,比如负无穷,正无穷,0,NaN (非数值,比如0乘以无穷大)。

IEEE 754标准有一些复杂的细节,初次看上去难以理解,对于日常应用也不常用,本文就不介绍了。

如果你想查看浮点数的具体二进制形式,在Java中,可以使用如下代码:

Integer.toBinaryString(Float.floatToIntBits(value))

Long.toBinaryString(Double.doubleToLongBits(value));

写在最后

标签:表示,Java,二进制,0.1,精确,小数

来源: https://blog.csdn.net/DT235201314/article/details/97389521

网站文章

  • C#使用OpenCVSharp进行轮廓检测

    在计算机视觉领域中,轮廓检测是一项常见的任务,它可以帮助我们找到图像中的物体边缘或形状边界。在本文中,我们将介绍如何使用C#和OpenCVSharp库实现轮廓检测。在上述代码中,我们首先读取了输入图像...

    2024-02-01 00:37:23
  • 通过PXE服务器批量安装系统

    通过PXE批量部署服务器

    2024-02-01 00:36:56
  • c语言基础局部变量与全局变量

    局部变量1. 在函数内部定义的变量2. 生命周期:从变量定义到函数结束3. 作用域:从变量定义到函数结束全局变量1. 在函数外部定义的变量2. 生命周期:从程序创建到程序销毁(全局变量的地址一旦文件编...

    2024-02-01 00:36:49
  • Android性能优化之-LeakCannary

    Android性能优化之-LeakCannary

    LeakCanary使用 LeakCanary是一个用于Android的内存泄漏检测库.本文从如下四点分析源码 检查哪些内存泄漏 检查内存泄漏的时机 如何判定内存泄漏 如何分析内存泄漏(只有一点点,可...

    2024-02-01 00:36:42
  • Linux 学习记录

    Linux 学习记录

    > 开口朝哪里,哪里的东西输出到另一面,只要有输出就能用管道灌过去 ,>>不覆盖原来内容,追加输入,|还未了解。:为了避免文件行数太多,部分展示,可以用回车一页一页的翻,可以按q中途退出,或者空格全部...

    2024-02-01 00:36:33
  • Python编程基础

    Python编程基础

    Python编程基础主要包括基本语法、内建数据结构、函数以及文件操作。一篇文章马上懂基础python使用、文件写入读出、csv文件处理,函数使用、列表全方位处理等操作。必会包会

    2024-02-01 00:36:06
  • git如何回滚到历史某个版本

    git如何回滚到历史某个版本

    问题:比如某员工误操作提交到了公司比较重要的develop分支,如何恢复到之前的某一个版本呢,git如何回滚到历史某个版本 方法一:idea里面git操作版本 1、 2、选中某一个版本,右键copy版本号 3、如何指针再指向本地的head版本号 4、提交到远程。push,会报错。应该加上-f 属性,推送ch成功。push的时候有一个小按钮 推送成功。 参考链接:https...

    2024-02-01 00:36:00
  • NOIP(CSP-J)信息学奥赛_普及组第十三课--嵌套循环-循环中的循环

    目录NOIP(CSP-J)信息学奥赛_普及组第十二课--嵌套循环-循环中的循环????先看一个案例二、 请写出如下程序的输出结果,深入理解嵌套循环三、嵌套循环的基本结构:四、嵌套循环图形题五、常见的数...

    2024-02-01 00:35:53
  • 蓝桥杯2019 第十届 c/c++ c组 编程大题 第四题 第2019个质数

    题目:结果填空,找质数,3是第一个质数 5是第二个质数,7是第三个质数,呢么第2019个质数是多少在绝望中水博客,这题我看成了2019以内的质数有多少个,估计也就我会看叉成这样吧,祝大家拿省一喽,心痛。代码:#include <iostream>#include <math.h>using namespace std;int main(){ int num...

    2024-02-01 00:35:16
  • 作业3F

    Problem F: 成绩的等级 Time Limit: 1 Sec Memory Limit: 2 MB Submit: 18628 Solved: 7766 [Submit][Status][We...

    2024-02-01 00:34:57