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

166924385_1_20190726023746987

小数计算为什么会出错?

简要答案

实际上,不是运算本身会出错,而是计算机根本就不能精确的表示很多数,比如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的次方的一些例子:

166924385_2_20190726023747691

可以精确表示为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));

写在最后

https://www.icode9.com/content-1-357901.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/567928.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『TensorFlow』模型保存和载入方法汇总

一、TensorFlow常规模型加载方法 保存模型 tf.train.Saver()类,.save(sess, ckpt文件目录)方法 参数名称功能说明默认值var_listSaver中存储变量集合全局变量集合reshape加载时是否恢复变量形状Truesharded是否将变量轮循放在所有设备上Truemax_to_keep保留最近检…

STL13-list容器(链表)

链表是由一系列的结点组成,结点包括两个域:一个数据域,一个指针域 1、链表内存是非连续的,添加删除元素效率较高,时间复杂度都是常数项,不需要移动元素 2、链表只有在需要的时候才会分配内存 3、链表只要…

php 前往页面,PHP实现网页截图?

如何使用PHP实现网页截图PHP实现网页截图是一个在日常开发中不常见的需求,但是如果实现还是非常有意思的。目前业界有很多成熟的方案,下面我推荐使用一个很稳定的第三方服务来直接实现,该服务有如下特点:支持多线路支持登录截图支…

STL14-set/multiset容器

set只有一个方法就是insert #include<iostream> #include<set> //set和multiset是一个头文件 //set内部实现机制 红黑色&#xff08;平衡二叉树的一种&#xff09; //关联式容器 //set不允许有重复元素 //multiset运行有重复元素 //容器查找效率高 //容器根据元素的…

普通的java类型是指,String是一个很普通的类 - Java那些事儿

上一篇我们讲了Java中的数组&#xff0c;其实是为本章的内容做准备的&#xff0c;String这个类是我们在写Java代码中用得最多的一个类&#xff0c;没有之一&#xff0c;今天我们就讲讲它&#xff0c;我们打开String这个类的源码&#xff1a;声明了一个char[]数组&#xff0c;变…

STL15-map/multimap容器

map的key值不可以重复 multimap的key值可以重复 #if 1 #include<iostream> #include<map> using namespace std; //初始化 void test01() {//map容器参数 第一个参数key的类型 第二个参数value类型map<int, int> mymap;//插入元素 pair.first key值 pair.se…

php nginx日志分析,如何通过NGINX的log日志来分析网站的访问情况,试试这些命令...

想知道你的网站每天的访问情况吗&#xff1f;有多少人访问了&#xff1f;访问最多的页面是哪个&#xff1f;哪个时段访问的人最多&#xff1f;哪个地方访问的最多&#xff1f;每秒有多少请求&#xff1f;很好奇吧&#xff0c;只要你是使用了nginx进行请求抓发&#xff0c;那么就…

php带来互联网的影响,网络对我们的影响有哪些?

影响有&#xff1a;1、丰富了我们的业余生活&#xff1b;2、降低了获取知识的成本&#xff0c;降低了提升工作的能力的成本&#xff0c;提高了工作的效率&#xff0c;可以快速建立良好的人脉关系&#xff1b;3、让购物变得更加简单便捷&#xff1b;4、朋友间深度沟通与交流越来…

STL17-函数对象

仿函数&#xff1a; #include<iostream> #include<vector> #include<algorithm> using namespace std; //仿函数&#xff08;函数对象&#xff09;重载“&#xff08;&#xff09;”操作符 使类对象可以像函数那样调用 //仿函数是一个类&#xff0c;不是一个…

STL18常用算法

#include<iostream> #include<algorithm> #include<vector> using namespace std; //transform 将一个容器中的元素搬运在另一个容器中 #if 0 //错误 struct PrintVector {void operator()(int v) {cout << v << " ";} }; void test0…

