PHP中的整数除法

23

我正在寻找在php中进行整数除法的最快方法。例如,5 / 2应该是2,6 / 2应该是3,依此类推。如果我只是简单地这样做,php将在第一种情况下返回2.5,我唯一能找到的解决方案是使用intval($my_number/2) - 这不是我想要的速度(但会得到期望的结果)。

我该怎么做?

编辑:
感谢大家提供的意见,我使用rubber_boots发布的脚本测试了其中一些意见,这里可以看到结果(在2Ghz英特尔Core 2 Duo MacBook上运行MAMP且已使用10000000次迭代)。

start (10000000)
(int)...: 2.26 sec
floor(): 4.36 sec
int_divide(): 2.86 sec
bit-shift: 1.45 sec //note: only works for divisions through powers of 2
intval(): 4.51 sec
round() with PHP_ROUND_HALF_DOWN: 5.48 sec

到目前为止,位移操作是最快的方式,但我会把这个问题保持开放状态一天,看看是否有其他可能性...

EDIT2:
更新了结果,使用了PHP_ROUND_HALF_DOWN进行四舍五入(感谢Col._Shrapnel)


4
请问您的代码中涉及了多少计算量,以使得这个功能具有显著的性能? - Your Common Sense
8
5/2应该是4吗?这是PHP的特性吗? - nvuono
我需要进行大约2-3百万次计算,使用intval()函数将需要30-40秒(我认为可能可以快2或3倍)。 - oezi
1
除了@Alexander的建议之外,您可能还想考虑使用Gearman或其他JobQueues的多个异步进程。 - Gordon
1
你的服务器速度有点慢。位移操作确实是最快的,应该避免使用intval函数。但我在我的服务器(基于linux/pentium)上进行了300万次迭代测试:$result = intval($x/2) [3.5481秒]; $result = (int)(6/2) [1.5452秒]; $result = $x>>1 [1.1542秒]; result = ($x-($x%2))/2 [1.692秒] 不清楚为什么需要高度优化php性能。你在大约6分钟内回答了自己的问题,这让我怀疑这是一个恶意提问。 - ghoppe
显示剩余3条评论
8个回答

33

将其转换为整数类型:

$result = (int)(6 / 2);

由于某些原因,它比intval()要快得多。

编辑:假设你正在寻找一个普遍的整数除法解决方案。位移是除以(或乘以)2的幂的特殊情况。如果这引起了您的兴趣,则可以参考以下内容:

a / b^n = a >> n where a, b, n are integers

所以:

a / 2 = a / 2^1 = a >> 1

但是有两个需要注意的地方:

  1. 许多编译器/解释器会自动为您完成这项工作,因此没有必要猜测;

  2. 除非您在单个脚本执行中进行至少 100,000 次此操作,否则不要费心。这是一种毫无意义的微小优化。

进一步阐述(2),是的,(int)parseInt()更快,但这重要吗?几乎肯定不重要。将注意力集中在可读性强的代码和良好的算法上。这样的事情是一个无关紧要的干扰。


这很不错,但是我在此期间找到了更快的解决方案 - 但非常感谢您的提示。 - oezi
1
由于某种原因,intval()是一个函数,而(int)不是。在PHP中,函数有相当大的开销,这就是为什么它更快的原因。许多编译器/解释器会自动为您完成此操作。PHP很蠢,它不会为您进行优化。但完全同意“专注于可读的代码和良好的算法。这种事情是无关紧要的干扰。” :) - NikiC

28

如果要进行除以2的操作,最快的方式是使用位移。

5>>1 = 2
6>>1 = 3

等等之类的东西。它所做的就是将位向右移动1位,从而将数字除以2并丢失余数。

1110 >> 1 =  111
1011 >> 1 =  101
1011 >> 2 =   10 //division by 4
1011 << 1 =10110 

5
感谢您的智慧方法和关注计算机架构课程!+1 - Leonel
如果不是除以2怎么办? - Pacerier
然后你只需要用那个数字去除。除以2的幂是一种特殊情况,而且在很多场合下都被使用。例如,如果你想将十六进制颜色格式转换为RGB值,二进制就是实现这个过程的方式。我也遇到了许多问题,使用这种操作有优雅的解决方案,比使用十进制算术的算法高效数十倍。 - Alex
谢谢,我不知道在PHP中可以移位整数!@Pacerier:移位用于将数字除以2的幂次方:要除以2^n,您必须从整数中移出n位。 - Yoone
@Yoone,不是,我的意思是如果我们不想除以2。例如,我们想除以5? - Pacerier
@Pacerier,位移只用于将整数乘以或除以2^n,因为该操作直接在整数的二进制值上进行。如果您需要进行更复杂的计算,则无法使用此方法,但是这里有一个答案可能会让您受益:http://stackoverflow.com/a/18669991/860283(请注意,除以5可能会有些棘手,因为它不能写成`2^n`与`n>1`的和)。 - Yoone

