首页 常见问题 double浮点数运算为啥会丢失精度

double浮点数运算为啥会丢失精度

Jun 26, 2019 am 09:15 AM
double 浮点数

double浮点数运算为啥会丢失精度

前言:在工作中,谈到有小数点的加减乘除都会想到用BigDecimal来解决,但是有很多人对于double或者float为啥会丢失精度一脸茫然。还有BigDecimal是怎么解决的?话不多说,我们开始。

1.浮点数是啥?

 浮点数是计算机用来表示小数的一种数据类型,采用科学计数法。在java中,double是双精度,64位,浮点数,默认是0.0d。float是单精度,32位.浮点数,默认是0.0f;

c96f0ea5-baf9-4457-818d-61647df176b6.jpg

 在内存中存储

float      符号位(1bit)   指数(8 bit)     尾数(23 bit)
double   符号位(1bit)  指数(11 bit)   尾数(52 bit)


float在内存中指数是8bit,由于阶码实际存储的是指数的移码,假设指数的真值是e,阶码为E,则有E=e+(2^n-1 -1)。其中 2^n-1 -1是IEEE754标准规定的指数偏移量,根据这个公式我们可以得到 2^8 -1=127。于是,float的指数范围为-128   +127,而double的指数范围为-1024  +1023。其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。


float的范围为-2^128 ~ +2^127,也即-3.40E+38 ~ +3.40E+38;
double的范围为-2^1024 ~ +2^1023,也即-1.79E+308 ~ +1.79E+308

2.走进失真之科学计数法

 我们先说说科学计数法,科学计数法是一种简化计数的方法,用来近似表示一个极大或极小且位数较多的数,对于位数较小的数值,科学计数法没有什么优势,但对于位数较多的数值其计数方法的优势就非常明显了。例如:光的速速是300000000米/秒,全世界人口数大约是6100000000。类似光的速度和世界人口数这样大数值的数,读、写都很不方便,所以光的速度可以写成3*10^8,全世界人口数可以写成6.1*10^9。所以计算器用科学计数法表示光速是3E8,世界人口数大约是6.1E9。

我们小时候玩计算器喜欢疯狂的累加或者累减,到最后计算器就会显示下图。这个就是科学计数法显示的结果 

868eb832-e8ce-4a08-a423-4f8de129bd9e.jpg

那图中真实的值是  -4.86*10^11=-486000000000。十进制科学计数法要求有效数字的整数部分必须在【1,9】区间内。

 

3.走进失真之精度

计算机在处理数据都涉及到数据的转换和各种复杂运算,比如,不同单位换算,不同进制(如二进制十进制)换算等,很多除法运算不能除尽,比如10÷3=3.3333.....无穷无尽,而精度是有限的,3.3333333x3并不等于10,经过复杂的处理后得到的十进制数据并不精确,精度越高越精确。float和double的精度是由尾数的位数来决定的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。float:2^23 = 8388608,一共七位,由于最左为1的一位省略了,这意味着最多能表示8位数: 28388608 = 16777216 。有8位有效数字,但绝对能保证的为7位,也即float的精度为7~8位有效数字;double:2^52 = 4503599627370496,一共16位,同理,double的精度为16~17位。

9a287b4c-dc57-4bb6-aef5-175b9812f2e5.jpg

当到达一定值自动开始使用科学计数法,并保留相关精度的有效数字,所以结果是个近似数,并且指数为整数。在十进制中小数有些是无法完整用二进制表示的。所以只能用有限位来表示,从而在存储时可能就会有误差。对于十进制的小数转换成二进制采用乘2取整法进行计算,取掉整数部分后,剩下的小数继续乘以2,直到小数部分全为0。

如遇到 

01f67b44-41bd-41f3-8dfc-96ded458d350.jpg

输出是 0.19999999999999998

double类型 0.3-0.1的情况。需要将0.3转成二进制在运算


0.3 * 2 = 0.6 => .0 (.6)取0剩0.6
0.6 * 2 = 1.2 => .01 (.2)取1剩0.2
0.2 * 2 = 0.4 => .010 (.4)取0剩0.4
0.4 * 2 = 0.8 => .0100 (.8) 取0剩0.8
0.8 * 2 = 1.6 => .01001 (.6)取1剩0.6
.............

 f69ad10d-ac33-4236-a6db-de618a119d9d.jpg

 

3.总结

  看完上面,大概清楚了为啥浮点数会有精度问题。简单来说float和double类型主要是为了科学计算和工程计算而设计,他们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近和计算而精心设计的。然而,他们并没有提供完全精确的结果,所以不应该被用于精确的结果的场合。浮点数达到一定大的数会自动使用科学计数法,这样的表示只是近似真实数而不等于真实数。当十进制小数位转换二进制的时候也会出现无限循环或者超过浮点数尾数的长度。
 

4.那我们怎么用BigDecimal来解决?

大家看下面的两个输出

ca3ccb4f-8e73-4b38-888f-925359b9846f.jpg

输出结果:

0.299999999999999988897769753748434595763683319091796875
0.3

图上阿里的代码约束插件已经标注警告,让我使用String参数的构造方法创建BigDecimal。因为double不能精确地表示为0.3(任何有限长度的二进制),构造方法传递的值也是不完全等于0.3。大家在使用BigDecimal的时候一定要用String参数的构造方法来创建。说到这里,是木有还有好奇的宝宝有疑问,BigDecimal的原理是啥?为啥它就没有问题呢?其实原理很简单,BigDecimal是不可变的,可以用来表示任意精度的带符号十进制数。double之所以会出问题,是因为小数点转二进制丢失精度。BigDecimal在处理的时候把十进制小数扩大N倍让它在整数上进行计算,并保留相应的精度信息。至于BigDecimal是怎么保存的可以翻阅一下源代码。

