Java中的浮点数据(float、double)进行算术运算时出错的问题剖析

2023-10-31

本文主题:

  • 对浮点数进行算术运算时,为何运算结果不正确?
  • BigDecimal类型、常用方法的讲解。
  • 简单的浮点数算术运算工具类的设计。

在Java前面讲解float、double两种基本浮点类型时已经指出,这两个基本类型的浮点数容易引起精度丢失。其实,不仅是Java,很多编程语言也存在这个问题。先看如下程序:

public class DoubleTest {

	public static void main(String[] args) {
		System.out.println("以下为double数据类型的运算结果:");
		
		System.out.println("0.05 + 0.01 = " + (0.05+0.01));
		System.out.println("1.00 - 0.42 = " + (1.0-0.42));
		System.out.println("4.015 * 100 = " + (4.015*100));
		System.out.println("123.3 / 100 = " + (123.3/100));
	}
}

程序运行结果为:

 

上面的程序运行结果表明,Java的double类型会发生精度丢失,尤其在进行算术运算时更容易发生这种情况。为了能精确表示、计算浮点数,Java提供了BigDecimal类,该类提供了大量的构造器用于创建BigDecimal对象,包括把所有的基本数值型变量转换成一个BigDecimal对象,也包括利用数字字符串、数字字符数组来创建BigDecimal对象。

查看BigDecimal类的BigDecimal(double val)构造器的详细说明时,可以看到不推荐使用该构造器的说明,主要是因为使用该构造器时有一定的不可预知性。当程序使用BigDecmal bd = new BigDecimal(0.1); 来创建一个BigDecimal对象时,参数的值并不一定是0.1,它实际上是一个近似0.1的数。这是因为double类型无法准确/精确的表示出0.1这个浮点数,所以传入BigDecimal构造器的值不会正好等于0.1(虽然表面上看貌似等于该值)。

而如果使用BigDecimal(String val)构造器来创建一个对象,那么它的结果是可预知的----写入BigDecmal bd = new BigDecimal("0.1"); 将创建一个BigDecimal,它正好等于预期的0.1。因此通常建议优先使用基于String的构造器。

如果必须使用double浮点数作为BigDecimal构造器的参数时,不要直接将该double浮点数作为构造器参数创建BigDecimal对象,而是应该通过BigDecmal bd = BigDecimal.valueOf(double val); 这一静态方法来创建BigDecimal对象。

BigDecimal类提供了add()、substract()、multiply()、divide()、pow()等方法对精确浮点数进行常规算术运算。下面的程序示范了BigDecimal的基本运算。

import java.math.BigDecimal;

public class BigDecimalDemo {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		BigDecimal f1 = new BigDecimal("0.05");
		BigDecimal f2 = new BigDecimal(0.01);
		BigDecimal f3 = BigDecimal.valueOf(0.01);// BigDecimal.valueOf(0.01);
		
		System.out.println("使用String作为BigDecimal构造器参数:");
		System.out.println("0.05 + 0.01 = "+f1.add(f3));
		System.out.println("0.05 - 0.01 = "+f1.subtract(f3));
		System.out.println("0.05 * 0.01 = "+f1.multiply(f3));
		System.out.println("0.05 / 0.01 = "+f1.divide(f3));
		
		System.out.println("使用double作为BigDecimal构造器参数:");
		System.out.println("0.05 + 0.01 = "+f2.add(f3));
		System.out.println("0.05 - 0.01 = "+f2.subtract(f3));
		System.out.println("0.05 * 0.01 = "+f2.multiply(f3));
		System.out.println("0.05 / 0.01 = "+f2.divide(f3));
		
	}
}

程序运行结果为:

 

 从上面的运行结果可以看出BigDecimal进行算术运算的效果,而且可以看出创建BigDecimal对象时,一定要使用String对象作为构造器的参数,而不是直接使用double数字。

如果我们经常要进行一些浮点数的算术运算,可以考虑以BigDecimal为基础定义一个算术运算的工具类----Arith,该工具的代码如下。

/**
 * 文件名:Arith.java
 * 功能描述:Arith工具类,是一个无法实例化的类,它只有静态方法
 */
