四舍五入到分计算器
结果
舍入结果
- 分
- 268
舍到分就是把一个金额截到两位小数,恰好半分时舍向远离零的一侧:2.675 变成 2.68,0.005 变成 0.01。它几乎是每一笔钱的最后一步,因为单价乘数量、税率乘小计,算出来的结果极少正好落在整数分上。做对这件事比看起来难。最直白的做法是先乘一百、舍成整数再除回来,而它恰恰在人们最在意的金额上失效:1.005 乘一百是 100.49999999999999 而不是 100.5,于是这个数往下舍成 1.00,那一分悄无声息地没了。把原值直接印成两位小数会犯同样的错,理由也一样——它看的是同一个略小于真值的数。两种错法都不会自己声张:印出来的是一笔再正常不过的金额,想发现它,只能事先知道答案该是多少。本页靠把小数点当作文本来移动绕开这个坑,舍入的判据因此落在精确值上。它同时把金额折算成整数分印出来,那才是值得留着手继续算的形式。结果按你的语言写钱:中文和英文一样把一千二百三十四元五角七分写成 1,234.57,而德语读者看到的是同一笔钱的 1.234,57;差一分钱的两笔金额,回来就是两个不同的数,这正是重点。
四笔看着平常、却会舍错方向的金额
| 金额 | 舍入后 | 分 |
|---|---|---|
| 2.675 | 2.68 | 268 |
| 1.005 | 1.01 | 101 |
| 0.005 | 0.01 | 1 |
| -0.005 | -0.01 | -1 |
这里每一行都是直白实现会弄错的金额,而每一行失败的理由各不相同。2.675 是所有人都举的例子,也是乘一百恰好成立的那一格——它正好落在 267.5 上;所以它是一个坏样本、却是一句好提醒:只核这一行的读者会发现自己的代码与页面一致,从而以为没什么可修的。1.005 才是真正的失效:百分之一没有精确的二进制表示,乘一百得到 100.49999999999999,两种直白做法都答 1.00。0.005 是恰好半分,远离零,在正数上这与向上舍是一回事。而 -0.005 是同一个金额带上了符号,两条规则在这里分道扬镳:把一个负的半数朝正无穷方向舍会得到负零,印出来是 -0.00。第三列一路并排印着,所以每一行还顺带展示了同一笔金额的两种视图——左边是小数,右边是整数分,而它由舍入后的金额算出,两者因此不可能对不上。
公式
2.675 -> 2.68;1.005 -> 1.01;0.005 -> 0.01;-0.005 -> -0.01;分 = 舍入后的金额 × 100
- value
- 要舍的金额,从负十亿到正十亿。两位以上的小数是故意收得下的:1.005 与 2.675 正是这一页存在的理由,一个拒收它们的输入框会把页面最想处理的那些情形挡在门外。字段不强制两位小数,也不该强制——本页讲的是舍入规则,不是输入格式
- 舍入后的金额
- 舍到两位小数之后的金额,也是结果的第一行。它是 number 而不是一段文本,所以它按你语言的小数点与千位分组来写:中文与英文看到 1234.57 或 1,234.57,德语看到 1.234,57。这一处与通用舍入页的差别是有意的,也是两者在同一个输入上唯一的差别
- 分
- 同一笔金额折合成的整数分,是一个独立的输出,而不是第一种写法的另一种拼法。268 是能加减、能拆账、能接着数的整数;2.68 是一乘就变回浮点数的小数。它由舍入之后的金额算出,而不是由原始输入算出,所以两行不可能对不上——1.005 给出的永远是 1.01 旁边 101,不会出现 1.01 旁边 100
- 半分远离零
- 恰好落在半分上的处理:0.005 进到 0.01,-0.005 退到 -0.01。向上舍与远离零在正数上结论一致,在负数上分道扬镳,而区别正是在那里才有意义——朴素的整数舍入会在这里返回一个负零,而「-0.00」不是该拿给人看的欠款金额
- 1.005 × 100 = 100.49999999999999
- 本页绕着转的那个失效。百分之一在二进制里没有精确表示,所以乘一百之后落在半分点下面一点点,舍入因此朝了错的方向。凡是先乘后舍的方法——无论是舍那个乘完的数,还是把原值格式化成两位小数——都继承了这个误差,答案都是 1.00。改成在文本里移动小数点,值保持精确,答案就是 1.01
- -0.005 -> -0.01
- 负的半分,也是两条舍入规则分开的地方。这笔金额正好在零下面半分处,规则把它推得离零更远而不是往上抬,所以是 -0.01 而不是 -0.00。把一个负的半数朝正无穷方向舍,得到的是负零,印出来是 -0.00,读起来像是舍入出了错,尽管算术本身自洽
凡是最后落到钱上的计算都停在这里。账单在几个人之间分摊,极少正好分到整分,多出来的那一分总要有人承担或者舍掉;税率、小费比例乘上小计,得到的金额小数位比钱本身的位数多;单价乘数量同理。到最后一步才舍是显而易见的,但更要紧的是中间那一步也得舍。摊还表每一期都拿剩余本金算利息,而且每一期的利息都要先舍到分再加回本金,因为只在最后舍一次,零头会一路累积——三百六十期下来这个漂移是整块钱,最后的本金余额就跟贷款方对不上了。同一个道理适用于任何由「先舍过的部分」拼起来的总数:先舍各部分再相加,而不是反过来。这种活里分才是该留着的那个形式,它是整数,一路都精确。当问题问的是小数位本身而不是钱——某个测量值到三位小数、某个数据到两位有效数字——通用舍入页把十三档目标收在一处;本页只管钱用的那一档,并且用一条算术弄不错它的规则来做。
算例
默认那一笔:2.675
- 这笔金额介于 2.67 与 2.68 之间,而且正好落在两者正中
- 半分远离零,所以答案是 2.68 而不是 2.67
- 把舍入后的金额乘一百:2.68 × 100 = 268
- 答案就是两块六毛八,也就是 268 分
所有人都会举的那个例子,也是它的默认值。它同时是朴素做法恰好也对的那一格:2.675 乘一百在二进制里正好落在 267.5 上,而 1.005 不会——所以如果页面上只有这一格,拿自己的代码来核对的读者会发现两边一致,从而以为根本没有问题要解决。下面那一格才看得出差别,这就是首页给这个金额、正文却用另一个举例的原因。
骗过所有人的那一笔:1.005
- 这笔金额正好落在 1.00 与 1.01 的正中
- 半分远离零,所以答案是 1.01
- 乘一百得到的是 100.49999999999999 而不是 100.5,因为百分之一在二进制里没有精确表示
- 先乘后舍的做法于是朝下舍,给出 1.00,那一分没了
- 在文本里移动小数点则让值保持精确,出来的就是 1.01
本页为之而生的那一格。乘一百得到 100.49999999999999,并不是碰巧差一点点,而是因为 1.005 在二进制里存不下、存进去的那个值本身就比真值小一丝。两种直白做法都继承了这个误差:舍那个乘完的数得 1.00,把原值格式化成两位小数也得 1.00,理由一样——它们看的是同一个略小的数。印出来的东西看不出任何异常,一分钱就是没了,而唯一能发现它的办法是事先知道答案该是多少。钱的计算落在这种半分金额上的频率非常高,因为税率乘上奇数价格,随手就是一个。
一笔普通金额:1234.5678
- 前两位小数是 56,后面跟的是 78——比半分多
- 所以第二位小数进位:1234.57
- 乘一百:1234.57 × 100 = 123457
- 写出来就是一千二百三十四元五角七分
平平无奇的一格,值得放一个例子是为了让页面不全是陷阱。这里什么都没发生:第三位小数是 7,所以照常进位,两种做法彼此一致。这一格真正展示的是写法,而那正是读者第一眼会注意到的差别。中文与英文一样把它写成 1,234.57——小数点在前、逗号分千位;德语把它写成 1.234,57,两个符号刚好对调;法语把逗号换成一个空格。数字本身完全相同,页面一个也没动,变的是「你的语言怎么写钱」这件事。中文读者在这里看不出区别,正因为中文的写法恰好与英文一致,所以这一条得靠正文说清楚:它跟随的是语言规则,而不是某一种固定写法。
零下面半分:-0.005
- 这笔金额正好落在零与负一分的正中
- 半分远离零,在这里就是往下走:-0.01
- 乘一百正好是 -0.5,这一格缩放得很干净
- 把 -0.5 按朴素的整数舍入处理会得到负零,印出来是 -0.00
- 在文本里移动小数点得到的是 -1 分,金额是 -0.01
把两条舍入规则分开的那一格。在正数上,「半数向上舍」与「半数远离零」结论一致,所以这个区分看起来像吹毛求疵。到这里两者才分岔:把 -0.5 朝正无穷方向舍,得到的是一个带负号的零,而「负零分」不是该拿给人看的东西。这一格缩放得很干净,因为 -0.005 乘一百正好是 -0.5——二进制误差并不随符号对称,这也是一组测试金额里必须有负数的原因。退款、贷记、以及超过金额本身的折扣,都会走到这里。
局限
金额必须是从负十亿到正十亿之间的数,而舍入后的结果允许落在范围外一步:999999999.999 会进到正好十亿,那一格的「分」是一千亿。这个量级下输出仍然精确,因为一千亿分这个整数还在「每个整数都能被精确表示」的范围内。舍入规则是半数远离零,而且没有开关可调——一个带规则开关的页面得解释你所在法域用的是哪一条,而它不解释。负数按远离零的方向舍,不是向上舍,两者恰好在半分那一类金额上不同。页面不携带货币:它知道一笔金额折合多少分,但不知道那些分叫什么,所以输出是数,符号由你自己加。它一次只舍一笔金额,不做分摊、不指派零头、不平摊还表。下面那张参考表印的是四个固定金额,不跟着你的输入走,而且表里的数字不做本地化,所以它们在十个语言下都读作 2.675 与 -0.005,尽管上面的结果面板是本地化的。
常见问题
- 1.005 为什么舍成 1.01 而不是 1.00?
- 因为它正好落在 1.00 与 1.01 正中,而半分的规则是把金额推向离零更远的一侧。这个答案之所以显得意外,只是因为直白的实现会返回 1.00。把 1.005 乘一百得到的是 100.49999999999999 而不是 100.5——百分之一在二进制里存不精确,而存进去的那个值比真值小一丝。于是先做缩放的方法,无论是舍那个数还是把它格式化成两位小数,看到的都是一个略低于半分点的值,本来该进位的那一位因此没有进。那一分不是响亮地丢掉的,输出是一笔看着再正常不过的金额。正是因为存在这一个输入,这里才值得有一个页面,而不是一行公式。
- 是四舍五入还是银行家舍入?
- 半数远离零——正数往上进,负数往下退。这是几乎所有现金系统在做的、也是普通人说「四舍五入」时想的那件事,所以 2.5 分变成 3 分,-2.5 分变成 -3 分。银行家舍入(半数取偶)是另一条常见规则,它存在的意义是防止一长列数字相加时整体向上偏移,在统计和部分会计工作里要紧,而本页做的不是它。两条规则除了恰好落在半分的那些金额以外处处一致,所以差别很容易被忽略,直到某一次结算正好踩在上面。页面不提供开关,因为一个开关就得解释哪条规则适用于哪个法域。
- 为什么除了金额还要印一个分?
- 因为两者各有各的用处,而整数那个才是一路保持精确的形式。268 是整数:你可以把它加到别的整数分上,在几个人之间拆开,乘上一个数量再接着算,中途什么都不会发生。二点六八是小数,一乘就重新变成浮点数,又回到你来这里想躲开的那个表示问题。这也是为什么分是由舍入之后的金额算出来的,而不是由原始输入算出——分开算的话,像 1.005 这样的输入可能一行印 1.01、另一行印 100,而同一块面板上的两个数互相打架,比其中哪一个略有偏差都更糟。
- 结果为什么有些语言里用逗号当小数点?
- 因为货币的写法随语言变,而这在各地并不一样。中文和英文把小数点放在分前面、用逗号分千位,写成 1,234.57;德语和欧洲大陆大部分地方刚好对调,写成 1.234,57;法语把逗号换成一个空格,写成 1 234,57。本页把金额当作数而不是一段文本输出,所以格式跟随你正在阅读的语言。中文读者在这里看不出区别,因为中文的写法恰好与英文一致——但这一点仍然是语言规则,而不是某个固定写法。本站的通用舍入页故意不这么做:它的结果是文本,小数点恒定、没有千位分隔符,这就是两页在同一个输入上唯一的真实差别。
- 舍到分究竟在什么地方要紧?
- 凡是总数由若干部分拼起来的地方都要紧。摊还表是最清楚的一例:每一期的利息按剩余本金算出来,舍到分,再加回本金,而只在最后舍一次会让零头一路累积——三百六十期下来这个漂移是整块钱,最后的本金余额就跟贷款方对不上了。分摊账单是同一个形状,因为总额在几个人之间分,极少正好落在整分上,多出来的那一分总得有个去处。每一种「按比例算在金额上」的东西——税、小费、折扣——算出来都带着比钱本身更多的小数位。值得带走的那条规则是:先舍各部分,再把舍好的部分相加,而不是反过来。
- 页面最大收多大的金额?
- 正负十亿,而舍入后的答案允许落在范围外一步:999999999.999 会进到正好十亿,那一格的分是一千亿。这个量级下输出仍然精确,因为一千亿分这个整数还在「每个整数都能被精确表示」的范围内,上界离整数开始被近似的那个点还很远。超出边界的输入会被拒绝而不是截断,因为悄悄改掉一个你没输入的金额,比直接说不收更糟。
参考资料
- 四舍五入——中文语境下的四舍五入、它与「半数远离零」的关系,以及负数的处理 — 维基百科(中文)
- Rounding —— 半数远离零这条规则,以及它与半数向上舍在负数上的差别 — Wolfram MathWorld(美国)
- What Every Computer Scientist Should Know About Floating-Point Arithmetic —— 1.005 存成双精度之后为什么比半分点低一丝 — Oracle(原载 ACM Computing Surveys)
- 教育部关于印发义务教育课程方案和课程标准(2022 年版)的通知——附件清单第 5 项为《义务教育数学课程标准(2022 年版)》;小数的四舍五入是小学阶段数与代数领域的内容,原文与学段要求以该附件为准 — 中华人民共和国教育部