请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册

QQ登录

只需一步,快速开始

Winny
超级版主   /  发表于:2025-4-8 17:16  /   查看:44  /  回复:0
需求背景:“”四舍六入五成双“”这种修约方式被称为“银行家舍入法”(Banker's Rounding),因为它倾向于产生更少的舍入偏差,并且在中国国家标准《GB/T 8170-2008数值修约规则与极限数值的表示和判定》中有明确规定。这种方法可以应用于各种需要数值修约的场合,如金融计算、统计分析等,以尽量减少由于舍入引起的系统性误差。四舍六入五成双在线下Excel中,部分用户可能会考虑用自定义函数来实现。业务迁移到线上Excel时,由于浏览器无法识别宏语言,因此需要使用浏览器可以识别的js语言来实现这些函数需求。

本文结合spreadjs自定义函数,在全局注册了名为FDA的函数,来完成SpreadJS上的四舍六入。实现之前,我们先来了解一下算法原理。
    舍:如果要舍弃的数字小于5(即0、1、2、3或4),则直接舍去这些数字,保留的数字不变。
    六入:如果要舍弃的数字大于5(即6、7、8或9),则进位,即将前一位数字加1。
   五成双:如果要舍弃的数字是5,则根据具体情况决定是否进位;
        如果5后面的数字不全是0,则无论5前面的数字是奇数还是偶数,都进位。
        如果5后面没有数字或者后面的数字全部为0,则看5前面的数字:
               如果5前面的数字是偶数,则舍去5,不进位。
               如果5前面的数字是奇数,则进位,即将前一位数字加1


实现代码:
  1. let FdaFunction = function () { };

  2.             FdaFunction.prototype = new GC.Spread.CalcEngine.Functions.Function(
  3.                 "FDA", 1, 2, {
  4.                 description: "四舍六入五成双",    //单元格输入函数时对应的提示信息
  5.                 //parameters是对函数每个参数的描述,对应的是一个Object的数组
  6.                 parameters: [
  7.                     {
  8.                         name: 'num',         //参数的名字,输入函数中的参数时会显示
  9.                         repeatable: false,   //参数是否可以重复,一般只有最后一个参数可重复
  10.                         optional: false      //参数是否可选
  11.                     },{
  12.                         name: 'decimals',         //参数的名字,输入函数中的参数时会显示
  13.                         repeatable: false,   //参数是否可以重复,一般只有最后一个参数可重复
  14.                         optional: false      //参数是否可选
  15.                     }
  16.                 ]
  17.             }
  18.             )


  19.             FdaFunction.prototype.evaluate = function (value, decimalPlaces) {
  20.                 if (typeof value !== 'number' || isNaN(value)) {
  21.                     throw new Error('The first argument must be a valid number.');
  22.                 }
  23.                 if (!Number.isInteger(decimalPlaces) || decimalPlaces < 0) {
  24.                     decimalPlaces = 2
  25.                 }
  26.             
  27.                 // 计算修约所需的乘数因子
  28.                 const factor = Math.pow(10, decimalPlaces);
  29.             
  30.                 // 将数值放大指定倍数并提取整数部分和小数部分
  31.                 const scaledValue = value * factor;
  32.                 const integerPart = Math.floor(scaledValue);
  33.                 const fractionalPart = scaledValue - integerPart;
  34.             
  35.                 let roundedValue;
  36.             
  37.                 if (fractionalPart < 0.5) {
  38.                     // 四舍
  39.                     roundedValue = integerPart;
  40.                 } else if (fractionalPart > 0.5) {
  41.                     // 六入
  42.                     roundedValue = integerPart + 1;
  43.                 } else {
  44.                     // 五成双
  45.                     if (integerPart % 2 === 0) {
  46.                         // 偶数时舍去
  47.                         roundedValue = integerPart;
  48.                     } else {
  49.                         // 奇数时进位
  50.                         roundedValue = integerPart + 1;
  51.                     }
  52.                 }
  53.             
  54.                 // 将结果缩小回原比例
  55.                 return roundedValue / factor;
  56.             };
  57. GC.Spread.CalcEngine.Functions.defineGlobalCustomFunction("FDA", new FdaFunction());
复制代码
注册完成后,就可以正常使用了:
函数修约.gif864648437.png

详细代码参考附件文件。






customeFormula.html

7.11 KB, 下载次数: 1

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部