【问题标题】:How to simulate the result of a Google Sheet multiplication formula in Javascript?如何在 Javascript 中模拟 Google Sheet 乘法公式的结果?
【发布时间】:2017-03-03 09:17:25
【问题描述】:

如果 Google 表格中 f5 单元格的值为 1.1000(格式为小数点后 4 位的数字),f6 的值为 = f5 * 1.073,我如何确保得到相同的乘法结果Javascript 中的那些值,例如:

var original_value = 1.1000;
var derivative_value = original_value * 1.073;

具体来说,我的问题是 - Javascript 乘法 (derivative_value) 的结果是否与 Google 公式 (f6) 的结果相同?如果没有,我怎样才能做到这一点?

上下文/我尝试过的内容

就上下文而言,这个问题是我试图解决的一个更大问题的一部分,我为此设置了这个JSFiddle

JSFiddle 有一个original_value 的输入和一个multiplier 的输入。

它将结果输出到小数点后四位,并在需要的地方添加尾随零(这是结果所需的格式)。

尝试检查我正在编写的 Javascript 代码是否会产生与 Google Sheet 公式相同的结果。

[ JSFiddle 已更新为还将decimal.js 结果记录到控制台以进行比较]

编辑

有人建议使用 decimal.js,但我不确定它会如何应用 - 类似于以下内容?

var original_value = new Decimal(1.1000);

// some different multipliers for testing
var multiplier_a = new Decimal(1.073); 
var multiplier_b = new Decimal(1.1);

// some different results for testing
var derivative_value_a = original_value.times(multiplier_a).toString();
var derivative_value_b = original_value.times(multiplier_b).toString();

console.log(derivative_value_a);  //  1.1803
console.log(derivative_value_b);  //  1.21

这比纯 Javascript original_value * multiplier 更准确吗?更重要的是,对于这个问题,它是否总是模拟 Google Sheet 公式产生的相同结果?

【问题讨论】:

  • Javascript 可能是一种危险的语言,因为它的舍入错误而进行大量计算。如果您想安全起见,可以使用以下数学库:github.com/MikeMcl/decimal.js?
  • 据我了解,我仍然需要知道 Google Sheet 的乘法“用途”是什么,以便我可以复制相同的行为。我已经在问题中添加了使用 decimal.js 的尝试。我还更新了原始的 JSFIddle 以将 decimal.js 结果记录到控制台以进行比较。
  • @EmilS.Jørgensen 据我所知,JS 和 Google 表格都使用相同的 float64 数字格式 - 你有任何关于 Google 表格的规范,它们使用不同的东西吗??
  • @user1063287 请不要忘记= f5 * 1.073= round(f5, 4) * 1.073 不同!!!
  • @EmilS.Jørgensen :除了灾难性的抵消之外,您还需要对大约 1e11 次运算进行非常大的计算,才能通过累积的浮点噪声影响第 5 位有效数字。因此,尽管您必须注意,但我不会将其描述为“危险”。当然,在所有与合同相关的事项中,都应使用与合同中概述或暗示的条件和程序完全一致的方法。

标签: javascript math google-sheets decimal multiplication


【解决方案1】:

JavaScript 使用所谓的双精度浮点格式(64 位)-https://tc39.github.io/ecma262/#sec-terms-and-definitions-number-value

Google Sheets好像用的格式一样,可以通过=f6*1E13 - round(f6*1E13)测试看看f6不是STORED为固定数字格式,只是FORMATTED

请参阅Number.toFixed 如何在 Javascript 中格式化数字

生成一些测试数据:

[...Array(10)].forEach(() => {
  const f5 = 1.1
  const x = Math.random() / 100
  const f6 = f5 * x
  console.log(x, f6.toFixed(4))
})

并在 Google 表格中进行比较:

https://docs.google.com/spreadsheets/d/1jKBwzM41nwIEyatLUHEUwteK8ImJg334hzJ8nKkUZ5M/view

=> 所有四舍五入的数字都相等。

P.S.:您需要复制控制台输出,粘贴到工作表中,使用菜单项Data > Split text into columns... > Space,然后在第 3 列乘以 1.1,最后格式化所有数字

【讨论】:

    【解决方案2】:

    重新访问后,我更新了jsFiddle

    我认为令人满意的解决方案的主要组成部分是:

    • original_valuemultiplier 都转换为decimal.js 对象。

    • 使用decimal.js times 方法进行乘法运算。

    • 使用decimal.js toDecimalPlaces 方法进行舍入。

    • 使用参数值(4,7)定义4位小数,ROUND_HALF_CEIL四舍五入,相当于Math.round (reference)

    例如:

    var my_decimal_js_value = new Decimal(original_value).times(new Decimal(multiplier)).toDecimalPlaces(4, 7);
    

    为了在结果中添加任何必要的尾随零,我使用:

    function trailingZeros(my_decimal_js_value) {
      var result = my_decimal_js_value; 
      // add zeros if required:
      var split_result = result.toString().split(".");
      // if there are decimals present
      if (split_result[1] != undefined) {
        // declare trailing_zeros;
        var trailing_zeros;
        // get the amount of decimal numbers
        decimals_present = split_result[1].length;
        // if one decimal number, add three trailing zeros
        if (decimals_present === 1) {
          trailing_zeros = "000";
          result += trailing_zeros;
        }
        // if two decimal numbers, add two trailing zeros
        else if (decimals_present === 2) {
          trailing_zeros = "00";
          result += trailing_zeros;
        }
        // if three decimal numbers, add one trailing zero
        else if (decimals_present === 3) {
          trailing_zeros = "0";
          result += trailing_zeros;
        }
        // if four decimal numbers, just convert result to string
        else if (decimals_present === 4) {
          result = result.toString();
        }
      }
      // if there are no decimals present, add a decimal place and four zeros
      else if (split_result[1] === undefined) {
        trailing_zeros = ".0000";
        result += trailing_zeros;
      }
    
      return result;
    }
    

    我仍然不确定这是否模仿了 Google 表格乘法公式,但是根据帖子,使用 decimal.js 或另一个专用十进制库似乎是优于纯 JavaScript 的首选方法(以避免可能的舍入错误)比如这些:

    http://www.jacklmoore.com/notes/rounding-in-javascript

    Is floating point math broken?

    https://spin.atomicobject.com/2016/01/04/javascript-math-precision-decimals

    【讨论】:

      猜你喜欢
      • 2019-09-27
      • 2021-10-10
      • 2021-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-17
      相关资源
      最近更新 更多