java-(double,BigDecimal),sql-(decimal,nuermic)

最近在项目中数据的处理涉及到了金额,所以才查了这个,double平常使用可以但是不能用来计算金额,bigDecimal可以用来计算金额

但是我看项目中他们之前写的也是使用的double,所以我还是使用了double

如果对精度要求比较高的情况下还是使用bigDecimal比较好

java中的BigDecimal和double

1.double

1.1. 无法精确表示部分十进制小数

十进制小数转换为二进制时,部分数值会变成无限循环小数 ,而 double 只能存储有限位数的二进制,因此会截断并保留近似值。

最经典的例子:

java 复制代码
double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 输出 0.30000000000000004(而非预期的 0.3)

原因:0.1 转二进制是 0.0001100110011...(无限循环),double 只能存储其近似值,叠加后误差被放大。

1.2. 能精确表示的情况(特例)

  • 整数:在 2⁵³范围内的整数(约 ±9×10¹⁵),double可以精确存储(例如2520.0等)。

    例:25 * 0.8 = 20.0,结果是整数,因此 double能精确表示。

  • 可被 2 的幂次整除的小数 :例如 0.5(二进制 0.1)、0.25(二进制 0.01)等。

1.3、适用场景与风险场景

适合使用 double 的场景
  • 科学计算 / 工程计算:允许微小误差(如物理模型、图形渲染、数据统计等)。
  • 非精确小数场景 :如温度(23.5℃)、体重(62.3kg)等,对精度要求不高的数值。
  • 需要大范围数值 :当数值范围超过整数类型(如 long 的上限 ±9×10¹⁸),且可接受近似值时。
禁止使用 double 的场景(精度敏感)
  • 金融 / 支付相关 :金额(如 100.05 元)、税率、手续费等,误差可能导致资金损失或对账不平。
  • 业务关键小数 :如商品单价、折扣率、库存数量(带小数,如 1.5 件)等,需精确记录和计算。
  • 数据库字段映射 :若数据库用 NUMERIC/DECIMAL(精确小数类型),实体类用 double 接收会导致精度丢失。

1.4、常见问题与解决方案

