• 小数位数的处理(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  写到这里吧

        

        

        

  • 相关阅读:
    linux_ssh用户枚举猜测
    Nginx 主配置文件参数详解
    OSI七层模型
    linux-Python升级安装
    qt多线程
    python 对串口的操作
    keil 下模拟u-boot的cmd功能
    <转载>CentOS 6.3下Samba服务器的安装与配置
    Magento开发完整指南
    飞书信(Facebook Messenger)是什么?
  • 原文地址:https://www.cnblogs.com/denghuachengle/p/5342332.html
Copyright © 2020-2023  润新知