news 2026/10/2 23:26:47

金额存储选型:Long还是BigDecimal?精度、单位与工程实践全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
金额存储选型:Long还是BigDecimal?精度、单位与工程实践全解析

这个题目我太有发言权了。老读者都知道,我过去几年一直在做交易结算类的系统,几乎每个迭代都要跟金额打交道。组里新来的同事几乎都问过同一个问题:金额到底用Long还是BigDecimal?面试的时候我也常拿这个当考点,十个人里有七八个会先愣一下,然后给出一个“看情况”的模糊答案。其实这个问题没有标准答案,但背后有一整套需要想清楚的东西:单位怎么定、计算怎么做、数据库怎么存、前端怎么传、对账怎么搞。今天就把我这几年的实践经验和踩过的坑一次性说透。

1. 先认清问题本质:金额字段到底在防什么

1.1 精度丢失是怎么发生的

先说结论:我们讨论Long还是BigDecimal,本质上是在讨论精度和单位两个维度的问题。精度指的是一个数能精确表达多少位有效数字,单位指的是你存的是“元”还是“分”还是“厘”。

绝大多数人对金额精度没概念,是因为日常用计算器按个0.1加0.2,显示0.3,没觉得有什么问题。但计算机里的浮点数不是这么工作的。我们常用的float和double,底层是IEEE 754标准的二进制浮点表示,用有限的二进制位去逼近一个十进制小数。十进制的0.1转成二进制是一个无限循环小数,计算机只能截断存储,所以当你计算0.1 + 0.2的时候,实际得到的是类似0.30000000000000004这样的结果。

我见过最典型的翻车现场:一个订单系统里,商品单价9.9元,客户买了3件,代码直接用double相乘,得到29.700000000000003,然后往数据库一存,再读出来展示,前端页面上赫然显示29.700000000000003。用户直接截图投诉,财务对账也对不上。这种问题一旦上线,解释成本极高。

1.2 Float/Double在金额场景下的四个致命伤

除了精度丢失,浮点数在金额场景还有几个很隐蔽的问题:

  • 比较不可靠:两个看起来相等的金额,因为计算路径不同,可能在二进制上不相等。比如0.3 == 0.1 + 0.2的结果是false,如果你代码里用==或equals做金额判断,就会莫名奇妙走错分支。
  • 累加漂移:大量的金额累加时,误差会不断积累。日终清算、月度汇总这类场景,浮点误差会被放大到肉眼可见。
  • 跨语言不一致:Java算出来一个结果,Python算出来另一个结果,C++又不一样。一旦涉及多语言系统对账,你根本没法定位是谁的问题。
  • 数据库排序索引混乱:浮点列在数据库里做范围查询时,边界值的处理也会出现意想不到的结果。

所以第一道红线先划下来:金额计算场景,float和double直接出局,没有任何商量的余地。剩下的选择就是Long和BigDecimal两个,这才是真正的战场。

2. Long存分与BigDecimal存元:两种主流方案的正面交锋

2.1 Long + 最小货币单位:我早期最爱的方案

Long方案的思路很简单:既然小数在计算机里容易丢精度,那我干脆不存小数。把金额统一乘以100,以“分”为单位,全部用整数存储和计算。

这个方案最大的优点是快且省。Long是Java原生基本类型,走的是CPU直接支持的整数运算,没有对象开销。在一些高频交易、风控拦截这类对延迟极度敏感的场景,一个方法里做上百万次金额累加,Long的性能优势非常明显。存储上也省空间,数据库里一个bigint占8字节,比decimal类型要紧凑一些。

另一个隐藏优点是没有歧义。整数就是精确的,你看到600就是6元整,不会出现4.999999999这种尴尬值。团队里只要约定好“所有金额入参出参都是分”,整个链路从接口到数据库都是整数,逻辑会非常清爽。

但Long方案也有它要命的点,我踩得最深的一个坑是单位约定很难落到实处。你以为大家都默认存分,结果前端同学把他理解的“元”直接传了上来,或者第三方回调里某个字段就是元单位没做转换,系统就会把6元当成6分处理,账面上直接差100倍。这种问题排查起来要命,因为它不报错,只是数字不对。