更多常见问题的相关技术文章,请访问常见问题栏目进行学习!

以上是double浮点数运算为啥会丢失精度的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

php怎么将字符串转换成小数 php怎么将字符串转换成小数 Mar 22, 2023 pm 03:22 PM

PHP 是一门功能强大的编程语言,广泛应用于 Web 开发领域。其中一个非常常见的情况是需要将字符串转换为小数。这在进行数据处理的时候非常有用。在本文中,我们将介绍如何在 PHP 中将字符串转换为小数。

PHP浮点数四舍五入法 PHP浮点数四舍五入法 Mar 21, 2024 am 09:21 AM

这篇文章将为大家详细讲解有关PHP浮点数四舍五入法,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。PHP浮点数四舍五入法概述浮点数在计算机中表示为小数点后跟指数,然而,它们通常以有限位数的近似值存储。当需要将浮点数四舍五入到特定精度时,有几种方法可以实现。方法1.round()函数round()函数将浮点数四舍五入为最接近的整数。它接受浮点数和可选的精度参数。例如:$num=1.55;echoround($num);//输出:2echoround($num,1)

PHP浮点数计算误差原因及避免策略 PHP浮点数计算误差原因及避免策略 Feb 27, 2024 pm 06:33 PM

PHP作为一种流行的服务器端脚本语言,在进行浮点数计算时常常会遇到精度丢失或计算误差的问题,这些问题可能会对程序的准确性和稳定性造成影响。本文将探讨PHP浮点数计算误差的原因,并提出一些避免策略,同时给出具体的代码示例供参考。1.PHP浮点数计算误差的原因在计算机中,浮点数是以二进制形式表示的,而二进制并不能精确地表示所有的十进制小数,这就导致了浮点数的精

使用strconv.FormatFloat函数将浮点数转换为字符串 使用strconv.FormatFloat函数将浮点数转换为字符串 Jul 25, 2023 am 11:45 AM

使用strconv.FormatFloat函数将浮点数转换为字符串在Go语言中,我们经常需要将浮点数转换为字符串类型,用于输出或者存储等需求。Go语言中提供了strconv包,其中的FormatFloat函数可以将浮点数转换为字符串类型。FormatFloat函数有三个参数:f表示要转换的浮点数,fmt表示格式,以及prec表示要保留的小数位数。其中,f参数

C++程序将double类型的变量转换为int类型 C++程序将double类型的变量转换为int类型 Aug 25, 2023 pm 08:25 PM

在C++中,int类型的变量只能保存正整数或负整数值;它们不能保存小数值。有float和double值可用于此目的。为了存储小数点后最多七位的小数,创建了双精度数据类型。整数到双精度数据类型的转换可以由编译器自动完成(称为“隐式”转换),也可以由程序员向编译器显式请求(称为“显式”转换)。在接下来的部分中,我们将介绍各种转换方法。隐式转换编译器自动执行隐式类型转换。要实现这一点,需要两个变量——一个是浮点类型,另一个是整数类型。当我们简单地将浮点值或变量分配给整数变量时,编译器将处理所有其他事情

深入浅出解析PHP BCMath:释放数字运算的潜力 深入浅出解析PHP BCMath:释放数字运算的潜力 Feb 23, 2024 am 09:10 AM

:一、BCMath简介BCMath是PHP内置的一个扩展库,专门用于处理大型整数和浮点数运算。它提供了丰富的函数来进行加、减、乘、除、平方、开方等各种数学运算,并且支持多种进制的数字表示。二、BCMath的优势BCMath相较于php原生提供的算术运算符和函数,主要有以下几个方面的优势:精度更高:BCMath的运算结果可以保留更多的有效数字,这对于涉及大数计算的场景尤为重要。范围更广:BCMath可以处理比PHP原生数据类型更大的数字,从而避免溢出或精度丢失的问题。功能更丰富:BCMath提供了

如何在PHP中将字符串转换为浮点数 如何在PHP中将字符串转换为浮点数 Mar 27, 2024 pm 12:48 PM

将字符串转换为浮点数是在PHP中常见的操作,可以通过内置的方法来实现。首先要确保字符串是合法的浮点数格式,才能成功地转换为浮点数。下面将详细介绍如何在PHP中将字符串转换为浮点数,并提供具体的代码示例。一、使用(float)强制转换在PHP中,将字符串转换为浮点数最简单的方式就是使用强制转换。强制转换的方式是在字符串前加上(float)即可,PHP会自动将其

如何使用C#中的Math.Truncate函数对浮点数进行向下取整 如何使用C#中的Math.Truncate函数对浮点数进行向下取整 Nov 18, 2023 pm 02:02 PM

如何使用C#中的Math.Truncate函数对浮点数进行向下取整,需要具体代码示例在C#编程中,经常会遇到需要对浮点数进行取整的情况。其中,向下取整是一种常见的操作,可以利用C#中的Math.Truncate函数实现。本文将详细介绍Math.Truncate函数的用法,并提供具体的代码示例。Math.Truncate函数是C#中的一个数学函数,用于将一个浮