import java.math.BigDecimal;

public class Arith {
	// 默认除法运算精度
	private static final int DEF_DIV_SCALE = 10;
	// 构造器私有,让这个类不能实例化
	private Arith() {
		
	}
	
	// 提供精确的加法运算
	public static double add(double v1,double v2) {
		BigDecimal b1 = BigDecimal.valueOf(v1);
		BigDecimal b2 = BigDecimal.valueOf(v2);
		
		return b1.add(b2).doubleValue();
	}
	
	// 提供精确的减法运算
	public static double substract(double v1,double v2) {
		BigDecimal b1 = BigDecimal.valueOf(v1);
		BigDecimal b2 = BigDecimal.valueOf(v2);
		
		return b1.subtract(b2).doubleValue();
	}

	// 提供精确的乘法运算
	public static double multiply(double v1,double v2) {
		BigDecimal b1 = BigDecimal.valueOf(v1);
		BigDecimal b2 = BigDecimal.valueOf(v2);
		
		return b1.multiply(b2).doubleValue();
	}

	// 提供精确的除法运算
	public static double divide(double v1,double v2) {
		BigDecimal b1 = BigDecimal.valueOf(v1);
		BigDecimal b2 = BigDecimal.valueOf(v2);
		
		return b1.divide(b2).doubleValue();
	}

}

ArithTest.java测试文件:

/**
 * 文件名:ArithTest.java
 * 功能描述:Arith工具测试
 */
public class ArithTest {
	public static void main(String[] args) {
		System.out.println("使用Arith工具类进行浮点数的算术运算:");
		System.out.println("0.05 + 0.01 = "+Arith.add(0.05,0.01));
		System.out.println("0.05 - 0.01 = "+Arith.substract(0.05,0.01));
		System.out.println("4.015 * 100 = "+Arith.multiply(4.015,100));
		System.out.println("123.5 / 100 = "+Arith.divide(123.5,100));
	}

}

运行结果如下:

  

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中的浮点数据(float、double)进行算术运算时出错的问题剖析 的相关文章

