将float转换为decimal时值错误的技术问题求助
问题根源与解决方案
首先得明确:你的问题不是转换方式的问题,而是从一开始使用float类型存储大整数时就已经丢失精度了。
为什么会出错?
float是单精度浮点数,它的有效数字只有7-8位。你要处理的数值10071072872302有13位有效数字,当你把它赋值给float变量时,CLR会自动把它近似到float能表示的最近值——这就是为什么你后续不管转double还是decimal,结果都是错的,因为原始的float已经不是你想要的那个数了。
解决思路:全程用Decimal计算,避免浮点类型
既然你的最终目标是匹配数据库的decimal类型,那就要在NCalc计算的全过程中都使用decimal类型,不要转成double或者float,这样才能保留完整精度。
修改你的CalculateString方法如下:
private static object CalculateString(string formula) { var expression = new Expression(formula); expression.EvaluateFunction += (name, args) => { switch (name) { case "ABS": // 用Decimal.Abs代替Math.Abs,参数转成decimal args.Result = Decimal.Abs(Convert.ToDecimal(args.Parameters[0].Evaluate())); break; case "MAX": // 用Decimal.Max代替Math.Max,参数都转成decimal var param1 = Convert.ToDecimal(args.Parameters[0].Evaluate()); var param2 = Convert.ToDecimal(args.Parameters[1].Evaluate()); args.Result = Decimal.Max(param1, param2); break; } }; // 强制NCalc用decimal计算,避免默认转成float/double expression.Options = ExpressionOptions.DecimalAsDefault; var value = expression.Evaluate(); return value; }
关键修改点:
- 把所有
Convert.ToDouble换成Convert.ToDecimal,确保参数都是decimal类型 - 替换
Math.Abs/Math.Max为Decimal.Abs/Decimal.Max——因为Math类的方法是针对double的,会引入精度损失 - 设置
expression.Options = ExpressionOptions.DecimalAsDefault,让NCalc在解析表达式时默认用decimal类型计算,而不是自动转成浮点类型
验证效果
当你计算表达式"10071072872302"时,expression.Evaluate()会直接返回decimal类型的10071072872302,不需要再转换,完全匹配数据库的类型要求。
对于你提供的复杂表达式:
10071072872302+872302+MAX((ABS(-72872302)-ABS(2302)),0)
计算结果也会是精确的decimal值,不会有任何精度丢失。
内容的提问来源于stack exchange,提问作者Minh Giang
相关产品推荐
相关产品推荐