还有,Long方案做复杂的业务计算时容易“算丢单位”。比如计算折扣:订单金额600分,打8.5折,600 * 85 / 100 = 510分,逻辑没问题。但如果业务里出现更复杂的运算,比如按比例分摊、跨币种折算,就非常容易在某个环节忘了除回去,或者除的顺序不对导致截断误差。

2.2 BigDecimal:精度无上限但需要纪律

BigDecimal是Java标准库里的高精度数值类型,它可以表达任意精度的十进制小数,并且提供了add、subtract、multiply、divide这些自带舍入控制的计算方法。存元、存分都可以,甚至存厘存毫都行。

我后来在结算类系统里大量用BigDecimal,主要是因为业务的安全性优先级远高于性能。交易金额、佣金、退款、优惠分摊,这些数字一旦出错,不是用户体验问题,是资损问题。BigDecimal的不可变性(每次运算都返回新对象)和显式的舍入控制,让代码的可读性和可审计性更好。比如amount.divide(3, 2, RoundingMode.HALF_UP),一眼就能看出除3保留两位四舍五入,这对后续接手代码的同事极其友好。

BigDecimal最需要注意的问题有三个:构造方式、运算写法、性能。特别是构造方式,new BigDecimal(0.1)会把double的二进制近似值完整转出来,得到一个超长小数;正确做法是用new BigDecimal("0.1")或者BigDecimal.valueOf(0.1)。运算上必须用add等方法,不能用+。性能上,每次运算都是对象分配,高频循环里会有明显的GC压力,需要结合场景权衡。

2.3 一张表看懂两个方案的核心差异

对比维度Long(以分为单位)BigDecimal(以元为单位)
精度精确到分,整数天然精确精度可控,可到厘甚至更小
性能原生整数运算,极快对象运算,有性能开销
存储bigint,8字节decimal,按精度定长度
单位约定强依赖团队约定单位天然是元,直观
运算写法普通算术,但要自己管理乘除换算方法调用,舍入规则显式
出错模式单位混乱、换算遗漏、截断构造方式错误、比较方式错误
适用场景高频计算、对性能极致敏感业务复杂、对可审计性要求高

2.4 我的选型建议:别上来就二选一

很多人问我的时候,我给的答案不是“用Long”或者“用BigDecimal”,而是先反问三个问题:

  1. 这个金额是不是要和外部系统对接?如果对方文档里写的单位是元,你用Long存分,就必须在所有接口边界做转换,任何一个接口漏了就是资损。
  2. 这个金额的精度要求是什么?只到分,还是有可能到厘、到毫?比如某些行业的分佣、政府补贴、金融利息计算,精度可能超过分,Long就没法直接覆盖。
  3. 这个金额是否参与复杂计算?只是存一下展示还好,如果涉及多步乘除、分摊、舍入,BigDecimal的显式规则会更安全。

我个人的倾向是:核心交易类、结算类、财务类,无脑选BigDecimal,多花点性能换安全性和可维护性,完全值得。某些极高频的场景,比如订单金额在内存里做千万次累加去重这种,可以考虑Long+分,但一定要在架构层面做强制约定和转换封装,不能靠自觉。还有一类折中方案:数据库里用decimal,应用层用BigDecimal,对外接口传String,这在金融行业是标准姿势。

3. 金额计算的六个核心陷阱与操作要点

3.1 加减法里最容易忽略的“单位差”

加减法的坑不在BigDecimal本身,而在参与运算的数值单位是否一致。我遇到过一种很隐蔽的错误:系统主体用BigDecimal存元,但某个历史遗留接口返回的是Long型分,代码里直接amount.add(BigDecimal.valueOf(oldAmount))。编译不报错,逻辑不报错,但700分加到了700元上,结果差了100倍。这种问题靠代码审查很难发现,因为肉眼扫过去都是正常加法。

我的实操建议是:在系统内部定义一个统一的金额类型,比如Money类,内部持有BigDecimal字段,所有入参出参都走这个类型。然后在所有外部接口边界做单位转换,并把转换逻辑收敛到一处,禁止在业务代码里散落divide(100)和multiply(100)。你可能会觉得这有点小题大做,但资损类事故十有八九都出在这种“看似没问题”的地方。

