Java浮点数精度处理:BigDecimal、DecimalFormat等5种保留小数方法详解

发布时间:2026/8/1 4:49:50

Java浮点数精度处理:BigDecimal、DecimalFormat等5种保留小数方法详解 1. 从一次线上事故说起为什么“保留小数”不是小事前几天团队里一个刚上线的服务出了个不大不小的线上问题。一个核心的计费模块在计算用户的服务费用时本该输出125.50元结果页面上赫然显示着125.49999999999999。用户直接截图投诉虽然金额差得不多但“看起来”非常不专业直接影响了产品的可信度。追查下去根源就在于一个简单的Double类型数值在格式化输出时没有处理好小数位数的保留浮点数的精度陷阱在最后一步展示时暴露无遗。这个场景我相信很多做后端开发、数据处理甚至前端展示的朋友都遇到过。Double或double作为编程中最常用的浮点数类型处理起来方便但涉及到金额、比例、测量值等需要精确表示和展示的场景时直接进行加减乘除或者简单的转换很容易掉进坑里。BigDecimal、DecimalFormat、String.format这些词你肯定不陌生网上相关的代码片段一搜一大把。但什么时候该用哪个每种方法背后的原理是什么为什么用BigDecimal设置精度有时结果还是不对String.format的舍入规则到底是什么这篇文章我就结合自己这些年踩过的坑和积累的经验把这五种最常用、也最应该掌握的方法给你掰开揉碎了讲清楚。我们不止看“怎么做”更要深究“为什么这么做”以及“在什么场景下该选哪个”。无论你是正在处理axios请求中Double参数精度丢失的问题还是在排查double free or corruption之前的数据准备阶段亦或是确保BigDecimal在加减乘除和比较大小时的绝对可靠相信都能找到答案。2. 理解根源Double的精度陷阱与我们的诉求在直接上代码之前我们必须先统一认知为什么简单的“保留两位小数”会成为一个需要专门讨论的问题这源于Double类型的本质——它遵循 IEEE 754 标准是一种二进制浮点数用于在有限的存储空间64位内近似表示一个非常大的实数范围。2.1 二进制浮点数的“近似”艺术计算机用二进制存储数据。对于像 0.1、0.2 这样的十进制小数在转换为二进制时往往会变成一个无限循环小数例如十进制 0.1 的二进制表示是0.0001100110011...循环。Double的 64 位长度是固定的它必须截断这个无限循环的二进制序列只保留有限位。这就导致了精度丢失存储的值已经是原始值的一个近似值。当你写下double d 0.1;时变量d里存储的并不是精确的数学上的 0.1而是一个非常接近 0.1 的二进制近似值。进行多次运算后这个近似误差可能会累积、放大最终导致像125.49999999999999这样的结果。注意这不是 Java 或任何特定语言的缺陷而是所有使用 IEEE 754 浮点数的编程语言C/C, Python, JavaScript 等的共性问题。你在 JavaScript 的axios请求参数里遇到double处理问题或者在 C 里担心double free之前的数据一致性根源都可能在此。2.2 “保留小数位数”的真实含义当我们说“将 Double 保留 N 位小数”时通常包含两层含义而很多初学者会混淆数值计算层面的精确化在后续的数学运算如加总、比较中需要一个具有确定精度和舍入规则的、精确的数值表示。这关乎计算的正确性。展示层面的格式化将一个数值按照四舍五入或其他规则格式化为一个具有固定小数位数的字符串用于显示、存储或传输。这关乎呈现的规范性。不同的方法侧重点不同。BigDecimal核心解决的是第一类问题精确计算而DecimalFormat和String.format主要解决第二类问题格式化输出。用错了场景就会事倍功半甚至引入新的错误。3. 方法一BigDecimal——精确计算的基石BigDecimal是 Java 中用于高精度计算的类。它通过一个“非标度值”unscaledValue一个 BigInteger和一个“标度”scale整数来表示一个十进制数例如unscaledValue1255scale1就表示125.5。这种方式避免了二进制的精度丢失特别适合财务计算。3.1 正确构造BigDecimal对象使用BigDecimal的第一步也是最多坑的一步就是构造对象。绝对不要使用new BigDecimal(double)构造函数// 错误示例精度已丢失 Double d 125.49999999999999; BigDecimal bdWrong new BigDecimal(d); // 此时bdWrong已经是125.49999999999999了 System.out.println(bdWrong); // 输出125.4999999999999857891452847979962825775146484375传入double构造时double的近似值已经被确定BigDecimal只是忠实地记录了这个不精确的值。正确的方法是使用String构造或者使用BigDecimal.valueOf(double)静态方法其内部实现也是先转为String。// 正确示例1使用String构造 Double d 125.49999999999999; BigDecimal bdCorrect1 new BigDecimal(d.toString()); // 先转为String // 正确示例2使用valueOf (推荐更简洁) BigDecimal bdCorrect2 BigDecimal.valueOf(d); // 对于已知的数值直接使用字符串字面量 BigDecimal bdCorrect3 new BigDecimal(125.5);BigDecimal.valueOf(double)方法会先调用Double.toString(double)这个方法会生成一个能精确表示该double值的最短十进制字符串从而避免了直接传递double的精度问题。3.2 设置精度与舍入模式构造出精确的BigDecimal后我们可以使用setScale(int newScale, RoundingMode roundingMode)方法来设置小数位数标度并指定舍入规则。import java.math.BigDecimal; import java.math.RoundingMode; public class BigDecimalDemo { public static void main(String[] args) { BigDecimal num new BigDecimal(125.505); // 四舍五入保留2位小数 BigDecimal rounded1 num.setScale(2, RoundingMode.HALF_UP); System.out.println(rounded1); // 输出125.51 // 直接向上舍入向正无穷大方向 BigDecimal rounded2 num.setScale(2, RoundingMode.CEILING); System.out.println(rounded2); // 输出125.51 // 银行家舍入法四舍六入五成双统计学常用减少偏差 BigDecimal rounded3 num.setScale(2, RoundingMode.HALF_EVEN); System.out.println(rounded3); // 输出125.50 (因为5前面是0偶数所以舍去) BigDecimal num2 new BigDecimal(125.515); BigDecimal rounded4 num2.setScale(2, RoundingMode.HALF_EVEN); System.out.println(rounded4); // 输出125.52 (因为5前面是1奇数所以进位) } }关键点解析RoundingMode.HALF_UP最常见的“四舍五入”。大于等于5则进位。RoundingMode.HALF_EVEN银行家舍入法。当舍去部分为5时看前一位数字奇数则进位偶数则舍去。这种规则在大量统计计算中能更公平地分摊舍入误差。其他模式如CEILING向上取整、FLOOR向下取整、DOWN向零方向舍入等根据业务场景选用。3.3 进行精确的加减乘除BigDecimal的运算方法add,subtract,multiply,divide都返回新的BigDecimal对象保证了不可变性。其中除法运算divide必须指定精度和舍入模式否则如果结果是无限小数如 1 除以 3会抛出ArithmeticException。BigDecimal a new BigDecimal(10); BigDecimal b new BigDecimal(3); // 加法、减法、乘法相对简单 BigDecimal sum a.add(b); // 13 BigDecimal product a.multiply(b); // 30 // 除法必须指定精度和舍入模式 BigDecimal quotient a.divide(b, 4, RoundingMode.HALF_UP); // 保留4位小数四舍五入 System.out.println(quotient); // 输出3.3333实操心得对于金额计算我个人的习惯是所有涉及金额的变量从数据库读取DECIMAL类型、网络传输用String或BigDecimal的toString、到内存计算全程使用BigDecimal。只在最后一步需要展示时才格式化为字符串。这样可以最大程度避免精度问题。这也是处理axios参数中double类型金额时后端更推荐的做法让前端传递金额的字符串形式后端直接用BigDecimal接收和计算。4. 方法二DecimalFormat——灵活强大的格式化工具java.text.DecimalFormat是一个专门用于格式化数字的类功能非常强大可以控制小数位数、千位分隔符、百分比、货币符号等。它主要用于将数字转换为特定格式的字符串而不是进行精确的数学运算。4.1 基础用法模式字符串DecimalFormat的核心是一个模式字符串。import java.text.DecimalFormat; public class DecimalFormatDemo { public static void main(String[] args) { double number 12345.6789; // 保留两位小数四舍五入 DecimalFormat df1 new DecimalFormat(#.##); System.out.println(df1.format(number)); // 输出12345.68 // 保留两位小数不足补零 DecimalFormat df2 new DecimalFormat(#.00); System.out.println(df2.format(number)); // 输出12345.68 (注意整数部分格式是#不会补零) // 保留两位小数整数部分至少一位不足补零小数部分不足补零 DecimalFormat df3 new DecimalFormat(0.00); System.out.println(df3.format(number)); // 输出12345.68 System.out.println(df3.format(0.456)); // 输出0.46 // 添加千位分隔符 DecimalFormat df4 new DecimalFormat(#,##0.00); System.out.println(df4.format(number)); // 输出12,345.68 } }模式字符解释0一位数字如果该位没有数字则用0补足。#一位数字如果该位没有数字则省略。.小数分隔符。,分组分隔符千位分隔符。4.2 设置舍入模式与解析数字默认情况下DecimalFormat使用RoundingMode.HALF_EVEN银行家舍入法。你可以通过setRoundingMode方法来改变。import java.text.DecimalFormat; import java.math.RoundingMode; public class DecimalFormatRounding { public static void main(String[] args) { double num 125.505; DecimalFormat df new DecimalFormat(0.00); df.setRoundingMode(RoundingMode.HALF_UP); // 设置为四舍五入 System.out.println(df.format(num)); // 输出125.51 df.setRoundingMode(RoundingMode.DOWN); // 向零方向舍入直接截断 System.out.println(df.format(num)); // 输出125.50 } }DecimalFormat也可以将格式化的字符串解析回Number对象通常是Double。但要注意解析回来的Double依然有精度问题。try { DecimalFormat df new DecimalFormat(#.##); String formatted df.format(125.505); // 125.51 Number parsed df.parse(formatted); double value parsed.doubleValue(); System.out.println(value); // 输出125.51 } catch (ParseException e) { e.printStackTrace(); }踩坑提醒DecimalFormat不是线程安全的它的format和parse方法会修改内部状态。如果在多线程环境下使用必须为每个线程创建独立的实例或者进行外部同步。一种常见的做法是使用ThreadLocalprivate static final ThreadLocalDecimalFormat dfHolder ThreadLocal.withInitial( () - new DecimalFormat(#,##0.00) ); public String formatCurrency(double value) { return dfHolder.get().format(value); }5. 方法三String.format——最简洁的格式化输出String.format方法借鉴自 C 语言的printf语法简洁是快速格式化字符串包括数字的利器。它内部也是通过Formatter类实现的对于数字格式化其舍入规则是**“四舍六入五成双”**即RoundingMode.HALF_EVEN。5.1 格式化符号与示例用于浮点数的格式化符号主要是%f。public class StringFormatDemo { public static void main(String[] args) { double num 125.505; // 保留两位小数默认四舍六入五成双 String s1 String.format(%.2f, num); System.out.println(s1); // 输出125.50 (注意是125.50不是125.51) // 保留三位小数 String s2 String.format(%.3f, 125.5055); System.out.println(s2); // 输出125.506 // 总宽度8位右对齐不足补空格保留1位小数 String s3 String.format(%8.1f, num); System.out.println([ s3 ]); // 输出[ 125.5] // 总宽度8位左对齐不足补空格 String s4 String.format(%-8.2f, num); System.out.println([ s4 ]); // 输出[125.50 ] // 输出正负号 String s5 String.format(%.2f, num); System.out.println(s5); // 输出125.50 } }格式说明符%[argument_index$][flags][width][.precision]conversion%起始符。.precision对于浮点数表示保留的小数位数。注意这里的舍入规则是HALF_EVEN。width最小输出宽度不足用空格或0填充。flags-左对齐输出正负号0用零填充宽度,使用本地化的千位分隔符在中文环境下是逗号。conversionf表示浮点数。5.2 舍入规则的“坑”与应对String.format默认使用HALF_EVEN舍入这与很多人直觉的“四舍五入”HALF_UP不同是最大的一个坑。例如上面的125.505被格式化为125.50而不是125.51。如果你业务上严格要求HALF_UP就不能直接使用String.format。变通方案是先使用BigDecimal进行舍入再格式化为字符串。import java.math.BigDecimal; import java.math.RoundingMode; public class StringFormatWorkaround { public static void main(String[] args) { double num 125.505; // 先用BigDecimal进行HALF_UP舍入 BigDecimal bd BigDecimal.valueOf(num).setScale(2, RoundingMode.HALF_UP); // 再转换为字符串或者用String.format格式化此时数字已精确 String result bd.toString(); // 最简单 // 或者 String result String.format(%.2f, bd.doubleValue()); // 注意再次转换double可能轻微风险但此时bd精度已控制 System.out.println(result); // 输出125.51 } }个人建议String.format非常适合日志输出、快速生成报告等对绝对精度要求不高但要求代码简洁的场景。对于财务、科学计算等要求精确舍入规则的场景建议优先使用BigDecimal.setScale。6. 方法四NumberFormat——本地化与货币格式化java.text.NumberFormat是一个抽象基类用于格式化和解析与地区Locale相关的数字、货币和百分比。它的两个常用子类是DecimalFormat我们刚讲过和ChoiceFormat。通过NumberFormat的工厂方法我们可以轻松获取针对特定地区的数字格式化器。6.1 获取不同风格的实例import java.text.NumberFormat; import java.util.Locale; public class NumberFormatDemo { public static void main(String[] args) { double num 12345.6789; Locale usLocale Locale.US; Locale cnLocale Locale.CHINA; // 获取通用数字格式会根据Locale调整小数分隔符和千位分隔符 NumberFormat generalFormatUS NumberFormat.getInstance(usLocale); NumberFormat generalFormatCN NumberFormat.getInstance(cnLocale); System.out.println(generalFormatUS.format(num)); // 输出12,345.679 (默认保留3位小数HALF_EVEN) System.out.println(generalFormatCN.format(num)); // 输出12,345.679 (中文环境千位分隔符也是逗号) // 获取整数格式会四舍五入到整数 NumberFormat integerFormat NumberFormat.getIntegerInstance(usLocale); System.out.println(integerFormat.format(num)); // 输出12,346 // 获取货币格式 NumberFormat currencyFormatUS NumberFormat.getCurrencyInstance(usLocale); NumberFormat currencyFormatCN NumberFormat.getCurrencyInstance(cnLocale); System.out.println(currencyFormatUS.format(num)); // 输出$12,345.68 System.out.println(currencyFormatCN.format(num)); // 输出12,345.68 // 获取百分比格式会自动乘以100并添加%符号 NumberFormat percentFormat NumberFormat.getPercentInstance(usLocale); percentFormat.setMinimumFractionDigits(2); // 设置百分比后保留2位小数 System.out.println(percentFormat.format(0.4567)); // 输出45.67% } }6.2 自定义NumberFormat的行为通过getInstance获取的NumberFormat实例实际上通常是DecimalFormat对象可以向下转型后进行更精细的控制。import java.text.NumberFormat; import java.text.DecimalFormat; import java.util.Locale; import java.math.RoundingMode; public class CustomizeNumberFormat { public static void main(String[] args) { NumberFormat nf NumberFormat.getInstance(Locale.US); if (nf instanceof DecimalFormat) { DecimalFormat df (DecimalFormat) nf; df.applyPattern(#,##0.000); // 应用自定义模式保留3位小数有千位分隔符 df.setRoundingMode(RoundingMode.HALF_UP); // 修改舍入模式为四舍五入 } System.out.println(nf.format(12345.6789)); // 输出12,345.679 } }适用场景当你的应用需要支持国际化i18n根据用户所在地区显示不同格式的数字、货币或百分比时NumberFormat是首选。它帮你处理了不同地区的符号差异如小数点、千分位、货币符号你只需要关心业务逻辑和 Locale 设置。7. 方法五Math.round与手动计算——原始但有效的取舍对于非常简单的、性能极其敏感的、或者是在非 Java 环境如某些嵌入式或脚本环境下的场景我们可能会回归最基础的数学运算来保留小数。7.1 使用Math.round进行四舍五入Math.round(double a)方法返回最接近参数的long。它采用“四舍五入”规则但只舍入到整数。要保留小数位需要先乘后除。public class MathRoundDemo { public static void main(String[] args) { double num 125.505; int scale 2; // 保留2位小数 double factor Math.pow(10, scale); // 10的scale次方用于移位 // 经典的四舍五入到指定位数 double rounded (double) Math.round(num * factor) / factor; System.out.println(rounded); // 输出125.51 // 注意结果仍然是double打印时可能仍会显示精度问题 System.out.println(new BigDecimal(rounded)); // 可能输出125.5100000000000051159076974727213382720947265625 } }原理剖析num * 100将小数点右移两位12550.5Math.round对这个数进行四舍五入到整数12551再除以 100将小数点左移两位得到125.51。7.2 手动实现其他舍入模式基于同样的“乘-舍入-除”原理我们可以利用Math.floor向下取整和Math.ceil向上取整来实现其他舍入规则。public class ManualRounding { // 向下取整向负无穷大 public static double floorTo(double num, int scale) { double factor Math.pow(10, scale); return Math.floor(num * factor) / factor; } // 向上取整向正无穷大 public static double ceilTo(double num, int scale) { double factor Math.pow(10, scale); return Math.ceil(num * factor) / factor; } // 向零方向舍入截断 public static double truncate(double num, int scale) { double factor Math.pow(10, scale); return (num 0) ? Math.ceil(num * factor) / factor : Math.floor(num * factor) / factor; } public static void main(String[] args) { double pos 125.505; double neg -125.505; System.out.println(floorTo(pos, 2)); // 125.50 System.out.println(floorTo(neg, 2)); // -125.51 (注意负数的floor) System.out.println(ceilTo(pos, 2)); // 125.51 System.out.println(ceilTo(neg, 2)); // -125.50 System.out.println(truncate(pos, 2)); // 125.50 System.out.println(truncate(neg, 2)); // -125.50 } }重要警告这种方法最大的问题是结果仍然是double类型。经过乘除运算后很可能又引入了新的二进制浮点误差如上例中rounded变量实际存储的值可能并不是精确的125.51。因此这种方法仅适用于最终目的是为了显示或存储且对极端精度不敏感的场景。绝对不能用于需要继续参与精确计算的中间步骤。8. 终极对决五种方法的选择与实战场景指南现在我们对五种方法都有了深入理解。在实际项目中如何选择下面这个表格和场景分析可以帮你快速决策。方法核心用途精度保证线程安全性能适用场景BigDecimal精确计算数值的创建、运算、舍入。绝对精确基于十进制。是不可变对象。较低对象创建、运算开销大。财务计算金额、利率、科学计算、需要高精度和可控舍入的任何场景。DecimalFormat灵活格式化将数字转为特定格式的字符串。依赖输入数字的精度格式化过程使用BigDecimal逻辑保证舍入正确。否需同步或使用ThreadLocal。中等。复杂的数字格式化如模式字符串、百分比、自定义符号、需要复用格式器对象。String.format快速格式化简洁的字符串格式化输出。格式化过程精确但输入double本身可能不精确。舍入规则固定为HALF_EVEN。是内部创建新Formatter。中等每次调用都解析格式字符串。日志输出、简单的格式化消息拼接、代码简洁性优先的场景。NumberFormat本地化格式化根据Locale格式化数字、货币、百分比。同DecimalFormat。通常否具体实现决定。中等。国际化i18n应用需要根据用户地区显示数字、货币格式。Math.round基础舍入简单的四舍五入到指定位数。不保证结果仍是近似double可能引入新误差。是。最高纯数学运算。性能极度敏感的非关键路径、简单的UI显示且能接受微小误差、非Java环境下的算法实现参考。8.1 场景化决策树场景计算商品总价单价 * 数量分析涉及乘法必须精确。金额计算是核心业务。选择BigDecimal。从数据库DECIMAL读出或用字符串构造计算全程使用BigDecimal最后用setScale(2, RoundingMode.HALF_UP)确定金额。场景在网页上显示一个统计百分比保留一位小数分析纯展示用途需要四舍五入且可能涉及国际化。选择NumberFormat.getPercentInstance(locale)。设置setMinimumFractionDigits(1)和setMaximumFractionDigits(1)。它处理了乘以100和添加%符号以及本地化格式。场景生成一个格式固定的日志信息如“处理耗时xxx.xx ms”分析代码要求简洁性能不是瓶颈格式固定。选择String.format(“处理耗时%.2f ms”, elapsedTime)。一行代码搞定非常清晰。注意其舍入规则是HALF_EVEN。场景高性能流处理中对海量传感器数据double类型进行近似聚合保留两位小数输出摘要分析数据量极大精度要求相对宽松传感器本身有误差性能是关键。选择可以考虑Math.round方法。在聚合的最后一步对最终结果进行(double) Math.round(value * 100) / 100。虽然仍有极小误差但在该场景下可接受且性能最优。场景解析用户输入的复杂格式数字字符串如“1,234.56%”并转换为数值分析需要解析自定义格式可能包含千分位、百分号等。选择DecimalFormat。使用applyPattern设置匹配的模式如“#,##0.00%”然后调用parse方法。BigDecimal的字符串构造器无法处理这些额外符号。8.2 一个综合案例订单金额处理流水线假设我们有一个订单处理流程从请求如axios提交的 JSON中接收商品单价字符串“19.99”和数量整数。计算单品总价单价 * 数量。计算税费单品总价 * 税率 0.08。计算最终支付金额单品总价 税费。将所有金额以“xxx.xx”的格式展示给用户。import java.math.BigDecimal; import java.math.RoundingMode; import java.text.NumberFormat; import java.util.Locale; public class OrderProcessor { public static void main(String[] args) { // 1. 接收数据假设从JSON来 String priceStr 19.99; int quantity 3; BigDecimal taxRate new BigDecimal(0.08); // 2. 使用BigDecimal进行精确计算 BigDecimal unitPrice new BigDecimal(priceStr); // 正确构造 BigDecimal itemTotal unitPrice.multiply(BigDecimal.valueOf(quantity)); // 乘法 BigDecimal tax itemTotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP); // 计算税费并四舍五入 BigDecimal finalAmount itemTotal.add(tax); // 加法 System.out.println(单品总价计算中: itemTotal); // 59.97 System.out.println(税费已舍入: tax); // 4.80 System.out.println(支付金额计算中: finalAmount); // 64.77 // 3. 使用NumberFormat进行本地化货币格式化展示 NumberFormat currencyFormat NumberFormat.getCurrencyInstance(Locale.CHINA); System.out.println(单品总价: currencyFormat.format(itemTotal)); // 59.97 System.out.println(税费: currencyFormat.format(tax)); // 4.80 System.out.println(支付金额: currencyFormat.format(finalAmount)); // 64.77 } }在这个流水线中BigDecimal负责所有核心算术运算和精度控制从源头上杜绝了误差。NumberFormat则在最后一步负责以符合地区习惯的、美观的格式呈现给用户。两者各司其职构成了处理金额类业务的最佳实践。9. 避坑指南与性能考量9.1 BigDecimal的“相等”陷阱与比较BigDecimal是一个对象使用equals()方法比较时不仅比较数值是否相等还会比较标度scale。new BigDecimal(“2.0”)和new BigDecimal(“2.00”)用equals()比较会返回false。正确的比较方式是使用compareTo()方法。BigDecimal a new BigDecimal(2.0); BigDecimal b new BigDecimal(2.00); System.out.println(a.equals(b)); // false System.out.println(a.compareTo(b) 0); // true对于bigdecimal比较大小小于等于这类需求也使用compareTo()if (a.compareTo(b) 0) { // a 小于等于 b }9.2 性能与对象复用BigDecimal创建和运算开销大。在循环中频繁创建新的BigDecimal对象尤其是通过字符串会产生大量临时对象增加GC压力。对于性能关键路径可以考虑重用对象但要注意其不可变性或者评估是否真的需要如此高的精度。DecimalFormat非线程安全且初始化new DecimalFormat(pattern)和applyPattern有一定开销。在频繁格式化的场景应该复用实例。String.format每次调用都会解析格式字符串如果是在循环中使用固定格式性能不如预先创建好的DecimalFormat实例。9.3 数据库与序列化的映射当使用BigDecimal对应数据库的DECIMAL/NUMERIC类型时一般没有问题。但在使用一些 ORM 框架或 JSON 序列化库如 Jackson、Gson时需要注意确保序列化/反序列化时BigDecimal被正确地当作数字处理而不是被转换为科学计数法或精度丢失的浮点数。通常这些库都提供了相关配置。在axios请求中如果前端传递BigDecimal对应的数值最好以字符串形式传递并在后端用RequestBody配合BigDecimal类型接收以避免 HTTP 协议传输过程中的精度问题。9.4 关于“Double保留小数”的哲学最后我想强调的是“保留小数”这个操作本质上是一个从“连续、近似的实数领域”向“离散、精确的表示领域”的映射。选择哪种方法取决于你的业务是停留在“近似领域”如图形计算、物理模拟还是必须进入“精确领域”如金融交易、法律计量。在“精确领域”BigDecimal是你的不二之选而在“近似领域”的边界进行格式化输出时则要清醒认识到数据本身的近似性并选择合适且一致的格式化工具。理解这层区别你就能在面对double相关问题时做出最合理的技术决策。

相关新闻