如何解決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.pswp.cn/diannao/12878.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/12878.shtml
英文地址,請注明出處:http://en.pswp.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最大驅動電流 ? 單獨…