JavaScript Math.ceil(Math.abs()) 优化

6

我在循环中使用Math.ceil( Math.abs( x ) )。 有人能够对这个操作进行优化吗?(位运算或其他方法?)

欢迎您在jsperf.com上进行基准测试。


请返回操作的返回值,而不是它是否小于或大于某个值的条件。 - Dan
1
我已将测试值更改为负浮点数,而不是整数,以便代码实际上需要执行某些操作。 - OrangeDog
1
从WebKit源代码中看来,JavaScript方法Math.abs()Math.ceil()使用了C++ cmath函数fabs()ceil()。我真的不认为你可以对其进行优化。你确定这是一个问题吗?你能贴出你正在使用的代码吗? - Crozin
大家好!我发布了最终的基准测试,但还有一些问题没有解决。http://jsperf.com/best-math-ceil-math-abs - Dan
@Crozin:这些计算用于动画紧密循环。 - Dan
显示剩余3条评论
6个回答

9

根据WebKit JavaScriptCore,Math.abs函数并没有更简单的实现方式。

case MathObjectImp::Abs:
result = ( arg < 0 || arg == -0) ? (-arg) : arg;

然而 ceil 使用 C 语言的 ceil 函数

 case MathObjectImp::Ceil:
    result = ::ceil(arg);

在JSpref测试中,http://jsperf.com/math-ceil-vs-bitwise,使用位运算比使用Math.ceil更快。位运算更快
测试@orangedog的答案http://jsperf.com/math-ceil-vs-bitwise/2,Math.ceil更快。

所以我想你最好的选择是:

var n = Math.abs(x);
var f = (n << 0),
f = f == n ? f : f + 1;

1
你做了一项出色的WebKit源代码调查,点个赞! - Dan

3

x < 0 ? Math.ceil(-x) : Math.ceil(x)在Firefox 3.6中可以提高40%的速度(其他浏览器差异不大),并保持相对易读。

这里是jsPerf页面。请忽略“一些位运算符”的标签;上面的表达式没有使用任何位运算符。


1
我在jsperf上发现,在这种情况下,进行一些位运算比使用ceiling函数更快。 - starwed

2
JavaScript不像C语言一样是编译型语言,因此在JS中可以奏效的位运算,在这种语言中并不那么好用,因为数字被存储为64位浮点数。请参阅此SO帖子
即使如此,在JS中编写的内容也会以某种方式转换为本地代码,并且它可能会更快或更慢,这取决于实现方式。
由于Math.ceilMath.abs是内置的,我猜想它们已经得到了大力优化,所以我怀疑您是否能通过自己的一些技巧来获得更好的性能。
底线:有三件事阻碍您更快地完成它:
  1. JS中的数字表示法
  2. 它是解释性语言
  3. 您使用的函数是“本地”函数,因此它们应该足够快

1
你忘记了调度开销 - 使用运算符而不是方法很可能会加速代码。 - Christoph
1
尽管在JS中数字似乎都是双精度浮点数,但所有JS引擎实际上在可能的情况下将值存储为整数,因此当数字已经是整数时,位运算符仍然很快,因为不需要进行转换。 - gsnedders

1

parseInt(Math.abs(x)) + 1 在 Firefox 上比 jsperf 测试快约30%

由于参数始终为正,因此 Math.ceil() 中的分支是不必要的。


4
可能会更快,但这也是错误的——任何带有零值小数组成部分的数字都将比向上取整的值高1个不正确的值。 - annakata
啊,说得好。不出所料,检查这个会使它再次变慢。 - OrangeDog

1

这里还有一个不需要进行任何查找的例子:

((x >= 0 ? x : -x) + 0.5) >> 0

这里的四舍五入是使用 Math.round 而不是 Math.ceil - MikeM

-2

进行计算的两种最快方法(在现代浏览器中几乎具有相同的速度):

function f (n) {
   return (~~n) + 1;
}

// or 

function f1 (n) {
   return (n | 0) + 1;
}

// some tests, ~~ operator seems to work identicaly on numbers:

( 3.3 | 0 ) === 3;   
( 3.8 | 0 ) === 3;   
( -3.3 | 0 ) === -3; 
( -3.8 | 0 ) === -3;  

Math.floor(-3.3) == Math.floor(-3.8) == -4 不同


f1(1); // 2. 这些函数不遵循 Math.ceil 的行为。也许你应该接受最高评分的答案,而不是你自己的答案。 - MikeM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接