3.2 乘除法与舍入模式的选择

BigDecimal的乘法比较简单,multiply结果精度是两个乘数精度之和,一般不会出问题。真正的难点在除法:除不尽的数怎么办?

比如100元分给3个人,每人33.33元,还剩0.01元;或者某个订单的优惠金额需要按商品金额占比分摊,除出来是无限小数。这时候你必须指定舍入模式,否则会抛ArithmeticException: Non-terminating decimal expansion。

舍入模式的选择不是随意拍的,需要结合业务语义:

  • HALF_UP(四舍五入)是大多数场景的默认选择,符合人的直觉。
  • HALF_EVEN(银行家舍入)在金融领域更常见,它会让结果偏向偶数,长期统计下误差更小。
  • DOWN(直接截断)常用于给用户算优惠的场景,确保平台不亏。
  • UP(向上进位)常用于罚金、利息的场景,保证应收足额。

最容易被忽略的是多步舍入的累积误差。建议整个计算链路里,中间过程尽量用高精度(比如统一scale=6或更高),只在最终落库或展示时做一次舍入。

3.3 大小比较必须用compareTo而不是equals

这是BigDecimal最经典的一个坑。new BigDecimal("1.0").equals(new BigDecimal("1"))返回的是false,因为equals会同时比较数值和精度(scale);但compareTo只比较数值,返回0。如果你用equals去判断金额是否相等,明明都是1元,结果判成不相等。

同理,判断一个金额是否大于0,很多人顺手就写amount.compareTo(BigDecimal.ZERO) > 0,这是对的;但如果你写成amount.equals(BigDecimal.ZERO)去判断恰好等于0,就可能在精度不一致时翻车。

我在代码评审里反复强调一条规则:凡是涉及BigDecimal的比较,一律用compareTo,禁用equals。也有团队写了一个AmountUtils.isEqual(a, b)之类的工具方法,内部统一走compareTo,这样至少能在工具层兜底。

3.4 单位换算与JSON序列化的坑

BigDecimal在序列化成JSON时默认会变成数字,比如10.00可能被序列化成10,或者反过来,前端收到10.0然后又按数字做运算,精度就可能损失。尤其是JavaScript的Number类型对超过Number.MAX_SAFE_INTEGER的大整数会丢精度,BigDecimal如果以数字形式传到前端,一旦数值很大或小数位很多,前端再传回来就变了。

工程上的标准做法是:对外接口(尤其是HTTP接口)把金额序列化成字符串,前端展示直接用字符串,不做数值运算。Jackson里可以配置ToStringSerializer,或者统一用@JsonFormat(shape = JsonFormat.Shape.STRING)来标注金额字段。前端要参与计算的话,建议用字符串接、字符串回传,由后端统一处理。我自己踩过这个坑:一个后台管理系统的导出功能,金额字段被Excel插件自动转成数字,几分钱的记录直接显示成0,后来把所有金额字段统一转字符串导出才解决。

3.5 数据库列类型与ORM映射的匹配

数据库层面也有一堆讲究。MySQL里对应BigDecimal的自然是用DECIMAL类型,比如DECIMAL(12,2)表示总共12位、小数2位,整数部分最多10位。这里要特别提醒两点:一是DECIMAL(10,2)能存的最大值是99999999.99,如果业务量增长导致金额超过这个上限,数据库不会报错,但会截断成最大值,等财务发现时已经晚了。所以定义精度时必须按未来的峰值预留,我一般建议至少预留到DECIMAL(14,2)以上。

另一个点:如果用了Long方案,ORM框架里字段类型是Long,映射到数据库BIGINT,这是没问题的。但如果你把数据库列定义成INT(4字节),那最大只能存21亿多,以分计量的话就是2100万元,很多订单系统上线几年就能撞上这个上限,直接溢出报错或者变成负数,非常危险。表结构设计这一步,千万别偷懒用默认长度。

3.6 前端展示与精度保护的最后一公里

后端所有防线都做完了,前端依然可能捅娄子。最常见的就是前端又做了一遍金额计算。比如购物车页面,前端根据单价和数量自己算合计,你以为后端会重新算一遍,结果后端真就信了前端传过来的合计金额,直接落库。稍微懂点技术的人都知道,前端传的数据永远是不可信的,但这类的“信任”事故在团队里反复出现。

