Derrick.Jiao 发表于 2022-2-21 12:15:46

利用自定义公式实现“四舍六入五成双”规则

    通常,我们都知道在保留小数位的时候,常常会用到四舍五入。但是在一些更加专业的场景中,四舍五入往往不够精确。因此,有了“四舍六入五成双”这样一种规则。四舍六入五成双是一种比较精确比较科学的计数保留法,是一种数字修约规则。下图是来自百度百科的解释,感兴趣或者还不太熟悉的朋友可以先搜索看一下。

那有了这样一个背景之后呢,我们想要实现这样一个规则,在excel中需要通过一系列的内置公式进行复合。对于普通用户来说无论是理解还是使用上,都比较繁琐且复杂。那么我们就可以通过自定义函数来完成这样一个需求,这样用户只需要记住或者使用我们自定义的函数名既可使用具有这样一个规则的函数。

关于如何实现自定义函数可以看一下学习指南
https://demo.grapecity.com.cn/spreadjs/SpreadJSTutorial/features/calculation/custom-functions/purejs

我们首先需要定义函数的名称,以及里面的参数数目。因为我们想要实现的是,传两参数,1是需要被约修的数值,2是保留小数点后面的位数,根据值和位数进行约修。
var FdaFunction = function() {
             this.name = "FDA";
             this.minArgs = 1;
             this.maxArgs = 2;
         };

接下来就是为了方便用户理解和使用,我们需要对这个自定义函数添加一些描述
FdaFunction.prototype.description = function() {
             return {
               description: "对value进行四舍六入五留双修约,保留小数点后指定位数",
               parameters: [{
                     name: "value",
                     repeatable: false,
                     optional: false
               }, {
                     name: "places",
                     repeatable: false,
                     optional: false
               }]
             }
         }最后关键步骤,也就是函数的逻辑运行都放在evaluate中,在里面,我们会对传入的值做一些判断,并且会利用正则表达式做一些匹配。要实现“五成双”,那么我们还要对需要约修的最后一个位值做判断,来决定是否进位。具体可以参考附件完整的demo。
         FdaFunction.prototype.evaluate = function(context, num, places) {

            if (!isNaN(parseInt(num)) && !isNaN(parseInt(places))) {
                console.log("evaluate")
               num = numGeneral(num);
                if (!isNumber(num)) {
                  return num;
                }
                var d = places || 0;
                var m = Math.pow(10, d);
                var n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors
                var i = Math.floor(n),
                  f = n - i;
                var e = 1e-8; // Allow for rounding errors in f
                var r = f > 0.5 - e && f < 0.5 + e ? (i % 2 == 0 ? i : i + 1) : Math.round(n);
                var result = d ? r / m : r;

                if (places > 0) {
                  var s_x = result.toString();
                  var pos_decimal = s_x.indexOf(".");
                  if (pos_decimal < 0) {
                        pos_decimal = s_x.length;
                        s_x += ".";
                  }
                  while (s_x.length <= pos_decimal + places) {
                        s_x += "0";
                  }
                  return s_x;
                } else {
                  return result;
                }
            }else{
               return "#VALUE!";
            }
         
         }


下载附件即可查看完整demo。

许少雄 发表于 2023-12-7 16:46:28

FDA(B3,A3)   这里面的B3如果是这个单元格本身呢 ,必须要辅助单元格吗

Richard.Huang 发表于 2023-12-7 17:22:31

许少雄 发表于 2023-12-7 16:46
FDA(B3,A3)   这里面的B3如果是这个单元格本身呢 ,必须要辅助单元格吗

如果是B3这个单元格本身,那么就是循环引用问题了,Excel不能正确计算一个包含循环引用的公式,因为它试图用单元格自己未确定的值去更新它自己。请问您的具体需求是什么,为什么会想要将公式参数中传递单元格自己的引用呢

Ellia.Duan 发表于 2023-12-7 17:24:31

您好,上述示例中 ,第一个参数是要进行计算的单元格引用,第二个参数是保留的小数点位数。
您提到的B3是单元格本身的话,您是想对B3单元格直接进行”四舍六入“计算吗?。如果是的话,那不需要用到FDA公式,直接对B3单元格进行处理,执行上述中evaluate的关键代码。

许少雄 发表于 2023-12-8 10:46:24

Ellia.Duan 发表于 2023-12-7 17:24
您好,上述示例中 ,第一个参数是要进行计算的单元格引用,第二个参数是保留的小数点位数。
您提到的B3是 ...

html中的numGeneral 函数几个for循环是不是又问题啊

Ellia.Duan 发表于 2023-12-8 11:11:25

本帖最后由 Ellia.Duan 于 2023-12-8 11:12 编辑

您好,不太明白您的意思,您可以描述下您在使用过程中遇到了什么问题。同时建议您发新帖描述您的问题。
页: [1]
查看完整版本: 利用自定义公式实现“四舍六入五成双”规则