5

嘿,我不知道我是如何被牵扯进这个问题的,因为它似乎来自2010年,并且这并不是一个真正的答案,但由于作者似乎收集了所有快速划分整数的方法,所以它可能对某些人有所帮助。

当我为自己编写快速代码时,我通常使用0|而不是(int),因为"|"运算符的优先级比大多数其他运算符都低,所以你不需要额外的括号。即使

$x=0| 0.3+0.7;

这段代码将按预期工作,并且在查看代码时很容易找到(至少对我来说),因为我认为“= 0 |”是一种特殊的运算符“设置并转换为整数”。

因此,为了丰富您的知识库(这些只是将其它方法转换为整数):

$c=0| $x/$y;

并且

$c=$x/$y % PHP_INT_MAX;

哇,谢谢。我以前从没听说过 0|。等我回家了,我会把它加到我的列表里。 - oezi
在PHP 8中,你会得到"从浮点数0.5到整数的隐式转换会丢失精度"的错误。因此,我们必须使用(int)来代替。 - undefined

4

只是测试一下:

结果(Win32,Core2/E6600):

 generic division (3000000)
 (int)DIV:       1.74 sec
 intval(DIV):    6.90 sec
 floor(DIV):     6.92 sec
 int_divide():   1.85 sec

 division by 2 (3000000)
 (int)(VAL/2):   1.75 sec
 VAL >> 2:       1.63 sec
 (int)(VAL*0.5): 1.72 sec

代码:

 ...
 echo "generic division ($N)\n";
 $start = getTime(); for($i=1; $i<$N; $i++) { $c = (int)(($i+1) / $i); }
 printf("(int)DIV:\t %.2f sec\n", getTime()-$start);

 $start = getTime(); for($i=1; $i<$N; $i++) { $c = intval(($i+1) / $i); }
 printf("intval(DIV):\t %.2f sec\n", getTime()-$start);

 $start = getTime(); for($i=1; $i<$N; $i++) { $c = floor(($i+1) / $i); }
 printf("floor(DIV):\t %.2f sec\n", getTime()-$start);

 $start = getTime(); for($i=1; $i<$N; $i++) { $c = ($i - ($i % ($i+1))) / ($i+1); }
 printf("int_divide():\t %.2f sec\n", getTime()-$start);

 echo "division by 2 ($N)\n";
 $start = getTime(); for($i=1; $i<$N; $i++) { $c = (int)(($i+1) / 2.0); }
 printf("(int)(VAL/2):\t %.2f sec\n", getTime()-$start);

 $start = getTime(); for($i=1; $i<$N; $i++) { $c = ($i+1) >> 2; }
 printf("VAL >> 2:\t %.2f sec\n", getTime()-$start);

 $start = getTime(); for($i=1; $i<$N; $i++) { $c = (int)(($i+1)*0.5); }
 printf("(int)(VAL*0.5):\t %.2f sec\n", getTime()-$start);
 ...

敬礼

rbo


2

只有当 $x 和 $y 是整数时才有效。

function int_divide($x, $y) {
    return ($x - ($x % $y)) / $y;
}

1

通常情况下,round() 函数用于此目的。但我不知道它的速度如何。我的代码中从未有过数百万次计算,最多只有几十次。


round/ceil/floor比intval慢一些,而且round会给出错误的结果(2.5将变成3而不是2)-但感谢您的尝试。 - oezi
@oezi round 可以给任何能阅读手册中几行代码的人所需的任何结果。无论如何,这是硬件或差劲应用程序设计问题,而不是 PHP 函数的问题。 - Your Common Sense
好的,我的错。如果您传递第三个参数,那么在这种情况下可以让 round() 函数向下舍入。我会更新我的问题结果以考虑这个选项... - oezi

1
使用round()、ceil()或floor()函数,否则在变量前声明类型,如int()。

正如我在另一条评论中提到的那样,这些函数甚至比intval()函数更慢。 - oezi

0
对于PHP 7+,您可以使用intdiv($a, $b)函数。否则,请使用(int)强制转换,而不是二进制OR操作(|),因为后者在PHP 8中会产生警告,如"从浮点数0.5隐式转换为整数会丢失精度"。

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