【问题标题】:JavaScript Math.floor: how guarantee number will round down?JavaScript Math.floor:保证数字如何四舍五入?
【发布时间】:2013-05-27 20:03:11
【问题描述】:

我想规范化一个数组,使每个值都在 [0-1) 中。即最大值永远不会是 1,但最小值可以是 0。这与返回相同范围内的数字的随机函数没有什么不同。

看着这个,我发现 .99999999999999999===1 是真的!同上 (1-Number.MIN_VALUE) === 1 但是 Math.ceil(Number.MIN_VALUE) 应该是 1。 其他一些:Math.floor(.999999999999) 为 0 而 Math.floor(.99999999999999999) 为 1

好的,所以 JS 中存在舍入问题。

有什么方法可以将一组数字归一化,使其位于 [0,1) 范围内?

【问题讨论】:

  • “标准化”是什么意思? 1的标准化值应该是多少? 1.5? 2?
  • 也许:number/Number.MAX_VALUE
  • 不存在“舍入问题”,但存在精度限制。 Javascript numbers 是双精度 64 位二进制格式 IEEE 754 值,因此在 15 位之后,您开始失去准确性,因为没有足够的位来准确表示所有数字。
  • 您的范围的问题在于,对于您的最大数量应该转换的值是一种随意性。到 0.999?到 0.9999?你到底想达到什么目的?也许有更好的解决方案来解决您的问题。
  • 谢谢大家......非常好的答案。稍后我将提交另一个更精确的问题。

标签: javascript math floor


【解决方案1】:

检查 JavaScript 对每个表达式执行的步骤可能会有所帮助。

.99999999999999999===1:

  1. 源文本.99999999999999999 被转换为数字。最接近的数字是 1,这就是结果。 (下一个最接近的数字是 0.99999999999999988897769753748434595763683319091796875,即 1–2–53。)
  2. 然后将 1 与 1 进行比较,结果为真。

(1-Number.MIN_VALUE) === 1:

  1. Number.MIN_VALUE 是 2–1074,大约是 5e–304。
  2. 1–2–1074 非常接近一。确切的值不能表示为数字,因此使用最接近的值。同样,最接近的值是 1。
  3. 然后将 1 与 1 进行比较,结果为真。

Math.ceil(Number.MIN_VALUE):

  1. Number.MIN_VALUE 是 2–1074,大约是 5e–304。
  2. 该值的上限函数为 1。

Math.floor(.999999999999):

  1. 源文本.999999999999 被转换为数字。最接近的数字是 0.99999999999900002212172012150404043495655059814453125,所以这就是结果。
  2. 该值的底函数为 0。

Math.floor(.99999999999999999):

  1. 源文本.99999999999999999 被转换为数字。最接近的数字是 1,这就是结果。
  2. 1 的底函数是 1。

这里最多只有两件事令人惊讶。一是将源文本中的数字转换为内部数字值。但这应该不足为奇。当然,文本必须转换为数字的内部表示,而 Number 类型不能完美地存储所有无限多的数字。所以它必须圆。当然数字非常接近 1 轮到 1。

另一个可能令人惊讶的事情是1-Number.MIN_VALUE 是 1。但这实际上是同一个问题:确切的结果无法表示,但它非常接近 1,因此使用 1。

Math.floor 函数工作正常。它从不引入任何错误,并且您不必做任何事情来保证它会向下舍入。总是这样。

但是,由于您想对数字进行规范化,因此您似乎可能会在某个时候将数字除以。在进行除法时,可能会出现舍入问题,因为很多除法的结果不能完全表示,所以必须进行舍入。

但是,这是一个单独的问题,您在这个问题中没有提供足够的信息来解决您计划进行的具体计算。你应该为它打开一个单独的问题。

【讨论】:

    【解决方案2】:

    Javascript 会将 0.999999999999999994 和 1 之间的任何数字视为 1,因此只需减去 .000000000000000006。

    当然,这并不像听起来那么容易,因为 .000000000000000006 在 Javascript 中被评估为 0,所以您可以执行以下操作:

    function trueFloor(x)
    {
        x = x * 100;
        if(x > .0000000000000006)
            x = x - .0000000000000006;
        x = Math.floor(x/100);
        return x;
    }
    

    编辑: 或者至少你认为你可以。显然 JS 在将 .99999999999999999 传递给函数之前将其转换为 1,因此您必须尝试以下操作:

    trueFloor("0.99999999999999999")
    
    function trueFloor(str)
    {
        x=str.substring(0,9) + 0;
        return Math.floor(x); //=> 0
    }
    

    不知道您为什么需要这种级别的精度,但从理论上讲,我想它是可行的。你可以看到一个工作小提琴here

    只要您将极其精确的 float 转换为 string,这可能是您最好的选择。

    【讨论】:

    • 这是标准吗?或者它是否因浏览器/JS 实现而异?谢谢!
    • 它是 Chrome、IE 和 FF 实现的标准。如果您好奇,请随意在其他环境中测试小提琴。
    • 注意:要查看实际的“标准”,请查看定义 number 变量类型的 ECMAScript Standards Page - Section 8.5。精度通常受公式S x M x 2^E 限制,其中 E 大于或等于 -1074
    • 杰森:谢谢!非常有趣,而且有点,呃,奇怪!但绝对是好书。还显示 Math.pow 略有偏差。我计算了一些示例数字。
    【解决方案3】:

    请理解一件事:这...

    .999999999999999999
    

    ...只是一个数字文字。就像

    .999999999999999998
    .999999999999999997
    .999999999999999996
    ...
    

    ...你看到了模式。

    JavaScript 如何处理这些文字完全是另一回事。是的,这种处理受到可用于存储 Number value 的位数的限制。

    根据定义,可能的浮点字面量的数量是无限的——无论为它们设置的范围有多小。比如上图:numbers very close to 1你能表达多少个?是的,它是无限的:只需将9 附加到该行即可。

    但是每个 Number 值的容器是非常有限的:它有 64 位。这意味着,它可以存储 2^64 个不同的值(Infinite-InfiniteNaN)——仅此而已。

    你还是想使用这样的文字?使用字符串来存储它们,而不是数字 - 以及一些 BigMath JS 库 (take your pick) 来处理这些值 - 再次作为字符串。

    但从您的问题来看,您似乎不是,因为您谈到了Numbers 的数组 - 数字 values,就是这样。而且绝对不可能有.999999999999999999 存储在那里,因为JavaScript 中没有这样的数字

    【讨论】:

    • 你成功了 - 有时人们会将实际对象与他们的表示混淆。
    猜你喜欢
    • 2015-11-12
    • 2023-01-12
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 1970-01-01
    • 2017-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多