我听说您可以将数字右移 0.5,而不是使用 Math.floor()。我决定检查它的限制以确保它是合适的替代品,因此我检查了以下值并在 Google Chrome 中得到了以下结果:


2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2;  // 15 9s
2.9999999999999999 >> .5 == 3;  // 16 9s

经过一番摆弄,我发现在 Chrome 和 Firefox 中,右移 0.5 时,得到 2 的最高可能值是 2.9999999999999997779553950749686919152736663818359374999999(重复 9)。在 IE 中该数字为 2.9999999999999997779 。

我的问题是:数字.0000000000000007779553950749686919152736663818359374有什么意义?这是一个非常奇怪的数字,这确实激起了我的好奇心。

我一直在试图寻找答案或至少某种模式,但我认为我的问题在于我真的不理解按位运算。我原则上理解这个想法,但是将位序列移动 0.5 对我来说根本没有任何意义。任何帮助表示赞赏。

根据记录,奇怪的数字序列随 2^x 变化。以下数字中仍能正确截断的最大可能值:

for 0: 0.9999999999999999444888487687421729788184165954589843749¯
for 1: 1.9999999999999999888977697537484345957636833190917968749¯
for 2-3: x+.99999999999999977795539507496869191527366638183593749¯
for 4-7: x+.9999999999999995559107901499373838305473327636718749¯
for 8-15: x+.999999999999999111821580299874767661094665527343749¯
...and so forth
有帮助吗?

解决方案

实际上,您只是在第一个操作数上执行floor(),而不进行任何浮点运算。由于左移和右移位操作仅对整数操作数有意义,因此JavaScript引擎首先将两个操作数转换为整数:

2.999999 >> 0.5

变为:

Math.floor(2.999999) >> Math.floor(0.5)

反过来又是:

2 >> 0

向0位移位意味着“不做换档”。因此,您最终会得到第一个操作数,只需截断为整数。

SpiderMonkey源代码包含:

switch (op) {
  case JSOP_LSH:
  case JSOP_RSH:
    if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
        return JS_FALSE;
    if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
        return JS_FALSE;
    j &= 31;
    d = (op == JSOP_LSH) ? i << j : i >> j;
    break;

你看到了“四舍五入”由于JavaScript引擎无法处理超过一定精度的十进制数字,因此您的数字最终会向上舍入到下一个整数。在浏览器中试试这个:

alert(2.999999999999999);

您将获得2.999999999999999。现在尝试再添加一个9:

alert(2.9999999999999999);

你会得到一个3。

其他提示

这可能是我见过的最糟糕的想法。它存在的唯一可能的目的是赢得混淆代码竞赛。您发布的长数字没有任何意义——它们是底层浮点实现的产物,通过天知道有多少个中间层进行过滤。按小数字节进行位移是疯狂的,我很惊讶它没有引发异常——但这就是 Javascript,总是愿意重新定义“疯狂”。

如果我是你,我会避免使用这个“功能”。它的唯一价值是作为异常错误情况的可能根本原因。使用 Math.floor() 并对下一个维护代码的程序员表示同情。


证实了我在阅读问题时的一些怀疑:

  • 右移任意小数 x 任意小数 y 会简单地截断 x, ,给出相同的结果 Math.floor() 同时彻底迷惑读者。
  • 2.999999999999999777955395074968691915...只是可以区分“3”的最大数字。尝试单独评估它——如果你向其中添加任何东西,它的评估结果将是 3。这是浏览器和本地系统浮点实现的产物。

如果你想更深入,请阅读“每个计算机科学家应该知道的关于浮点算术的内容”: http://docs.sun.com/source/806-3568/ncg_goldberg.html

我认为你的正确转变并不重要。您只是超出了双精度浮点常数的分辨率。

在Chrome中:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

document.write("x=" + x);
document.write(" y=" + y);

打印出:x = 2.9999999999999996 y = 3

试试这个javascript:   警报(parseFloat(QUOT; 2.9999999999999997779553950749686919152736663818359374999999&QUOT;));

然后试试这个:   警报(parseFloat(QUOT; 2.9999999999999997779553950749686919152736663818359375&QUOT;));

您看到的是简单的浮点不准确性。有关详细信息,请参阅此示例: http://en.wikipedia.org/wiki / Floating_point#Accuracy_problems

基本问题是浮点值可以表示第二个数字的最接近值大于或等于3,而浮点数可以达到第一个数字的关闭严格小于3。 / p>

至于为什么右移0.5会发生任何理智,似乎0.5本身就是事先将其转换为int(0)。然后像往常一样通过截断将原始浮点数(2.999 ...)转换为int。

右移操作符仅对整数(两侧)进行操作。因此,向右移位0.5位应完全等同于向右移位0位。并且,在移位操作之前,左侧将转换为整数,这与Math.floor()的作用相同。

  

我怀疑是转换2.9999999999999997779553950749686919152736663818359374999999   它的二进制表示将具有启发性。它可能只有1位不同   来自真实的3。

好猜,但没有雪茄。 由于双精度FP编号为53位,实际上是3之前的最后一个FP编号 (确切):2.999999999999999555910790149937383830547332763671875

但为什么会这样 2.9999999999999997779553950749686919152736663818359375

(这是确切的,不是49999 ......!)

更高比最后一个可显示单位?四舍五入。转换例程(字符串到数字)只是被正确编程,以便将输入舍入到下一个浮点数。

2.999999999999999555910790149937383830547332763671875

.......(值之间,增加) - &gt;向下滚动

2.9999999999999997779553950749686919152736663818359375

.......(值之间,增加) - &gt;最多3个

3

转换输入必须使用完全精度。如果数字恰好是两者之间的一半 那两个fp数字(即2.9999999999999997779553950749686919152736663818359375) 舍入取决于设置的标志。默认舍入是舍入到偶数,这意味着该数字将四舍五入到下一个偶数。

现在

3 = 11.(二进制)

2.999 ... = 10.11111111111 ......(二进制)

设置所有位,数字始终为奇数。这意味着精确的半数将被四舍五入,所以你得到奇怪的..... 49999期间,因为它必须小于精确的一半才能与3区分开。

我怀疑将2.9999999999999997779553950749686919152736663818359374999999转换为二进制表示将是有启发性的。它可能只与真正的3不同。

为了补充约翰的回答,这种表现比Math.floor更高的可能性非常小。

我不知道JavaScript是使用浮点数还是使用某种无限精度库,但无论哪种方式,你都会在这样的操作上得到舍入错误 - 即使它定义得很好。

应该注意,数字“.0000000000000007779553950749686919152736663818359374”很可能是 Epsilon ,定义为“最小数量E(1 + E) )&gt; 1.&QUOT;

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top