1.4.1. 问题:double 打印时显示很多小数位(如 0.1 显示为 0.1000000000000001
  • 原因:double 存储的是近似值,打印时会显示其真实存储的二进制对应的十进制值。

  • 解决:用 String.format()DecimalFormat格式化输出(仅用于展示,不改变存储的近似值):

java 复制代码
double num = 0.1;
// 格式化保留 1 位小数,输出 0.1
System.out.println(String.format("%.1f", num));
1.4.2. 问题:double 比较大小(如 a == b 失效)
  • 原因:近似值导致看似相等的数值,实际存储的二进制不同(如 0.1 + 0.20.3)。

  • 解决:

    • BigDecimal 比较(推荐):a.compareTo(b) == 0(相等返回 0)。

    • 若必须用 double,则比较两者差值的绝对值是否小于极小值(如 1e-9):

    java 复制代码
    double a = 0.1 + 0.2;
    double b = 0.3;
    // 差值小于 1e-9,认为相等
    if (Math.abs(a - b) < 1e-9) {
        System.out.println("相等");
    }

1.5、总结

double 是一把 "双刃剑":它的优势是范围大、计算效率高,适合非精确场景;但精度丢失的特性使其在核心业务(尤其是金融)中完全不可用。

核心原则

  • 若业务允许微小误差 → 用 double
  • 若要求绝对精确 → 必须用 BigDecimal + 数据库 NUMERIC 类型。

2.BigDecimal

BigDecimal的内部存储基于两部分:

  1. 一个任意精度的整数 "unscaled value"(未缩放值)
  2. 一个 32 位整数 "scale"(缩放因子,表示小数点的位置)

例如:

  • 当你输入new BigDecimal("123.45")时,它会存储为 unscaled value=12345,scale=2
  • 当你输入new BigDecimal("123.4500")时,同样存储为 unscaled value=1234500,scale=4

这意味着:

  1. 如果你通过字符串构造BigDecimal,它会精确保留字符串中的所有数字和小数点位置
  2. 如果你通过浮点数(如 double)构造,可能会因为浮点数本身的精度问题导致意外结果
  3. BigDecimal会保留尾部的零,这一点与 double 不同

示例代码:

java 复制代码
import java.math.BigDecimal;

public class BigDecimalExample {
    public static void main(String[] args) {
        BigDecimal bd1 = new BigDecimal("100.00");
        BigDecimal bd2 = new BigDecimal("100");
        BigDecimal bd3 = new BigDecimal(100.00); // 不推荐,可能有精度问题
        
        System.out.println(bd1); // 输出 100.00
        System.out.println(bd2); // 输出 100
        System.out.println(bd3); // 可能输出 100.000000000000000...
    }
}

所以,如果你希望精确控制BigDecimal的数字形式,最佳实践是:

  1. 使用字符串作为构造参数
  2. 需要时使用setScale()方法显式设置精度
  3. 避免使用 double 作为构造参数

sql中的decimal,nuermic

在数据库中,DECIMALNUMERIC 是用于存储精确数值的类型,主要用于需要高精度计算的场景(如金融、货币、税务等),它们的特性和使用方式非常相似,但存在一些细微差别。

1. 基本特性

两者都是精确数值类型 ,与 FLOATDOUBLE(近似数值类型)不同,它们不会丢失精度,因为它们以字符串形式存储数值(而非二进制浮点数)。

定义格式通常为:

sql 复制代码
DECIMAL(p, s)` 或 `NUMERIC(p, s)
  • p(precision):总位数(整数部分 + 小数部分),范围通常为 1~65(不同数据库可能有差异)。
  • s(scale):小数部分的位数,范围为 0~p。

2. 主要区别

标准 SQL 中对两者的定义有细微差异:

  • NUMERIC(p, s):严格遵守 ps 的限制,插入的值必须完全符合指定的精度和小数位数(超出则报错)。
  • DECIMAL(p, s):可以接受比指定精度更高的值,但会自动截断或四舍五入到指定的 s 位小数(具体行为取决于数据库实现)。

但实际中,大多数数据库(如 MySQL、PostgreSQL、SQL Server)将两者视为同义词,处理方式完全一致,没有区别。例如:

  • MySQL 文档明确说明 DECIMALNUMERIC 是相同的。
  • PostgreSQL 中两者行为一致,仅名称不同。

3. 使用场景

适合存储需要精确计算的数值,例如:

  • 货币金额(如 DECIMAL(10, 2) 表示最大 99999999.99)。
  • 税率、利息等需要固定小数位的数值。

不适合存储不需要高精度的大数值(如科学计算中的近似值),此时用 FLOAT 更节省空间。

4. 与 Java 中 BigDecimal 的配合

数据库的 DECIMAL/NUMERIC 类型在 Java 中通常映射为 BigDecimal 类型,两者都是精确数值类型,配合使用可以避免精度丢失问题。

示例(JDBC 读取):

java 复制代码
PreparedStatement ps = connection.prepareStatement("SELECT price FROM products WHERE id = ?");
ps.setInt(1, productId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
    BigDecimal price = rs.getBigDecimal("price"); // 直接获取为BigDecimal
}

总结

  • DECIMALNUMERIC 在大多数数据库中功能完全相同,都用于存储精确数值。
  • 核心是通过 (p, s) 控制总位数和小数位数,确保数值精度。
  • 与 Java 的 BigDecimal 配合使用,是处理金融等高精度场景的最佳实践。
相关推荐
ZJun_Ocean1 小时前
add_columns
数据库·sql
ID_180079054732 小时前
淘宝商品详情 API 接口 item_get: 高效获取商品数据的技术方案
java·前端·数据库
间彧2 小时前
布隆过滤器详解与Redis+Spring Boot实战指南
java
JobDocLS2 小时前
C++重要知识点相关代码
开发语言·c++
张飞飞飞飞飞2 小时前
python——Nuitka打包
开发语言·python
躲在云朵里`2 小时前
Jeecgboot框架-权限控制
java·开发语言
Ai runner2 小时前
Show call stack in perfetto from json input
java·前端·json
I_LPL2 小时前
day36 代码随想录算法训练营 动态规划专题4
java·算法·leetcode·动态规划·hot100
Mr YiRan2 小时前
C++二义性,多态,纯虚函数和模版函数
java·jvm·c++