商业计算中小数点金额消除误差-商业计算2
一般来说产生误差主要是因为四舍五入,而金额和单价、数量的小数位数都是有限的。为了避免小数点误差,要注意以下几点,否则经常会导致可能出库时数量出完了,但是金额还不为零的情况出现(不过这种情况很难避免,可以允许用户做调整,做一些特殊的出入库单,即允许只有金额、没有数量的出入库单,其实这种单据常用的,这是别话)
注意几个算法:
1、多用加法、少用乘法。
例如订单上可能有这么几个字段:数量、单价、含税单价、税率、金额、税额、价税合计。关系如下:
数量×单价=金额
数量×含税单价=价税合计;
数量×单价×税率=税额;
金额+税额=价税合计。
如果这里你分别用乘法算出金额、税额、价税合计,那么最后很可能就出现金额+税额不等于价税合计。这就是我前面说的不能自圆其说了,对于这种情况,你只能用乘法算出其中的两个,然后第三个,就其中的这两个金额做加法或者减法得到。例如,先用乘法算出金额和税额,然后用金额+税额得出价税合计;或者先用乘法算出价税合计和税额,然后用减法,算出金额。这样就不会产生误差。
2、注意用“倒挤”算法(分摊误差)即计算尾差
数量乘以单价不等于金额的现象肯定存在,这是允许的,实际手工业务中也是允许的,因为四舍五入的关系,谁也避免不了这个;但是软件中的做法是:允许误差,但是误差们要能自圆其说。时刻牢记要让“总计数要等于明细数的合计”。例如,总计数是A,总计数被分成了若干份,分别是B、C、D,最后一定要保证:B+C+D=A。
例如,一笔钱是456.87元,要按比例分成三份,第一份是15.8%,第二份是47.3%,第三份是36.9%,没有经验的程序员肯定是用总金额分别乘以三个比例,那肯定就有问题了,第一份:456.87×0.158=72.19。第二份:456.87×0.473=216.1,第三份:456.87×0.369=168.59,那么把这三个数再合计起来等于多少?72.19+216.1+169.59=456.88。比原来的总额多出了一分钱!这就不平了。所以,碰到这种情况,一定要选择分摊误差的地方,一般来说都是用最后一笔分担误差,所以叫做倒挤算法。所以这个例子应该这么算,前两笔都用乘法没问题,到了最后就一定不能总额乘以比例了,而是要用总额减去前面两笔的合计数,得出第三笔:456.87-(72.19+216.1)=156.58。这样就对了。
3、每一次乘除法以后立即按照精度四舍五入
一般在软件系统中会预先让用户定义数量、单价、金额的小数位数,这是常识性的做法,但是程序员必须在程序中注意四舍五入的时机:在得出一个结果(数量、单价、金额三者任何一个)之后,必须立即四舍五入以后,再投入其他运算,如果做了一次乘法、除法以后却没有立即根据定义的精度四舍五入,而是直接投入其他运算,最后再四舍五入,那肯定不对。例如:单价:8.49,数量:45.5789,用数量乘以单价得出金额:386.964861,如果你这时候你不把这个金额先四舍五入了再投入其他运算,那么肯定产生误差。
4、写程序注意选择相同类型的数值类型进行乘除运算。如果不相同类型的数值类型乘除,计算机自己都有可能产生误差,我就碰到过计算机算出:3.1×2=6.3这种情况
-------------------------------------------------------------------
建议做法,在数据类型等都正确的前提下,
(1)使用BigDecimal进行加减乘除
(2)每一次乘除法以后立即按照精度四舍五入,
(3)复杂时结合“倒挤”算法计算。
可以解决小数(四舍五入)误差。
-------------------------------------------------------------------
“倒挤”算法实例:
(1)
例子:某零件3件一个包装盒,每盒价格1.00元。
第一次出库1个零件,出库金额:0.33元
第二次出库1个零件,出库金额:0.33元
第三次出库1个零件,出库金额:0.33元
合计:0.99元。这与入库金额1.00元不符。
(2)“倒挤”算法实现:最后一次出库零件金额=1.00-(0.33+0.33)=0.34
有许多方法解决这个问题。例如,采用零件数量和金额独立存放的方案,建立如下数据表结构
零件编码 入库数量 入库金额 出库数量 出库金额 总数量 总金额
=================================
A 3 1.00 3 1.00
A 1 0.33 2 0.67
A 1 0.33 1 0.34
A 1 0.34 0 0.00