如何解决Java 中的精度问题

在 Java 编程中,处理浮点数和超大整数时常常会遇到精度丢失和数值溢出的困扰。为了确保计算结果的精确性,尤其是在金融计算等对精度要求极高的场景中,我们需要使用 BigDecimal 和 BigInteger 类。本文将详细介绍浮点数精度丢失的原因、如何解决该问题,以及如何处理超出 long 范围的整数。

一、浮点数运算精度丢失的原因

1. 浮点数的存储机制

计算机使用二进制(binary)系统来存储数据,浮点数也不例外。浮点数在计算机中是以科学记数法的形式存储的,即:

浮点数=尾数×2指数浮点数=尾数×2指数

在 Java 中,常见的浮点数类型有 float(32 位)和 double(64 位)。其中,float 使用 1 位符号位、8 位指数位和 23 位尾数位;double 使用 1 位符号位、11 位指数位和 52 位尾数位。

2. 精度丢失的原因

浮点数精度丢失的主要原因在于某些十进制小数无法被精确地表示成二进制小数。我们以十进制的 0.2 为例,看看它如何转换成二进制:

  • 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.2 在二进制中是一个无限循环小数。因此,计算机只能截断存储,从而导致精度丢失。

3. 代码示例

我们来看一个具体的代码示例:

java

public class FloatPrecisionLoss {public static void main(String[] args) {float a = 2.0f - 1.9f;float b = 1.8f - 1.7f;System.out.println(a); // 输出:0.100000024System.out.println(b); // 输出:0.099999905System.out.println(a == b); // 输出:false}
}

在上述代码中,我们分别计算了 2.0 - 1.9 和 1.8 - 1.7,预期结果都是 0.1,但实际上得到的结果是不同的。这是因为 0.1 无法被精确地表示成二进制小数,从而导致了精度丢失。

4. 实际应用中的注意事项

在实际应用中,我们需要特别注意浮点数的精度问题,尤其是在金融计算、科学计算等对精度要求较高的场景中。为避免精度丢失,可以考虑以下几种方法:

  • 使用 BigDecimal:Java 提供了 BigDecimal 类来进行高精度的浮点数运算。虽然计算速度较慢,但可以保证精度。

  • 舍入操作:对计算结果进行适当的舍入操作,例如四舍五入,可以减少精度丢失的影响。

  • 避免直接比较浮点数:在比较两个浮点数时,应使用一个小的容差值(epsilon)来判断它们是否相等,例如:

    java

