我的编程空间,编程开发者的网络收藏夹
学习永远不晚

double类型精度丢失问题以及解决方法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

double类型精度丢失问题以及解决方法

double类型精度丢失问题:

(1)加法运算。

public static void main(String[] args) {double number1 = 1;double number2 = 20.2;double number3 = 300.03;double result = number1 + number2 + number3;System.out.println("使用double运算结果: "+result);}

打印结果如下:

        使用double运算结果: 321.22999999999996。。。

 (2)减法运算。

double d1 = 2.11;double d2 = 2.10;System.out.println( d1 - d2);

口算结果是0.01。可是用程序执行出来的结果却出乎意料,执行结果为0.009999999999999787。

(3)乘法运算。

public static void main(String[] args) {    double a = 152.70;    System.out.println(a);    System.out.println(a * 100);//15280.000000000002    System.out.println(Math.round(a * 100));//还凑合     double b = 152.70;    System.out.println(b);    System.out.println(b * 100);//15269.999999999998    System.out.println((int)(b * 100));//15269 错误!!!不是想要的}

然后把15280.000000000002 - 15280 = 1.8189894035458565E-12这个结果返回给了前端, 

前端拿去和0比较, 结果就炸了

原因:就是double的精度问题, 单个double数可能就有损失精度,,多个double数运算(减法和乘法都输入加法)可能导致损失的精度更多。。。

为什么double类型会出现精度丢失问题?


        我们知道,计算机发展了如此长的一段时间,但它始终只能识别0和1(即二进制)。无论我们使用哪种编程语言,在哪种编译环境下工作,都要先把源代码翻译成二进制的机器码才能被计算机所识别。

        举个简单的例子,在源程序里面2.4,是十进制的,但计算机不能直接识别,要先编译成二进制。

        那么问题来了,2.4的二进制并非是精确的2.4,反而是最为接近的二进制表示是2.39999999996。

        原因在于浮点数由两部分组成:指数和尾数。如果知道怎么进行浮点数的二进制和十进制转换,应该不难理解。

        如果在这个转换过程中,浮点数参与了计算,那么在转换的过程中就会变得不可预知,并且变得不可逆。我们有理由相信,就是在这个过程中,发生了精度的丢失。

        而至于为什么有些浮点计算会得到准确的结果,应该也是碰巧那个计算二进制和十进制之间能够准确转换。

而当输出单个浮点型数据时,可以正确输出,如:

Double num3 = 2.4;System.out.println(num3);

输出的结果是2.4,而不是2.39999999996。也就是说,不进行浮点计算的时候,在十进制里浮点数能正确显示。

        这正印证了上面的说法,即如果浮点数参与了计算,那么浮点数二进制与十进制的转换过程就会变得不可预知,并且变得不可逆。

        结论就是,在Java中,浮点数不精确,因为计算机内部无法用二进制的小数来精确的表达。

经典问题:浮点数精度丢失

精度丢失的问题是在其他计算机语言中也都会出现,float和double类型的数据在执行二进制浮点运算的时候,并没有提供完全精确的结果。

        产生误差不在于数的大小,而是因为数的精度。

关于浮点数存储精度丢失的问题,话题过于庞大,感兴趣的同学可以自行搜索一下:【解惑】剖析float型的内存存储和精度丢失问题

        这里简单讨论一下十进制数转二进制为什么会出现精度丢失的现象,十进制数分为整数部分和小数部分,我们分开来看看就知道原因为何:

十进制整数如何转化为二进制整数?
将被除数每次都除以2,只要除到商为0就可以停止这个过程。

5 / 2 = 2 余 1

2 / 2 = 1 余 0

1 / 2 = 0 余 1

// 结果为 101

这个算法永远都不会无限循环,整数永远都可以使用二进制数精确表示,但小数呢?

十进制小数如何转化为二进制数?
每次将小数部分乘2,取出整数部分,如果小数部分为0,就可以停止这个过程

0.1 * 2 = 0.2 取整数部分0

0.2 * 2 = 0.4 取整数部分0

0.4 * 2 = 0.8 取整数部分0

0.8 * 2 = 1.6 取整数部分1

0.6 * 2 = 1.2 取整数部分1

0.2 * 2 = 0.4 取整数部分0

//... 我想写到这就不必再写了,你应该也已经发现,上面的过程已经开始循环,小数部分永远不能为0

这个算法有一定概率会存在无限循环,即无法用有限长度的二进制数表示十进制的小数,这就是精度丢失问题产生的原因。

浮点数并不适合用于精确计算,而适合进行科学计算。

        float和double型用来表示带有小数点的数,那为什么我们不称他们为小数或实数,要叫浮点数呢?是因为这些数都以科学计数法的形式存储。

        当一个数如50.534,转换成科学计数法的形式为5.053e1,它 的小数点移动到了一个新的位置(即浮动了)。

        可见,浮点数本来就是用于科学计算的,用来进行精确计算实在太不合适了。

如何用BigDecimal解决double精度问题?


 我们已经明白为什么精度会存在丢失现象,那么我们就应该知道,当某个业务场景对double数据的精度要求非常高时,就必须采取某种手段来处理这个问题,

        这也是BigDecimal为什么会被广泛应用于金额支付场景中的原因啦。

        BigDecimal类位于java.math包下,用于对超过16位有效位的数进行精确的运算。

        一般来说,double类型的变量可以处理16位有效数,

        但实际应用中,如果超过16位,就需要BigDecimal类来操作。

new BigDecimal(double val)
        该方法是不可预测的,以0.1为例,你以为你传了一个double类型的0.1,最后会返回一个值为0.1的BigDecimal吗?不会的,原因在于,0.1无法用有限长度的二进制数表示,无法精确地表示为双精度数,最后的结果会是0.100000xxx。

new BigDecimal(String val)
        该方法是完全可预测的,也就是说你传入一个字符串"0.1",他就会给你返回一个值完全为0,1的BigDecimal,官方也表示,能用这个构造函数就用这个构造函数叭。

BigDecimal.valueOf(double val)
        第二种构造方式已经足够优秀,可你还是想传入一个double值,怎么办呢?官方其实提供给你思路并且实现了它,可以使用Double.toString(double val)先将double值转为String,再调用第二种构造方式,你可以直接使用静态方法:valueOf(double val)。

        总结:将double转为BigDecimal的时候,需要先把double转换为字符串,然后再作为BigDecimal(String val)构造函数的参数,这样才能避免出现精度问题。

​​​​​​​

以下是提供的double精确运算工具类(如果计算的数值总和很大很大,超过50 0000,请使用带有ToStr的方法,不然double数字会转成科学计数法显示)

package com.phone.common_library; import java.math.BigDecimal; public class BigDecimalManager {         public static double additionDouble(double m1, double m2) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.add(p2).doubleValue();    }         public static double additionDouble(double m1, double m2, int scale) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.add(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();    }         public static String additionDoubleToStr(double m1, double m2, int scale) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.add(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();    }         public static double subtractionDouble(double m1, double m2) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.subtract(p2).doubleValue();    }         public static double subtractionDouble(double m1, double m2, int scale) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.subtract(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();    }         public static String subtractionDoubleToStr(double m1, double m2, int scale) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.subtract(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();    }         public static double multiplicationDouble(double m1, double m2) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.multiply(p2).doubleValue();    }         public static double multiplicationDouble(double m1, double m2, int scale) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();    }         public static String multiplicationDoubleToStr(double m1, double m2, int scale) {        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();    }         public static double divisionDouble(double m1, double m2, int scale) {        if (scale < 0) {            throw new IllegalArgumentException("Parameter error");        }        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();    }            public static String divisionDoubleToStr(double m1, double m2, int scale) {        if (scale < 0) {            throw new IllegalArgumentException("Parameter error");        }        BigDecimal p1 = new BigDecimal(Double.toString(m1));        BigDecimal p2 = new BigDecimal(Double.toString(m2));        return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).toPlainString();    } }