php中页面平滑回到顶部代码,原生JS实现平滑回到顶部组件

返回顶部组件是一种极其常见的网页功能&#xff0c;需求简单&#xff1a;页面滚动一定距离后&#xff0c;显示返回顶部的按钮&#xff0c;点击该按钮可以将滚动条滚回至页面开始的位置。实现思路也很容易&#xff0c;只要改变document.documentElement.scrollTop或document.bod…

C++基础01-C++对c的增强

所谓namespace&#xff0c;是指标识符的各种可见范围。C标准程序库中的所 有标识符都被定义于一个名为std的namespace中。 一 &#xff1a;<iostream>和<iostream.h>格式不一样&#xff0c;前者没有后缀&#xff0c;实际上&#xff0c; 在你的编译器include文件夹…

C++基础02-C++对c的拓展

变量名实质上是一段连续存储空间的别名&#xff0c;是一个标号(门牌号) 通过变量来申请并命名内存空间. 通过变量的名字可以使用存储空间. 变量名&#xff0c;本身是一段内存的引用&#xff0c;即别名(alias). 引用可以看作一个已定义变量的别名。 引用的语法&#xff…

php小程序onload,微信小程序 loading 组件实例详解

这篇文章主要介绍了微信小程序 loading 组件实例详解的相关资料,需要的朋友可以参考下loading通常使用在请求网络数据时的一种方式&#xff0c;通过hidden属性设置显示与否主要属性&#xff1a;wxml显示loading正在加载jsPage({data:{// text:"这是一个页面"hiddenLo…

C++基础04-类基础

一、类和对象 面向对象三大特点&#xff1a;封装、继承、多态。 struct 中所有行为和属性都是 public 的(默认)。C中的 class 可以指定行为和属性的访问方式。 封装,可以达到,对内开放数据,对外屏蔽数据,对外提供接口。达到了信息隐蔽的功能。 class 封装的本质,在于将数…

C++基础05-类构造函数与析构函数

总结&#xff1a; 1、类对象的作用域为两个{}之间。在遇到}后开始执行析构函数 2、当没有任何显式的构造函数&#xff08;无参&#xff0c;有参&#xff0c;拷贝构造&#xff09;时&#xff0c;默认构造函数才会发挥作用 一旦提供显式的构造函数&#xff0c;默认构造函数不复…

PHP网站配置项,Thinkphp5通用网站后台配置项的动态添加及更新

一、引入无论平时我们自己制作&#xff0c;还是浏览别人的网站&#xff0c;它都具有其相应的一些共用的、通用的属性&#xff0c;比如&#xff1a;网站的名字&#xff0c;关键字、备案号、分页数量、是否开启缓存等信息。一些网站可能将配置项写死在后台&#xff0c;无法动态更…

oracle 查询cpu 100%,Oracle 11g中查询CPU占有率高的SQL

oracle版本&#xff1a;oracle11g背景&#xff1a;今天在Linux中的oracle服务上&#xff0c;运用top命令发现许多进程的CPU占有率是100%。操作步骤&#xff1a;以进程PID:7851为例执行以下语句&#xff1a;方法一&#xff1a;(1)通过PID&#xff0c;查得相对应的系统进程对应的…

C++基础08-this指针-const修饰成员函数-函数返回引用/值

一、this指针 1、C类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效&#xff01; 2、C中类的普通成员函数都隐式包含一个指向当前对象的this指针。 3、静态成员函数、成员变量属于类 4、静态成员函数与普通成员函数的区别 静态成员函数不包含指…

C++基础09-货物售卖和MyArray实现

1、货物出货与进货 #if 0 #include<iostream> using namespace std; /* 某商店经销一种货物。货物购进和卖出时以箱为单位。各箱 的重量不一样&#xff0c;因此商店需要记录目前库存的总重量&#xff0c;现在用 C模拟商店货物购进和卖出的情况 */ class Goods { public:…