【问题标题】:What is the best way to use float numbers with decimal point in JavaScript?在 JavaScript 中使用带小数点的浮点数的最佳方法是什么?
【发布时间】:2014-07-11 23:16:45
【问题描述】:

我做了调查,看到有一个完整的网站向你解释什么是正确的使用浮点数的方法是:http://floating-point-gui.de/

例如,在 Java 中,我一直使用 BigDecimal 作为浮点数,只是为了确保一切正常工作而不会让我感到困惑。例如:

BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal c = a.add(b); // returns a BigDecimal representing exactly 0.3
                         // instead of this number: 0.30000000000000004 that can 
                         // easily confuse me

但是在 JavaScript 中,我意识到没有内置库之类的东西(至少在我看过的 Math 对象中)

所以到目前为止,我发现的最好的方法是使用一个 JavaScript 库,它正是这样做的!在我的项目中,我使用的是这个:https://github.com/dtrebbien/BigDecimal.js

虽然我认为这是我能找到的最好的图书馆,但图书馆并没有那么重要。我的主要问题是:

  1. 使用像BigDecimal 这样的库是最好的方式吗?还是我错过了什么?我想做基本的计算,比如加法、乘法...等。
  2. 有没有其他建议的方法,例如在 JavaScript 中添加两个浮点数?

例如,假设我想要:0.1 + 0.2。使用BigDecimal 库,我将拥有:

var a = new BigDecimal("0.1");
var b = new BigDecimal("0.2");

console.log(a.add(b).toString()); //returns exactly 0.3

那么有没有其他方法可以在 JavaScript 中添加 0.1 + 0.2 并恰好得到 0.3,而无需实际舍入数字? 作为参考,下面的 JavaScript 示例将不起作用:

var a = 0.1;
var b = 0.2;
console.log(a + b); //This will have as an output: 0.30000000000000004

【问题讨论】:

  • 如果您真的不想使用本机二进制浮点,那么您正在做正确的事情(一些变体)。

标签: javascript floating-point decimal


【解决方案1】:

由于 javascript 中的所有数字都是 64 位的,因此在 javascript 中进行浮点运算的最佳方法通常是直接使用数字。

但是,如果您特别遇到需要比 64 位提供的精度更高的问题,那么您需要做类似的事情。

不过,我敦促您认真考虑是否有这样的用例。

如果您的问题是一些小数点会影响您的比较,那么有一些函数可以专门处理这类事情。我强烈建议您查看 Number.prototype.toFixed(n) 函数并查看 this dicussion on almostEquals,它建议您将 epsilon 用于浮点比较。

【讨论】:

  • 你好@Mithon,谢谢你的回答(你有一个+1 :))。好的,我完全理解您的担忧,我只有一个问题。如果我改用图书馆有什么缺点吗?我认为唯一提到的是存储和计算速度?如果我错了,请纠正我,但我更愿意始终知道我的简单数学知识会得到“正确”的结果,而不是正确的浮点数计算。我只是不想在这么简单的数学计算中考虑一些更复杂的事情。
  • 性能显然是一个问题,因为像这样的 js 库是用 javascript 实现的,而直数算法是用原生 c++(或其他)实现的。然而,通过简单的数学计算,性能将可以忽略不计。但我会问自己:我是想学习使用带有怪癖的 JavaScript,还是会在每一步都与它们作斗争?就我个人而言,我宁愿学习使用它。
  • 很公平!我可能是那种更喜欢简单方法的人:)
  • 几乎所有语言都使用IEEE fp 标准,因此每次使用不同的语言时都可能遇到相同的问题。因此,您可能需要弄清楚如何处理这种数字。虽然 BigDecimal 解决了一些问题,但您仍然需要弄清楚要做什么除法,IEEE FP 或 BigDecimal 都不能精确地表示 1/7。
【解决方案2】:

如果你不依赖高精度,你可以使用 toFixed(n) 方法:

var a = 0.1;
var b = 0.2;
var sum = a + b;
console.log(sum.toFixed(1)); 

您的计算显示第 17 个浮点的精度丢失,这在大多数情况下都不是什么大问题。
如果您想获得正确的输出,我建议您使用 toFixed()。

【讨论】:

    【解决方案3】:

    这里有几点需要考虑。

    很多时候,当人们真正想要使用固定十进制时,他们会使用“浮点数”这个词

    固定小数

    例如,美元是固定小数。

    $12.40
    $0.90
    

    在这种情况下,总是有两个小数点。

    如果您的值符合 JavaScript 整数 (2^53-1) 或 9007199254740991 的范围,那么您可以简单地以美分计算并以这种方式存储所有值。

    1240
    90
    

    浮点小数

    现在,浮点是您处理极端数字范围的地方,小数点实际移动或浮动。

    12.394
    1294856.9458566
    .0000000998984
    49586747435893
    

    在浮点情况下,精确到 53 个有效位(这意味着大约 15 位的精确度)。对于很多事情,这已经足够了。

    大十进制类

    如果您需要超出 JavaScript 原生数字范围的内容,您应该只查看大十进制类。 BigDecimal 类比原生数学慢得多,您必须使用函数式编程而不是使用数学运算符。

    【讨论】:

      【解决方案4】:

      JavaScript 不支持运算符重载,因此没有内置方法可以使用 BigNumbers 进行像“0.1 + 0.2”这样的自然计算。

      你可以做的是使用math.js,它有一个表达式解析器并且支持BigNumbers:

      math.config({
        number: 'bignumber',  // Default type of number: 'number' or 'bignumber'
        precision: 64         // Number of significant digits for BigNumbers
      });
      
      math.eval('0.1 + 0.2'); // returns a BigNumber, 0.3
      

      参见文档:http://mathjs.org/docs/datatypes/bignumbers.html

      【讨论】:

        【解决方案5】:

        您可以预先将操作数转换为整数

        (0.1 * 10 + 0.2 * 10) / 10

        会给出确切的答案,但人们可能不想处理浮点数的东西

        【讨论】:

          猜你喜欢
          • 2021-08-05
          • 1970-01-01
          • 2010-09-12
          • 1970-01-01
          • 2010-09-21
          • 2017-03-28
          相关资源
          最近更新 更多