金额精度与小数位:兑换里「少一点」到底差多少
2026-10-25 实测:19 与 19.9 USDT 差 4.7% 本金、11.0% 到手;100 与 100.000001 逐位相同。再读格式化代码:到手 8 位小数、费率 8 位有效数字、上下限边读边漂。
「少一点」有两种。 2026-10-25 18:06:17 UTC,站内报价接口把 19 USDT-TRC20 报成 0.01334439 XMR;两秒后,19.9 USDT 报出 0.01481818。多付 4.7% 的钱,多拿 11.0% 的门罗——贴着下限的位置,少一点不是按比例少。四十秒前,同一个接口对 100 与 100.000001 USDT 逐位相同:0.14598334 XMR,费率 0.0014598334。多出的那一微个 USDT 什么都没换到。两个结果都对,它们一起划出了「少一点」的两种含义:跌破下单下限很贵,落在最后几位则完全看不见。 (XMR 换 USDT 退出路线)
每种资产都有不可再分的最小单位。 1 BTC = 100,000,000 聪,按今天 80,703 美元算,1 聪 = 0.000807 美元;1 XMR = 1,000,000,000,000 piconero,1 piconero = 0.0000000006 美元;1 ETH = 10^18 wei,1 wei 约 0.0000000000000024 美元。ZEC 的 zatoshi 是 1e-8,LTC 的 litoshi 是 1e-8,SOL 的 lamport 是 1e-9,TRX 的 sun 是 1e-6;USDT 在波场和以太坊上都是 6 位小数,最小单位 0.000001 美元。这些不是显示习惯,是账本能移动的最小数字;你看到的每个报价,都是某个整数计数的四舍五入版本。 (SOL 换 XMR 指南)
报价被裁到 8 位。 站内报价接口对到手量返回 `Number(best.toAmount.toFixed(8))`,对费率返回 `Number(best.rate.toPrecision(8))`;下单接口锁价时同样把到手量写成 8 位。上面那一微个 USDT 就是这样消失的:0.000001 USDT × 约 0.00146 ≈ 1.5e-12 XMR,连第 8 位都够不到。按今天的价格,8 位取整最多让你少 0.00000001 XMR,约 0.000006 美元。钱上可以忽略,但它决定了更要紧的事:承诺本身只有 8 位小数,而对面那条链记的是整数。 (XMR 换 USDT 退出路线)
位数会骗人。 同一个接口打出过 595.698 USDT(1 XMR)、0.0014615927(费率)、293.397277258162(XMR 上限)。看着是三种精度,其实是两条规则加一个格式化器:费率永远是 8 位有效数字,小数位数随量级变化;JSON 数字会丢掉尾随零,595.698 实际是 595.69800000。别数小数点后的位数,去读规则:到手量 8 位小数、费率 8 位有效数字、上下限用路由引擎自己的输出——这就是为什么同一个接口里,门罗上限带 12 位、USDT 下限只有 6 位。 (XMR 换 USDT 退出路线)
上下限本身在漂。 18:02 时 USDT-TRC20→XMR 的下限是 19.801624、上限 176,509.209306;18:06 变成 19.786762 与 176,443.886799;18:10 又是 19.791362 与 176,464.859757。没有人改参数:两个边界都从出金侧反算,跟着汇率走。反方向也一样,XMR→USDT 的最大值打出 293.397277258162,最小值 0.02974636。发 1 个代币不会得到报价,而是一条结构化错误 `amount below minimum`,两个边界一起返回。结论很朴素:任何从页面上抄下来的数字(包括本文里的)都是快照,发送前重新报一次。 (XMR 换 USDT 退出路线)
底下那层账本是整数。 今天 18:00:31 UTC 的比特币区块 973,103 有 25 笔交易、41 个输出:最大 2,449,754,800 聪,最小 0(一个不带金额的 OP_RETURN);全块手续费 32,787 聪、5,140 vB,即 6.379 sat/vB,约 26.46 美元。波场区块 87,426,048 有 536 笔交易,其中 131 笔是 USDT 转账:最小一笔动了 3,375,404 个基本单位,即 3.375404 USDT,最大一笔正好 100,000.000000。门罗区块 3,791,996 装 42 笔、87,296 字节,块奖励 600,030,720,000 个原子单位,即 0.60003072 XMR。没有哪条链存「3.375404 个什么」——它们存整数,小数点是边缘层做的显示决定。 (XMR 换 USDT 退出路线)
浮点数在哪里真的咬人。 在 Node 里跑四行就明白这一类 bug:`0.1 + 0.2` 是 0.30000000000000004;`1.15 * 100` 是 114.99999999999999——「Max」按钮把余额乘个价格,结果比余额少一丝,也可能比下限少一丝;`(0.000181501).toFixed(8)` 直接返回 0.00018150,第 9 位被静默丢掉而不是被拒绝;`10^18` wei 大于 2^53,JS 根本数不准 wei,在这个量级上给最大安全整数加 1 得到的还是它自己。所以金额要用整数或字符串表示,只在显示的那一刻才变成小数。
六步精度流程。 1)先换成最小单位再算:100 USDT 是 100,000,000 个单位,1 XMR 是 1,000,000,000,000 piconero。2)算你实际会发出的净额,而不是你希望发的金额——交易所提币会先扣费。3)别拿「Max」当报价金额:那是一次浮点减法,会留残数。4)离开下限保持一个可测的台阶,比如下限 19.79 就发 20,因为下限会动,被拒一次要赔上一分钟。5)金额不要超出资产本身的精度:BTC 的第 9 位小数不存在,谁取整谁就把它扔掉。6)对账用整数,不用显示值:XMR 差 1e-8 是 0.000006 美元,但 100 USDT 入金差 1e-6 就已经不是你申报的那个数了。 (XMR 换 USDT 退出路线)
在柜台上这意味着什么。 兑换里的精度,不是能背出十二位小数,而是知道眼前哪个数字是精确的:链上的整数、订单里 8 位小数的承诺、8 位有效数字的费率,还是会漂的上下限。19 与 19.9 USDT 之间,差的是 11% 的到手量;100 与 100.000001 之间,差的是零,因为它小于答案的分辨率。同一套物理,认清之后就不会为错的那一种付钱。按自己的量报一次价,看一眼下限,发净额,再用链上真正收到的整数去核对那 8 位的承诺。 (XMR 换 USDT 退出路线)