随机推荐

  • 如何查看自己的电脑硬盘是不是固态硬盘

    前段时间给自己的电脑C盘换了一个固态硬盘 结果今天发现运行比较慢 就特意查了一下是不是固态的 以下是具体方法 需要的可以参考 上图红框里面得到媒体类型 显示固态硬盘它就是固态硬盘 显示其他驱动的就是机械硬盘
  • Android dip(dp) 与 sp的自适应问题

    转自 http www oschina net question 272860 70761 今天碰到的一个问题 感觉应该其他人也会碰到 拿来分享一下 我们都知道android在开发配置界面时一般都会使用dip和sp这种逻辑长度单位来实现屏幕
  • Matlab 中prod函数的使用

    B prod A 将A矩阵不同维的元素的乘积返回到矩阵B Matlab中文论坛 如果A是向量 prod A 返回A向量的乘积 如果A是矩阵 prod A 将A看作列向量 返回每一列元素的乘积并组成一个行向量B 如果A是多维数组 prod A
  • jenkins_获取json

    import jenkins import yaml import json import urllib parse import xmltodict from xml etree import ElementTree def LoginJ
  • ubuntu 16.04 编译与安装 absl 库(cartographer库安装之1-absl)

    set o errexit set o verbose git clone https github com abseil abseil cpp git cd abseil cpp git checkout d902eb869bcfacc1
  • 【CTF】web-文件上传篇1

    upload labs靶场 1 前端js限制 2 修改Content Type 3 htaccess绕过 4 00截断 1 前端js限制 场景 使用 burp 抓包 在点击上传时 直接弹出了提示 这时 burp 并没有抓到数据包 说明是在前
  • 深度学习常用Linux命令

    Linux命令查询手册 https www linuxcool com 目录 一 常用快捷键 二 关于docker的使用 1 进入docker 2 设置使用某块dcu代码 3 docker与主机之间的文件传输 三 关于文件和文件夹 1 文件
  • ADS错误之the session file 'C:\user\username\default-1-2-0-0.ses' could not be loaded

    刚在编arm程序的时候遇到了这个错误 去网上搜了下资料 查到关于这个错误信息的解决方法 如下 如果出现错误信息 the session file C user username default 1 2 0 0 ses could not b
  • 105-----JS基础-----添加删除记录-修改

    一 代码 本节的代码是对104节的内容进行优化 因为按照上一节的内容这样写的话 会创建过多的资源 造成资源浪费 导致用户体验不好 当界面复杂起来 可能会变得很卡 造成用户体验不好 所以需要进行优化 下面的例子 避免了人为的创建多个节点元素
  • 总结一下刷过的题

    今年刷题还是挺爽的 1 Two Sum 用map建立number gt index映射即可 2 Add Two Numbers 利用链表的基本操作来模拟高精度相加过程 链表头为最低位 3 Longest Substring Without
  • Python人脸识别项目-人脸检测

    人脸检测 接下来我们先拿一个简单的人脸检测项目练练手 我们的目标是实现通过摄像头实时检测人脸 这里我们要用到一个分类器这个分类器可以从github上下载也可以从我们的Python第三包里直接用 在cv2包的data文件夹里面 coding
  • C#基础(条件运算符)

    作用 格式 用于比较两个变量或常量 条件运算符 一定存在左右两边的内容 左边内容 条件运算符 右边内容 分类 是否大于 gt 是否小于 lt 是否等于 是否不等于 是否大于等于 gt 是否小于等于 lt 比较的结果 返回的是 一个 bool
  • Deep Learning for Massive MIMO CSI Feedback

    这篇文章是自己之前学习论文的一点心得 是源于AI 无线通信这个比赛 论文百度搜这个 去IEEE官网就可以下载了 C Wen W Shih and S Jin Deep Learning for Massive MIMO CSI Feedba
  • 计算2支股票的M天运动平均价格时间

    计算2支股票的M天运动平均价格时间 题目描述 给定2支股票的开盘价和收盘价的N天历史数据 要求按开盘和收盘 分别计算每支股票的每个日期对应的M天移动平均价格 假定两个股票数据如下 日期 开盘 收盘 第1支股票价格S1 第2支股票价格S2 2
  • 移动端按设计图比例布局

    利用选择器设置全局字体大小 root font size 50px 根据设计图大小转换字体大小 相对于视图750为例 1rem 100px root font size calc 100vw 7 5
  • 北京大学肖臻老师《区块链技术与应用》公开课笔记16——ETH账户篇

    北京大学肖臻老师 区块链技术与应用 公开课笔记 以太坊账户篇 对应肖老师视频 click here 全系列笔记请见 click here About Me 点击进入我的Personal Page BTC系统是基于交易的账本 系统中并未显示记
  • 【密码算法 之八】Hash类算法(单向散列函数) MD5 \ SHA1 \ SHA224 \ SHA256 \ SHA384 \ SHA512等浅析

    1 综述 Hash算法 又称单向散列函数 one way hash function 单向散列函数有一个输入和一个输出 其中输入称为消息 message 输出称为散列值 hash value 单向散列函数可以根据消息的内容计算出散列值 而散
  • 1025 反转链表python3无超时

    终于在卡了好几天之后想到了解决办法 这道题给出的代码并不能保证完全成功 不超时的概率大概在50 文章目录 一 最初的代码 二 代码改进 一 最初的代码 这个问题一般解决思路如下 获得正序链表 根据条件反转链表 输出链表 代码如下 usr b
  • 【python】正则表达式匹配数据

    前言 使用正则表达式处理数据 可进行字符串匹配 提取和替换等操作 在python中 通过re库完成正则匹配的操作 一 正则语法规则 1 常用匹配符 模式 描述 匹配字符串开头 匹配字符串结尾 匹配任意字符 匹配前面的字符零次或多次 匹配前面
  • Java中的浮点数据(float、double)进行算术运算时出错的问题剖析

    本文主题 对浮点数进行算术运算时 为何运算结果不正确 BigDecimal类型 常用方法的讲解 简单的浮点数算术运算工具类的设计 在Java前面讲解float double两种基本浮点类型时已经指出 这两个基本类型的浮点数容易引起精度丢失