上周我上线了一个每日复利计算器。起初我以为这件事很简单——数学原理早已是定论:A = P(1 + r/n)^(nt),把参数接上输入框,搞定。 但事实并非如此,而且真正有趣的 bug 根本不在公式本身。 教科书公式处理的是"一笔本金放着不动"的情形。可一旦你加入定期投入——比如每月定投 100 美元——你就必须决定:每笔新存入的钱,从什么时候开始计息。 最偷懒的实现,是把所有投入加在期末一次性结算: ``` // 错误:定投金额从不参与复利 const balance = P * Math.pow(1 + r / n, n * t) + monthly * 12 * t; ``` 正确的做法需要让每笔存款从到账当天起就独立计息: ``` // 每笔投入独立计算自己的剩余期限 let balance = P * Math.pow(1 + dailyRate, totalDays); for (let day = 1; day <= totalDays; day++) { if (isDepositDay(day)) { balance += monthly * Math.pow(1 + dailyRate, totalDays - day); } } ``` (生产环境里我用的是封闭形式的年金公式,而不是循环;但循环版本最能直观展示两者差异。) 这个差异有多大?以 10,000 美元本金、年利率 5%、每日复利、每月追加 100 美元、持续十年为例: 结果是 3,535.63 美元的静默误差。我在开发时抽查了好几个排名靠前的复利计算器,不止一个给出的是错误版本的数字。 银行在报价时会标注"按 360 天基准计息",于是我加了一个 365/360 天数基准切换开关,预期会闹出大动静。 结果呢?相同的名义利率下,仅改变指数基准,10,000 美元十年期的结果只差 1 美分。这个开关确实是诚实的,但把它当亮点宣传,那就是在表演。 真正影响资金量的是"银行家算法":年利率除以 360,但按全部 365 天收取利息。这等于把实际利率乘以 365/360,等效利率提升 1.39%,把 5% 变成大约 5.07%,让 16,486.65 美元变成约 16,601 美元。同样的利率标签,效果差三个数量级。如果你在构建金融计算器,这才是最值得关注的那个细节。

打开网易新闻 查看精彩图片