.Net业务平台的数值精度陷阱与解决方法

又是一阵痛和无奈的修改,公司业务系统数量小数位保留3位精度不够,需要保留6位才行,回想起这个问题,公司开发上线的ERP系统,数量,金额,成本的计算方式反反复复都修改过好多次,以前都没有对这个业务规则进行计算封装,和统一指定规则,修改就成了一件多而繁琐的的事情了;现在深刻体会这些业务细节将会对业务系统的运行和维护是非常重要的, 而业务系统规则的明晰和好的系统业务架构是对其的保证。    修改过程是一个渐进,琐碎的过程,希望有过次方面经验的朋友能提提建议,没有注意到这些细节的朋友少走弯路

修改过程是一个渐进的过程,希望有过次方面经验的朋友提提建议,也希望没有注意到这些细节的朋友少走弯路
 
简单归纳了下,产生问题的地方有3个方面:

第一个陷阱是计算精度丢失,有这几个原因引起的

1 查看后台代码的发现代码里有有变量或DataTable里的Column是用的float或double类型,这样精度在计算的时候特别计算有乘除法的时候就会有精度转换误差,这个比较容易解决,都改为decimal 类型就可以了;
(我一时也没有找到合适的数据,如果找到数据的话,会加上这段demo):


2 是发现有很多计算的精度丢失是因为使用到中间变量的原因,而在中间变量有保留小数位!
现在修改方式第一种方式是中间变量的精度尽可能长,第二方式是直接用原始变量来计算最终结果

3 有部分计算是js来计算的,因为js里没有decimal类型的变量,这种常常会产生精度丢失,需要最后ToFix()下保留精度,这也使得在页面上显示的结果,需要指定精度。


第二个陷阱是 存储的数值的精度丢失,是因为数字类型未设置正确, 如设置成float ,double 类型都可能有转换精度丢失,decimal类型没有暂时没有在系统中使用过。也有种情况是中间小数位精度不够长,数值如果用于再次计算的时候,也将出现精度不够的情况。我们现在一般都需要设置数字类型为numerice,数量保留6位小数,显示金额保留2位,计算成本保留8位小数

第三个陷阱是 显示数值,精度保留高了,新的矛盾又产生出来了,如果不加以转换直接显示出来,小数位就会有长串的小数位0跟在后面,但显示有些地方,需要显示小数位去除多余的0, 比如在是报表的显示,和页面显示比较拥挤的地方

在报表里最简便的方法是,利用公式字段去除显示多余的0

设置方法如下:

选择需要格式化的字段, 选“自定义样式”,在“四舍五入”里选择0.0001,然后点“十位”后面的按钮,输入以下公式:   
    
  if   Right   (ToText   ({命令_4.PartNum},   6),   6) =   "000000"   then   0   else    
  if   Right   (ToText   ({命令_4.PartNum},   6),   5) =   "00000"   then   1  else   
  if   Right   (ToText   ({命令_4.PartNum},   6),   4) =   "0000"   then   2  else   
  if   Right   (ToText   ({命令_4.PartNum},   6),   3)   =   "000"   then   3  else   
  if   Right   (ToText   ({命令_4.PartNum},   6),   2)   =   "00"   then  4   else   
  if   Right   (ToText   ({命令_4.PartNum},   6),   1)   =   "0"   then   5  else   6

可以实现数字保留6位精度,如果数值有0 的地方自动去除掉多余的0

设置步骤如下图(里面公式稍稍有不一样,但是都可以实现结果):







也可以用修改Sql的方式去除多余的小数位0

SELECT
col,
col_convert = CASE
WHEN CHARINDEX('.', col) = 0
THEN col
WHEN RIGHT(col, PATINDEX('%[^0]%', REVERSE(col))) LIKE '.%'
THEN LEFT(col, LEN(col) - PATINDEX('%[^0]%', REVERSE(col)))
ELSE LEFT(col, LEN(col) - PATINDEX('%[^0]%', REVERSE(col)) + 1)
END
FROM(
SELECT col = '100' UNION ALL
SELECT col = NULL UNION ALL
SELECT col = '.100' UNION ALL
SELECT col = '.100100' UNION ALL
SELECT col = '0.' UNION ALL
SELECT col = '0' UNION ALL
SELECT col = '100.1010' UNION ALL
SELECT col = '100.0000' 
)A

这点解决方法,参考于csdn对这个问题的讨论
http://community.csdn.net/Expert/topic/5766/5766540.xml?temp=.384289


最后总结下其他还可能数值计算出错的地方

1很多数值不正确是因为计算公式或逻辑不明确引起的,有些代码是没有真正理解清楚就开始在开发了;ERP系统的开发对业务 的逻辑理解是非常重要的; 

2 后台显示去除0的方法也有一种是,在程序里直接double.Parse()下就可以去除掉小数位多余的0,再ToString()为字符显示,double类型有个问题,如果数字为连续的5位小数0,就会显示为自动缩变为科学计算法,如果是后台显示的地方,可以先double.parse()下,转为string,再转为decimal类型就会去除掉多余的小数位0了。
demo代码如下:
using System;

namespace Decimal_Round
{
    
/// <summary>
    
/// Class1 的摘要说明。
    
/// </summary>

    class Class1
    
{
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>

        [STAThread]
        
static void Main(string[] args)
        
{
            
decimal decQty = decimal.Parse("0.000001") ;
            Console.WriteLine(
"{0}",decQty);

            
float fQty =  float.Parse( "0.000001" ) ;
            Console.WriteLine(
"{0}",fQty);

            
double dQty =  double.Parse("0.000001") ;
            Console.WriteLine(
"{0}",dQty);

            decQty 
= decimal.Parse("0.100000") ;
            Console.WriteLine(
"{0}",decQty);
 
              if(decQty != 0)
             {
            decQty
=  decimal.Parse(num.ToString("f"+decimals.ToString()).TrimEnd('0')) ;//
            }
               
            Console.WriteLine(
"{0}",decQty);

            Console.ReadLine();
        }

    }

}



3 最先我们把数量保留3位精度,金额保留2位,和成本保留4精度,后来发现实际业务计算的时候是不够的,应该早考虑这方面的业务规则定义和计算,可以使用Excel或类似的工具来早与客户,架构师,程序员,测试员之间沟通; 

4 使用到业务类方法进行计算的地方,需要注意构造接口参数的正确性

5 需要有足够全面的测试用例进行测试,同时也注意容错性测试;

在js,后台程序,数据库,报表都需要一系列的规则来对这些业务规则进行封装,最终的想法是通过数据库或配置文件来实现自动选择配置,我打算稍后的文章将详细总结下这方面的架构和思路

转自:http://www.cnblogs.com/jchdong/archive/2007/09/25/902926.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值