小数位数的处理(JS前端,C#后台,SQL处理,报表处理)

最近零零散散的做了一些小数位数相关的内容,这里稍微总结一下。前段时间,我们的系统里面需要做一个根据小数位数设置来处理数据的需求,在我开发之前,系统里面已经有过处理。系统里面有金额和积分小数位数处理设置,这里以金额设置为例,之前的做法是这样的:

  1. 系统里面配置默认参数:ConsumeMoneyIndexChoice,默认值为:“保留小数”,就是保留两位小数的意思,有一位的话就显示一位。然后用户在参数设置里面也可以下拉选择来进行修改,之后便在消费收银(其他业务就不一一列举了,总之是有接近十个业务之多)界面进行控制。系统的前台页面用的Handler与js,在js加载时就在相对应的Handler里面请求参数,包括其他好几个参数(因为系统比较灵活,确实参数设置较多了些)一起请求到前台,然后在数值变化数值计算设计到数值变化的地方用if else 语句来处理前台数值。可能一个页面就有好几个地方十分雷同的出现,if else,处理OK了到了结账页面,结账页面又会到结账页面对应的Handler里面请求参数,包括小数位数设置的参数,然后也是好几个if else,当然,据我阅读代码,if else还有没写全的情况,可能后期参数个数有 变动,地方太多没改过来,也有一些地方干脆也没有进行if else判断了,估计是判断累了,呵呵。总之,是有点凌乱了。。结账之后到了后台,后台写了一个公共方法,对消费单据的数据按照参数再进行一次判断和处理。

  2. 我阅读完之后头就有点大,然而任务在这,没办法,只好硬着头皮改,因为新的需求需要新增几种小数位数的处理,我也一个页面一个页面的在原来的基础上进行添加新的逻辑,完善之前不完善的逻辑,修改起来进度缓慢,十分耗力,关键是修改后试运行(就是丢到网站上运行,并不能用编辑器运行的)也会经常性的有错误出现。。

  3. 我于是停下来,想办法。。。。

  4. 经过了一些尝试,突然想起来前两天看了会儿JS的教学视频,将的三个高级函数,有一个惰性函数,有一种模糊的感觉,似乎可以用上。。。于是再百度了下,尝试了下,在程序刚开始加载的时候去请求一次参数,并保存下来(这里的保存下来的意思,是系统在调用前台的方法时时按一个方式处理),看惰性函数吧

Init = (function () {
    Ext.Ajax.request({
        url: "Module/CheckOut/CheckOutHandler.ashx?action=GetMoneyPointIndexChoice",
        method: "POST",
        success: function (response, ops) {
            var o = Ext.decode(response.responseText);
            console.log("同步请求金额小数位数" + "#" + o.ConsumeMoneyIndexChoice);
            var ConsumeMoneyIndexChoice = o.ConsumeMoneyIndexChoice; /*折后金额小数点设置*/
            switch (ConsumeMoneyIndexChoice) {
                case "type1":  //四舍五入到元
                    return Number.prototype.toFixedTotalMoney = function () {
                        return Number(Math.round(this));
                    }
                    break;
                case "type2":  //四舍五入到角
                    return Number.prototype.toFixedTotalMoney = function () {
                        return Number(parseFloat(this).toFixed(1));
                    }
                    break;
                case "type3": //四舍五入到分
                    return Number.prototype.toFixedTotalMoney = function () {
                        return Number(parseFloat(this).toFixed(2));
                    }
                    break;
                case "type4": //抹零到元
                    return Number.prototype.toFixedTotalMoney = function () {
                        return Number(parseInt(this));
                    }
                    break;
                case "type5":  //抹零到角
                    return Number.prototype.toFixedTotalMoney = function () {
                        return Number(parseInt(this * 10) / 10);
                    }
                    break;
                default: //四舍五入到分
                    return Number.prototype.toFixedTotalMoney = function () {
                        return Number(parseFloat(this).toFixed(2));
                    }
                    break;
            }
            return newNumber;
        }
    });

})();

  这个js在页面刚开始加载时加载,请求到具体的值后再确定toFixedTotalMoney()的处理方式。这样,不再需要在多个Handler里面多次请求,不需要在前台if else,更重要的是

,相对来说,更加的符合开闭原则,利于后期维护。

  不过前台有一个问题,在四舍五入的时候(就是调用toFixed())有的时候出现误差,如:

  

 

   更准确的方式是自己写,例如下面的 四舍五入,抹零,相对的比较(有时候前台相加后会出现微小误差,可以绝对值比较相差<1e-6这样判断是否相等)

  

//四舍五入算法
//v,要处理的数据,e,保留的小数位数,
//e为正,保留小数点后面的位数,e为负,保留小数点前面的数
//MoneyPointRound1(3.555,1)=3.6
//MoneyPointRound1(3.555,-1)=0
//MoneyPointRound1(6.555, -1)=10
var MoneyPointRound = function (v, e) {
    var t = 1;
    for (; e > 0; t *= 10, e--);
    for (; e < 0; t /= 10, e++);
    debugger
    return Math.round(v * t) / t;
}

//抹去多余位数算法
//MoneyPointIgnore(3.555,1)=3.5
//MoneyPointIgnore(3.555,2)=3.55
//MoneyPointIgnore(3.555, -1) = 0
//MoneyPointIgnore(6.555, -1)=0
var MoneyPointIgnore = function (v, e) {
    var t = 1;
    for (; e > 0; t *= 10, e--);
    for (; e < 0; t /= 10, e++);
    debugger
    return parseInt(v * t) / t;
}


///用于前台判断两个值是否相等
//处理parseFloat(110.12+149.72)=259.84000000000003!=259.84的情况
//统一判断入口,后期可以修改判断精度
//也可以相减后的绝对值误差来做 abs(q-b)<1e-6
//Num1,Num2为数值
var EqualNumber = function (num1, num2) {
    if (Number(parseFloat(num1).toFixed(6)) == Number(parseFloat(num2).toFixed(6))) {
        return true;
    }
    return false;
}


//数量和折后金额保持2位小数
Number.prototype.toFixed2Decimal = function () {
    return Number(parseFloat(this).toFixed(2));
}



//数量和折后金额保持4位小数
Number.prototype.toFixed4Decimal = function () {
    return Number(parseFloat(this).toFixed(4));
}

  下面的toFixed4Decimal看起来多此一举,但有时候未必,例如我们的结账页面,有时候全部保持2位,有时候3位,页面里面都是toFixed(2)改起来较多,有时候可以考虑后期可能的修改并为此做好准备。易扩展易修改。

  5. 后台的方法时这样的,使用方法:price.toMath("保留两位小数");

  

public static class MathHelper
    {
        /// <summary>
        /// 数值小数处理
        /// </summary>
        /// <param name="value"></param>
        /// <param name="flag">消费积分小数位类型(参考系统参数)</param>
        /// <returns></returns>
        public static decimal toMath(this decimal value, string flag)
        {
            decimal globalValue;
            switch (flag.Trim())
            {
                case "四舍五入到元":
                    globalValue = Math.Round(value, MidpointRounding.AwayFromZero);
                    break;
                case "四舍五入到角":
                    globalValue = Convert.ToDecimal(value.ToString("#0.0"));
                    break;
                case "四舍五入到分":
                    globalValue = Convert.ToDecimal(value.ToString("#0.00"));
                    break;
                case "抹零到元":
                    globalValue = Math.Floor(value);
                    break;
                case "抹零到角":
                    globalValue = Math.Floor(value * 10) / 10;
                    break;
                case "保留两位小数":
                    globalValue = Convert.ToDecimal(value.ToString("#0.00"));
                    break;
                case "抹零取整":
                    globalValue = Math.Floor(value);
                    break;
                case "四舍五入取整":
                    globalValue = Math.Round(value, MidpointRounding.AwayFromZero);
                    break;
                case "保留四位小数":
                    globalValue = Convert.ToDecimal(value.ToString("0.####"));
                    break;
                default:
                    globalValue = Convert.ToDecimal(value.ToString("#0.00"));
                    break;
            }
            return globalValue;
        }



    }

    但是有这样一个问题,系统里面之前商品数量都是保留两位小数的,现在为了某些贵重金属行业需求,支持了4位小数,但是大量的客户都是两位的,如图:

    

    这时候一种较好的处理方式是,默认两位,但是有三位则显示三位,有四位显示四位,大于四位的显示四位。后来可以这样写一个函数:

    

/// <summary>
        /// 保留4位小数,末位为零的去掉,但是至少保留两位
        /// </summary>
        /// <param name="Number"></param>
        /// <returns></returns>
        public decimal GetFixed2To4Decimal(decimal number)
        {
            decimal index2 = Convert.ToDecimal(number.ToString("#0.00"));
            decimal index4 = Convert.ToDecimal(number.ToString("0.####"));
            if (index2 == index4)
            {
                return index2;
            }
            else
            {
                return index4;
            }
        }

      number.ToString("0.####") 是保留4位小数,末位的0去掉。

    

    6. 我们还有销售报表和库存报表,也需要处理,也是默认四位,末位去零,至少两位。报表是用的Report Service,数据源是SQL,查了下, Money类型刚好符合,

select Convert(MONEY,@quantity) 就是我需要的,例如2.3 SQL 查出来是2.30,但是用报表直接用的查询字段的时候,就都还原为4位了,大量的用户其实都是两位小数,

多余的0的显示看起来冗余杂乱不清晰,想着写SQL函数,可是函数只能有一个返回类型,返回Decimal,则1.23会返回1,Decimal(18,2)?decimal(18,3)? 显然都不行,

将Money类型转成字符串?不幸的是,SELECT CAST(CONVERT(MONEY,1.2324) AS NVARCHAR(MAX))的结果是1.23,怪了,可就是怪了,一时无解。

 

    7.在网上 搜索 Report Service 函数,搜到一点有用的信息:http://bbs.csdn.net/topics/390716722,

    里面说的Format函数,好像有用,然后再写上IFF条件判断,似乎可以。。最终终于尝试OK,

    

          
            CAST(Discount AS DECIMAL(18,4)) AS Discount,
            
        
            NumberFixed    
            =IIf( CDec(Format(Fields!Number.Value,"0.####"))=CDec(Format(Fields!Number.Value,"#0.00")),Format(Fields!Number.Value,"#0.00"),Format(Fields!Number.Value,"0.####"))
            
            TotalMoneyFixed
            =IIf( CDec(Format(Fields!TotalMoney.Value,"0.####"))=CDec(Format(Fields!TotalMoney.Value,"#0.00")),Format(Fields!TotalMoney.Value,"#0.00"),Format(Fields!TotalMoney.Value,"0.####"))
        

    SQL里面decimal(18,4),然后展示的时候进行判断,当然由于判断较为复杂,不适合那些经过了复杂的计算后的值的处理。我就讲报表主表,及上方的汇总信息显示为4位,在 数量,折后金额等列用上上面的判断。

           

    OK  写到这里吧

    

    

    

转载于:https://www.cnblogs.com/denghuachengle/p/5342332.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值