我的建议是:前端拿到的金额数据一律当字符串处理,只做展示用。任何需要计算的金额,都应该把原始参数传给后端,由后端统一计算后返回结果。前端如果想做实时展示,可以调一个预计算接口,而不是在前端代码里parseFloat(price) * quantity。要不厌其烦地在代码评审时强调:金额计算不准出现在前端。

4. 实操:一个完整金额链路的落地方案

聊完理论,我直接给一套我目前在项目里用的标准做法,你可以照着抄。

4.1 表结构设计:从源头定好类型

以订单表为例,我的习惯是核心金额字段全部用DECIMAL(14,2),同时加一个currency_code字段标识币种。为什么不用更高精度?因为大多数业务场景就到分。但如果涉及更复杂的分佣、返利,可以考虑DECIMAL(18,4),统一多留两位小数做中间精度,展示时再四舍五入到分。

CREATE TABLE `order` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '订单号', `total_amount` decimal(14,2) NOT NULL COMMENT '订单总额(元)', `discount_amount` decimal(14,2) NOT NULL DEFAULT '0.00' COMMENT '优惠金额(元)', `pay_amount` decimal(14,2) NOT NULL COMMENT '实付金额(元)', `currency_code` varchar(8) NOT NULL DEFAULT 'CNY' COMMENT '币种', `status` tinyint NOT NULL DEFAULT '0' COMMENT '订单状态', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

配套的Java实体类里,对应的类型就是java.math.BigDecimal。MyBatis-Plus、JPA这些主流ORM框架对BigDecimal映射到DECIMAL都有很好的支持,不需要做额外转换。

4.2 计算层封装:统一入口防呆

我强烈建议你别在业务代码里直接new BigDecimal,而是统一封装一个金额工具类。核心目的不是为了省打字,而是把所有的规则收敛到一个地方,比如舍入模式、默认精度、单位换算。下面是一个简化版的参考:

public final class MoneyUtils { private static final int DEFAULT_SCALE = 2; private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_UP; private MoneyUtils() {} public static BigDecimal of(String value) { return new BigDecimal(value).setScale(DEFAULT_SCALE, DEFAULT_ROUNDING); } public static BigDecimal of(long valueInCent) { return BigDecimal.valueOf(valueInCent, 2); } public static BigDecimal add(BigDecimal a, BigDecimal b) { if (a == null || b == null) { throw new IllegalArgumentException("金额参数不能为空"); } return a.add(b).setScale(DEFAULT_SCALE, DEFAULT_ROUNDING); } public static BigDecimal subtract(BigDecimal a, BigDecimal b) { if (a == null || b == null) { throw new IllegalArgumentException("金额参数不能为空"); } return a.subtract(b).setScale(DEFAULT_SCALE, DEFAULT_ROUNDING); } public static BigDecimal multiply(BigDecimal a, BigDecimal b) { if (a == null || b == null) { throw new IllegalArgumentException("金额参数不能为空"); } // 乘法结果精度容易膨胀,统一截断到默认精度 return a.multiply(b).setScale(DEFAULT_SCALE, DEFAULT_ROUNDING); } public static BigDecimal divide(BigDecimal a, BigDecimal b, int scale, RoundingMode mode) { if (a == null || b == null) { throw new IllegalArgumentException("金额参数不能为空"); } if (b.compareTo(BigDecimal.ZERO) == 0) { throw new IllegalArgumentException("除数不能为0"); } return a.divide(b, scale, mode); } public static boolean equals(BigDecimal a, BigDecimal b) { if (a == null || b == null) { return a == b; } return a.compareTo(b) == 0; } }

注意几个细节:BigDecimal.valueOf(long, scale)可以把以分为单位的整数直接转成元,比如BigDecimal.valueOf(600, 2)得到6.00;setScale(DEFAULT_SCALE, DEFAULT_ROUNDING)保证了每次运算结果都规整成两位小数。有人会问中间计算精度只保留2位会不会损失精度?我的做法是:如果中间计算链路长,内部会用scale=6,只在最终落库时收敛到2位,上面的简化写法为了可读性做了一定取舍,你可以按需调整。

4.3 兜底校验与对账:不可省略的最后一环

无论你选哪种方案,我都建议在系统里加一道独立的金额校验。比如订单创建时,增加一条校验规则:pay_amount = total_amount - discount_amount,如果等号两边不一致,直接拒绝请求并告警。再比如,日终跑批时可以写一个对账任务,把订单表里所有金额字段进行聚合运算,和财务系统的汇总结果比对,一旦不一致就生成差异单让人工核查。

这听起来像多做了很多工作,但它其实是你最后的安全网。代码写得再小心,总会有你没想到的业务分支;但校验逻辑一旦存在,就能把问题拦截在用户发现之前。我见过不少成熟的团队,业务代码写得一般,靠的就是一套扎实的校验和对账体系撑住了线上稳定性。

5. 特殊场景:换字段类型怎么操作最稳

5.1 老系统从浮点迁移到定点类型

我接手过不止一个“历史包袱”项目,数据库里的金额列是FLOAT或者DOUBLE,里面已经存了大量脏数据。做迁移的时候,绝对不能直接在数据库层面执行一句ALTER TABLE改类型,否则浮点数的二进制近似值会直接变成一串很难看的尾数。

正确的思路是分四步走:

