美文网首页源铸开发
源铸:数值处理问题

源铸:数值处理问题

作者: 十月里的男艺术家 | 来源:发表于2018-10-19 05:28 被阅读0次

    一、数值存储计算方案

    高精度浮点数

    采用双精度浮点数是最简单的存储方案,数据库支持,编程语言也支持。只要数据库和编程语言采用一样的浮点数标准即可。

    然而,浮点数始终有精度问题,会产生很多人类大脑一般认知以外的问题。

    字符串存储

    字符串存储容易产生计算性能问题,而且也大小的比较排序不方便。若转化为浮点数计算,又回到前面问题。如果是自己编写加减乘除算法,复杂度更高。

    采用长整数

    系统设置的最大金额为100,000,000,000。

    64位有符号整数,最大数((263)-1)/(107)=922,337,203,685.4775807,完全满足最大值需求。

    采用整数存储和计算不会丢失精度,普通开发者可以轻松驾驭整数的计算,系统做比较排序操作也很方便。

    二、采用四舍五入的情况

    所谓四舍五入就是,当舍去位的数值大于等于5时,在舍去该位的同时向前位进一;当舍去位的数值小于5时,则直接舍去该位。

    小数保留七位转化为整数

    某些计算结果是小数,需保留小数点后七位,并乘以10的七次方转化为整数,才能保存到系统中。

    对于某些小数,因为浮点数的精度问题。需要小数点后7位后,四舍五入再乘以10的7次方取整。如下代码所示

      def mul_10_7(num: BigDecimal): BigInt = {
        (num.setScale(7, RoundingMode.HALF_UP) * BigDecimal(BigInt(10).pow(7))).toBigInt()
      }
    

    而不是该小数乘以10的7次方直接取整。如下错误代码:

      def mul_10_7(num: BigDecimal): BigInt = {
        (num * BigDecimal(BigInt(10).pow(7))).toBigInt()
      }
    

    三、采用直接舍去的情况

    页面百分比展示

    页面展示百分比。当进度为99.9999999%这种情况时,采用小数点后两位四舍五入。页面展示进度时,变为100%。因此,页面展示进度改为直接舍去

    (BigDecimal(inAmount) / BigDecimal(c.crowd_total) * 100).setScale(2, RoundingMode.DOWN)
    

    而不是采取四舍五入。如下错误代码:

    (BigDecimal(inAmount) / BigDecimal(c.crowd_total) * 100).setScale(2, RoundingMode.HALF_UP)
    

    四、累积误差的处理

    分红计算累积误差

    如果单位分红计算时,小数点后7位四舍五入。然后,按照每个账户持有份额乘以单位分红获得分红。这样会产生累积误差。如下错误代码所示:

    # 单位分红计算,小数点7位后四舍五入
    val unitPrice = (feeTotal / BigDecimal(xyzCount)).setScale(7, RoundingMode.HALF_UP)
    
    # 账户持有份额乘以单位分红
    mul_10_7(div_10_7(ab.amount) * unitPrice)
    
    

    正确思路是保留精度,计算乘积后再取整存储

    # 单位分红计算,保留精度
    val unitPrice = feeTotal / BigDecimal(xyzCount)
    
    # 账户持有份额乘以单位分红
    mul_10_7(div_10_7(ab.amount) * unitPrice)
    

    分红计算改进方案1

    上述分红计算方案,仍旧会产生问题。会出现两种错误情况,一种是分红池总金额大于实际分红;另外一种情况是分红金额小于实际分红。这两种情况都会导致系统总金额产生错误。因此,需要用如下方案改进。

    假设有n个分红账户,持有该份额为X1、X2、X3...Xn-1、Xn,总份额为:

    X=X1+X2+...+Xn
    

    总分红额为total,那么前n-1位分红计算公式如下:

    Ti=Xi/X*total
    
    i代表第i位分红者,i<=n-1
    
    

    最后一位分红Tn,计算公式如下:

    Tn=total-(T1+T2+...+Tn-1)
    

    这样保证分红计算没有错误产生。

    分红计算进一步改进方案2

    上述分红计算方案,仍旧会产生问题。会出现如下错误情况:如果份额计算时,小份额先分,大份额最后分,有可能出现大份额分不到或分少的情况。因此,需要用如下方案改进。

    假设有n个分红账户,n个分红账户需要按照降序的顺序分红,计算方案仍旧如方案1所述。

    五、银行家舍入

    本系统并未采用这中计算方法。

    银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。其规则是:当舍去位的数值小于5时,直接舍去该位;当舍去位的数值大于等于6时,在舍去该位的同时向前位进一;当舍去位的数值等于5时,如果前位数值为奇,则在舍去该位的同时向前位进一,如果前位数值为偶,则直接舍去该位。

    相关文章

      网友评论

        本文标题:源铸:数值处理问题

        本文链接:https://www.haomeiwen.com/subject/umbezftx.html