    public class FloatComparison {public static void main(String[] args) {float a = 2.0f - 1.9f;float b = 1.8f - 1.7f;final float EPSILON = 1e-6f;System.out.println(Math.abs(a - b) < EPSILON); // 输出:true}
    }

通过这种方式,可以避免直接比较浮点数带来的问题。

二、如何解决浮点数运算的精度丢失问题?

1. 为什么选择 BigDecimal?

BigDecimal 提供了针对浮点数的高精度运算,其内部采用字符串或字符数组的形式来存储数值,从而避免了二进制浮点数表示法导致的精度丢失问题。与 float 和 double 相比,BigDecimal 可以精确表示任意大小且精度可控的小数。

2. BigDecimal 的常见用法

创建 BigDecimal 对象

为了防止精度丢失,推荐使用 BigDecimal(String) 构造方法或者 BigDecimal.valueOf(double) 静态方法来创建对象。例如:

java

BigDecimal a = new BigDecimal("1.23"); // 推荐
BigDecimal b = BigDecimal.valueOf(1.23); // 推荐
加减乘除

BigDecimal 提供了丰富的方法来进行基本的算术运算:

java

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");// 加法
BigDecimal sum = a.add(b);// 减法
BigDecimal difference = a.subtract(b);// 乘法
BigDecimal product = a.multiply(b);// 除法
BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP); // 保留2位小数,四舍五入

需要注意的是,使用 divide 方法时,推荐使用带有 scale 和 RoundingMode 参数的重载方法,以防止除不尽导致的 ArithmeticException

保留几位小数

通过 setScale 方法可以设置小数点后的位数以及舍入模式:

java

BigDecimal value = new BigDecimal("1.255433");
BigDecimal roundedValue = value.setScale(3, RoundingMode.HALF_DOWN);
System.out.println(roundedValue); // 输出:1.255

3. 实际应用中的案例

金融计算

在金融应用中,精确的数值计算至关重要。例如,银行系统中的利息计算、会计系统中的账目核算等,都需要使用 BigDecimal 来确保结果的准确性。

java

import java.math.BigDecimal;
import java.math.RoundingMode;public class FinancialCalculation {public static void main(String[] args) {BigDecimal principal = new BigDecimal("10000"); // 本金BigDecimal rate = new BigDecimal("0.035"); // 年利率BigDecimal time = new BigDecimal("5"); // 时间,单位为年// 计算利息BigDecimal interest = principal.multiply(rate).multiply(time).setScale(2, RoundingMode.HALF_UP);System.out.println("利息:" + interest); // 输出:利息:1750.00}
}
比较大小

使用 compareTo 方法进行大小比较:

java

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");int comparison = a.compareTo(b);
if (comparison > 0) {System.out.println("a 大于 b");
} else if (comparison < 0) {System.out.println("a 小于 b");
} else {System.out.println("a 等于 b");
}

4. 结论

BigDecimal 提供了高精度的浮点数运算,解决了 float 和 double 类型的精度丢失问题。在需要高精度计算的场景中,BigDecimal 是一个不可或缺的工具。通过正确使用 BigDecimal,我们可以确保计算结果的精确性和可靠性。

三、超过 long 整型的数据应该如何表示?

1. 为什么选择 BigInteger?

在 Java 中,long 类型是最大的基本整型数据类型,占用 64 位,表示的数值范围是从 -9223372036854775808 到 9223372036854775807。当需要处理超过 long 范围的整型数据时,我们需要借助 BigInteger 类来进行处理。BigInteger 内部使用 int[] 数组来存储任意大小的整型数据,支持所有常规的算术运算、比较运算以及位运算。

2. BigInteger 的常见用法

创建 BigInteger 对象

BigInteger 提供了多种构造方法,可以通过字符串、字节数组或指定的基数来创建对象:

java

BigInteger bigInt1 = new BigInteger("9223372036854775808"); // 使用字符串
BigInteger bigInt2 = new BigInteger("123456789012345678901234567890");
BigInteger bigInt3 = new BigInteger("101010", 2); // 使用二进制字符串
加减乘除运算

BigInteger 提供了丰富的方法来进行算术运算:

java

BigInteger a = new BigInteger("10000000000000000000");
BigInteger b = new BigInteger("20000000000000000000");BigInteger sum = a.add(b); // 加法
BigInteger difference = a.subtract(b); // 减法
BigInteger product = a.multiply(b); // 乘法
BigInteger quotient = a.divide(b); // 除法
BigInteger remainder = a.remainder(b); // 余数
比较运算

使用 compareTo 方法来比较两个 BigInteger 对象的大小:

java

BigInteger a = new BigInteger("10000000000000000000");
BigInteger b = new BigInteger("20000000000000000000");int comparison = a.compareTo(b);
if (comparison > 0) {System.out.println("a is greater than b");
} else if (comparison < 0) {System.out.println("a is less than b");
} else {System.out.println("a is equal to b");
}

其他常用方法

BigInteger 还提供了许多其他实用的方法,例如:

  • pow(int exponent):计算幂运算。
  • mod(BigInteger m):计算模运算。
  • gcd(BigInteger val):计算最大公约数。
  • isProbablePrime(int certainty):判断是否为素数。

3. 实际应用中的案例

大数计算

在密码学中,常常需要进行大数的计算。例如,RSA 算法中需要处理非常大的素数和乘积。

java

import java.math.BigInteger;
import java.security.SecureRandom;public class RSADemo {public static void main(String[] args) {SecureRandom random = new SecureRandom();BigInteger p = new BigInteger(512, 100, random); // 生成512位的素数BigInteger q = new BigInteger(512, 100, random); // 生成512位的素数BigInteger n = p.multiply(q); // 计算 n = p * qSystem.out.println("n: " + n);}
}

4. 结论

当需要处理超过 long 类型范围的整数时,BigInteger 类提供了强大的支持。通过使用 BigInteger,我们可以进行任意精度的整数运算,避免数值溢出的问题。在实际开发中,特别是在需要高精度、大数运算的场景中,BigInteger 是不可或缺的工具。

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

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

相关文章

更新Windows 11 后遇到的一些问题(更新中...)

目录 插入U盘后读取不到 在磁盘中新建文件夹需要管理员权限 导致不能安装一些软件 插入U盘后读取不到 解决方法&#xff1a;点击我的电脑或者是此电脑、选择管理、找到设备管理器、选择通用串行总线控制器、右键、选择启动。 第一步&#xff1a;点击我的电脑或者是此电脑、选…

数据质量检测标准

背景 为支持数据仓库全局的数据质量管控&#xff0c;需做好风险点监控&#xff0c;确保数据的完整性、准确性、及时性、一致性。为此&#xff0c;拟定DQC配置方案&规则&#xff0c;评审通过后落地实施。 目标 核心任务dqc覆盖率100%&#xff0c;质量问题及时知晓非核心任…

Java学习48-Java 流(Stream)、文件(File)和IO - 复习章节

1.File类的使用 File类的一个实例对应着磁盘上的文件或文件目录。(必须熟悉)File的实例化(新建一个对象)&#xff0c;常用的方法File类中只有新建&#xff0c;删除&#xff0c;获取路径等方法&#xff0c;不包含读写文件的方法&#xff0c;此时需要使用使用下面说的IO流 IO流…

论文阅读:基于改进 YOLOv5算法的密集动态目标检测方法

目录 概要 Motivation 整体架构流程 技术细节 小结 论文地址&#xff1a;基于改进YOLOv5算法的密集动态目标检测方法 - 中国知网 (cnki.net) 概要 目的&#xff1a;提出一种基于 YOLOv5改进的检测算法&#xff0c;解决密集动态目标检测精度低及易漏检的问题。 方法&…

Linux虚拟主机cPanel重置密码

我使用的Hostease的Linux虚拟主机产品默认带普通用户权限的cPanel面板&#xff0c;这边自购买后一直未重新设置过cPanel面板的密码&#xff0c;但是了解到要定期重置一下cPanel面板的密码&#xff0c;以确保主机数据安全&#xff0c;因此想要进行重置cPanel面板的密码&#xff…

二刷算法训练营Day08 | 字符串(1/2)

今日任务&#xff1a; 344.反转字符串 541. 反转字符串II卡码网&#xff1a;54.替换数字 151.翻转字符串里的单词卡码网&#xff1a;55.右旋转字符串 详细布置&#xff1a; 1. 344. 反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 …

Django 从零到一:pip 基本使用

文章目录 pip 常用命令pip 命令演示pip 自动补全 pip 是 Python 的包管理工具&#xff0c;就如同 Gnu/Linux 系统的 yum、apt&#xff0c;MacOS 系统的 brew。因此&#xff0c;我们必须要熟练使用该工具。 接下来我们就演示一下 pip 的常用命令。 pip 常用命令 pip 提供的命令…

SpringBoot上传文件到服务器(跨服务器上传)

目录 &#xff08;一&#xff09;上传文件到本地&#xff08;windows&#xff09; &#xff08;二&#xff09;上传文件到linux服务器 &#xff08;三&#xff09;跨服务器上传文件 &#xff08;一&#xff09;上传文件到本地&#xff08;windows&#xff09; 1.新建一个文件…

第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组 AB路线

//bfs 1000100010不会超时 #include<bits/stdc.h> using namespace std; #define int long long const int n1e311; int a,b,c,h[n][n][12],k[4][2]{0,1,0,-1,1,0,-1,0}; char t[n][n]; struct s {int x,y,z,w; }; signed main() {ios::sync_with_stdio(false);cin.t…

(规格参考)ADP5360ACBZ-1-R7 电量计 电池管理IC,ADP5072ACBZ 双通道直流开关稳压器,ADL5903ACPZN 射频检测器

1、ADP5360ACBZ-1-R7&#xff1a;具有超低功耗电量计、电池保护功能的先进电池管理PMIC 功能&#xff1a;电池保护 电池化学成份&#xff1a;锂离子/聚合物 电池数&#xff1a;1 故障保护&#xff1a;超温&#xff0c;过压 接口&#xff1a;I2C 工作温度&#xff1a;-40C ~ 85…

Spring Security入门教程:实现自定义用户配置

在上一篇文章中&#xff1a;Spring Security入门教程&#xff1a;利用Spring Security实现安全控制 我们学会了使用Spring Security实现安全控制&#xff0c;学会了他的基础使用&#xff0c;这节课我们来学习一下它的自定义的功能&#xff0c;更深入的了解和使用Spring Securit…

OpenHarmony 实战开发——ArkUI canvas组件

canvas 是 ArkUI 开发框架里的画布组件&#xff0c;常用于自定义绘制图形。因为其轻量、灵活、高效等优点&#xff0c;被广泛应用于 UI 界面开发中。本期&#xff0c;我们将为大家介绍 ArkUI 开发框架中 canvas 组件的使用。 一、canvas 介绍 1.1 什么是 canvas&#xff1f; …

rocketmq的存储和检索

messageId是rocketmq自动生成的。

Java的response返回Json格式

问题 今天开发过程中&#xff0c;写了个拦截器&#xff0c;对于所以请求进行一个token的工作&#xff0c;对于不合标准的token返回错误&#xff0c;在网上找了个拦截器进行二次开发。 package com.maizhiyu.yzt.handle;import org.springframework.beans.factory.annotation.…

AWS Lambda配置CloudWatch日志

Hello example&#xff1a;AWS Lambda 第一个例子Hello (JAVA)-CSDN博客 创建lambda函数&#xff0c;测试&#xff0c;然后点击CloudWatch日志 CloudWatch日志组不存在 创建CloudWatch日志组 1) CloudWatch -> 日志组 -> 创建日志组 2) 填写名称&#xff0c;创建 添加权…

掌握这些神器,让你的编程之路更加“丝滑”

前言&#xff1a; 在软件开发的旅程中&#xff0c;程序员的实用神器确实如同指南针&#xff0c;帮助他们在复杂的代码海洋中导航。以下是从三个方向——自动化测试工具、持续集成/持续部署&#xff08;CI/CD&#xff09;以及代码审查与质量分析——来探讨这些实用神器的应用和影…

Prettier与ESLint:代码风格与质量的自动化保证

Prettier 和 ESLint 是两个互补的工具&#xff0c;它们共同确保代码的风格一致性和质量。Prettier 负责格式化代码&#xff0c;而 ESLint 则执行更复杂的静态分析和规则检查。 2500G计算机入门到高级架构师开发资料超级大礼包免费送&#xff01; Prettier 作用&#xff1a; …

python数据清洗-找到重复的txt并输出字数最少的

import os import json from collections import Counterdef find_and_write_duplicate_txt_files(root_folder, output_file):# 存储所有找到的 txt 文件名和路径的字典列表txt_files_dict_list []# 统计每个文件名出现的次数filename_counts Counter()# 遍历文件夹及其子文…

nmap端口扫描工具——LInux

目录 系统版本: nmap主要功能 安装: nmap命令使用方法

SC8908电机驱动芯片替代AN41908

SC8908 描述 五路H桥静音驱动电机驱动芯片&#xff0c;闭环直流电机光圈调节&#xff0c;支持霍尔位置检测&#xff0c; 2个步进电机。步进电机驱动带256微步细分。 主要特性 • 步进驱动H桥每路250mA最大驱动电流 • 光圈直流驱动H桥每路150mA最大驱动电流 • 单独…