  1. 冻结数据变更,或者选择业务低峰期操作。
  2. 把每一行的浮点金额读出,做一次“金额规整”,比如用BigDecimal.valueOf(doubleValue).setScale(2, RoundingMode.HALF_UP)得到两位精确值。
  3. 把规整后的值写入新增的DECIMAL列,用SQL批量更新。
  4. 应用发布新代码、确认数据一致后,再删除旧列。

这一步最容易翻车的场景是浮点值本身就已经不准确,比如历史记录里存了29.700000000000003,你读出来再四舍五入可能得到的是29.70,但业务真实值可能是29.71(因为当时的计算方式不同)。这种数据靠程序是救不回来的,只能靠人工结合业务单据去核对。所以做这种迁移前,务必先导出一份差异清单给业务方确认,而不是闷头直接改库。

5.2 SQLite这类轻型库改类型要重建表

你可能觉得只有MySQL、PostgreSQL这类重型数据库才有改类型的需求,实际上我处理过好几个嵌入式项目,用的是SQLite。SQLite是个典型的弱类型数据库,它允许你在DECIMAL列里塞字符串,也允许你在INTEGER列里塞小数。这种灵活性的好处是开发时很爽,坏处是如果你发现某个金额列存进去的类型不对,想改列类型,直接执行ALTER TABLE ... ALTER COLUMN通常会失败,因为SQLite根本不支持这种语法。

SQLite的标准做法是“新建表-迁移-删旧表-重命名”十一步流程,简单说就是:

  1. 创建一个新表,字段类型按目标定义好(比如DECIMAL(14,2))。
  2. 从旧表查询所有数据,做类型转换和清洗后插入新表。
  3. 删除旧表,把新表重命名为旧表的名字。
  4. 重新创建对应的索引和触发器。

这个过程最怕的是表里数据量大,迁移耗时超过业务的接受范围。我自己踩过的坑是,迁移过程中如果有新的写请求进来,可能会出现数据丢失。所以正确的做法是先停写再迁移,或者把新数据写入一个兼容层,等迁移完成后再切换到新表。对于SQLite这种轻量库,通常的应对是直接做只读维护窗口。

5.3 泛微OA这类低代码平台的金额字段调整

有读者可能会问,像泛微OA这类低代码平台上改明细表字段类型,是不是也要处理类型问题?这类平台通常允许你在表单设计器里修改控件的字段属性,但要注意的是,平台底层表结构的变更往往也是先加临时列、做数据迁移、再改原列。如果你只是想在界面上把某个文本下拉框改成金额下拉框,最简单的操作是在表单设计器里调整控件类型,再检查历史数据是否需要清洗。如果历史数据里有非数字文本,金额控件可能会读取异常,需要手动批量修正。

这类场景虽然技术含量不如手写SQL迁移高,但同样需要评估数据兼容问题。很多低代码平台会直接“软改”字段展示类型,而底层字段权限和流程规则可能没有同步,这需要自己额外验证一遍流程节点的金额字段在审批时是否还能正确触发合计公式的计算。别问我为什么知道,问就是我改过一次,结果流程在某个审批节点一直校验不通过,查了半天才发现历史审批记录里的金额字段是字符串类型,新规则一校验就报类型错。

6. 常见问题速查与避坑清单

最后把高频问题整理成表,方便你直接查阅。

常见问题根因解决方式
0.1 + 0.2算出来是0.30000000000000004浮点数二进制表示金额一律不用double/float,改用Long或BigDecimal
equals比较两个BigDecimal结果falseequals会比较精度金额比较统一用compareTo
divide抛出ArithmeticException除不尽且未指定舍入模式调用divide时显式传入scale和RoundingMode
前端传回的金额数字越来越大JSON数字精度损失或前端运算对外接口金额一律字符串序列化
数据库金额自动变成29.999999列类型是FLOAT/DOUBLE迁移到DECIMAL并进行规整
订单金额突然变成负数或上限值数据库字段长度不够或溢出DECIMAL预留冗余位数,定期核对表结构
显示金额和计算金额差一分钱舍入时机不一致或四处舍入中间用高精度,最终统一舍入一次
秒杀场景Long累加比BigDecimal慢对象创建和GC高频纯计数场景用Long,交易结算场景别省

6.1 三个我认为值得养的编码习惯

第一,所有金额字段的业务名里带上单位。比如Java类里,如果字段以分为单位,命名就写成totalAmountInCent;以元为单位,写成totalAmount并在注释里写明精度。命名上带单位,能减少一半以上的单位混乱问题。

第二,统一用一个工具类做金额的新建和运算。之前展示的MoneyUtils还不够完善,你还可以在里面加历史金额计算、税率计算等公共逻辑,但核心是让所有人通过同样的入口处理金额,而不是各处散落BigDecimal运算。

第三,每次改到金额逻辑,强制自己写一个单元测试覆盖精度边界。比如0.01、99999999.99、0.1+0.2、9999.99除以5000这些用例。这套测试能让你在重构时有底气,不至于上线前一天晚上睡不着。

6.2 我个人最终的选型倾向

如果你让我给一个一句话结论,我会说:默认用BigDecimal存“元”,数据库用DECIMAL,接口传字符串;只有当你实在无法接受BigDecimal的性能开销,才考虑Long+分方案,且必须搭配严格的单位封装和全链路约束。

我见过太多团队在“性能优化”的旗号下选择了Long+分,结果半年后因为单位问题出了一次大事故,回头看省下的那点性能微不足道。金额系统的第一属性永远是正确性和可追溯性,而不是快。当然,如果你做的不是金额系统,只是数字计数,那Long没有任何问题;但只要是钱,就请对它多一点敬畏。

选型从来不是技术偏好问题,而是风险取舍问题。想清楚你系统里万一金额错了会付出多大代价,自然就有了答案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 23:23:42

26届课程论文怎么写?实测一学期,这些坑和捷径都告诉你

课程论文看着篇幅不长,真动笔才发现处处是坎:选题拿不准、文献理不清、初稿逻辑散、改三轮还被导师说表述不严谨。这学期我前后用了四款辅助工具,把踩过的坑和真正有用的功能一次说清。 passbug官网直达入口:https://passbug.cn/ …

作者头像 李华
网站建设 2026/10/2 23:20:43

DeepSeek Harness桌面端安装配置与skill部署全指南

1. 桌面端来了,为什么这件事比想象中重要DeepSeek Harness 出官方桌面端这件事,我第一反应不是"终于不用开浏览器了",而是"这套工作流终于可以脱离浏览器标签页活下去了"。如果你之前用过 DSH(社区里对 DeepS…

作者头像 李华
网站建设 2026/10/2 23:18:35

使用 Jev 作为 search reranker:基准测试与实现方法

作者:来自 Elastic Dustin Coates 我们让 Jev 为 Elasticsearch hybrid search 结果进行评分,并让一个简短的 Python policy 执行 reranking,在 250 个 Amazon Shopping Queries 上将 nDCG10 从 0.9351 提升到了 0.9565,所有代码都…

作者头像 李华