·测试、验证问题是否解决:

public static void main(String[] args) {double result1 = additionDouble(1, 20.2);double result2 = additionDouble(result1, 300.03);System.out.println("使用BigDecimal运算结果:" + result2);}

结果如下: 

使用BigDecimal运算结果:321.23

完美解决了double类型数据加减操作时精度丢失的问题。 

如对此有疑问,请联系qq1164688204。

推荐Android开源项目

项目功能介绍:RxJava2和Retrofit2项目,添加自动管理token功能,添加RxJava2生命周期管理,使用App架构设计是MVP模式和MVVM模式,同时使用组件化,部分代码使用Kotlin,此项目持续维护中。

项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2

来源地址:https://blog.csdn.net/NakajimaFN/article/details/125912091

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

double类型精度丢失问题以及解决方法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

js浮点数精度丢失的问题及解决方法

本篇内容介绍了“js浮点数精度丢失的问题及解决方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!说明1、在数学计算中,小数会有一定的误差,这
2023-06-20

Golang浮点数精度丢失问题扩展包的解决方法

小编给大家分享一下Golang浮点数精度丢失问题扩展包的解决方法,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!PS: 今天在做项目进行精度处理的时候出现精度丢失问题,在这里跟大家分享下扩展包解决方案, 注意:这个问题是可能
2023-06-08

SpringBoot分页的实现与long型id精度丢失问题的解决方案介绍

在以后的开发中,当全局唯一id的生成策略生成很长的Long型数值id之后会超过JS对Long型数据处理的能力范围,可能发生精度丢失而造成后端方法失效,我们要学会解决。分页功能虽然简单但是非常重要,对于刚接触项目的人一定要重点注意
2022-11-13

SpringBoot怎么解决Long型数据转换成json格式时丢失精度问题

这篇“SpringBoot怎么解决Long型数据转换成json格式时丢失精度问题”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇
2023-07-02

WordPress分类目录、标签丢失问题的解决方法

今天偶然注意到WordPress的分类和标签消失了,开始以为是数据库表损坏,于是通过phpMyAdmin修复表,但是注意到修复表的过程中有一个错误:Can't create/write to enGtcWTgkXfile '/tmp/...
2022-06-12

解决页面js接受Long型损失精度问题(最新解决方案)

这篇文章主要介绍了解决页面js接受Long型损失精度问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-03-09

SpringBoot雪花算法主键ID传到前端后精度丢失问题的解决

本文主要介绍了SpringBoot雪花算法主键ID传到前端后精度丢失问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2022-11-13

Java雪花算法生成的ID传输前端后精度丢失问题怎么解决

本文小编为大家详细介绍“Java雪花算法生成的ID传输前端后精度丢失问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java雪花算法生成的ID传输前端后精度丢失问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一
2023-07-05

iOS浮点类型精度问题的原因与解决办法是什么

这篇文章主要为大家分析了iOS浮点类型精度问题的原因与解决办法是什么的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“iOS浮点类型精度问题的原因与解决办法是什
2023-06-29

C++中常见的数据类型转换问题及解决方法

C++ 中常见的数据类型转换问题及解决方法引言:在 C++ 编程中,经常会遇到不同数据类型之间的转换问题。正确地进行数据类型转换是保证程序正确性和性能的关键之一。本文将介绍一些常见的数据类型转换问题,并提供相应的解决方法和具体的代码示例。一
2023-10-22

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录