fmod

(PHP 4 >= 4.2.0, PHP 5, PHP 7, PHP 8)

fmod返回参数相除后的浮点余数(模)

描述

fmod(float $num1, float $num2): float

返回被除数 (num1) 除以除数 (num2) 后的浮点余数。余数 (r) 定义为:num1 = i * num2 + r,其中 i 为某个整数。如果 num2 非零,则 rnum1 符号相同,其大小小于 num2 的大小。

参数

num1

被除数

num2

除数

返回值

num1/num2 的浮点余数

范例

范例 #1 使用 fmod()

<?php
$x
= 5.7;
$y = 1.3;
$r = fmod($x, $y);
// $r 等于 0.5,因为 4 * 1.3 + 0.5 = 5.7
?>

参见

  • / - 浮点除法
  • % - 整数模运算
  • intdiv() - 整数除法 - 整数除法

添加注释

用户贡献的注释 20 个注释

nospam at neonit dot de
7 年前
请注意,fmod 的行为与用 PHP 本身编写的类似函数不同,这是因为缺少修复浮点表示错误。

请查看以下示例:
<?php
var_dump
(10 / (10 / 3) === 3.0); // bool(true)
var_dump(fmod(10, 10 / 3)); // float(3.3333333333333)
var_dump(fmod(10, 10 / 3) < 10 / 3); // bool(true)
?>

在内部,无法精确地表示 10 / 3 的结果,因此它总是会略高于或略低于实际结果。在本例中,示例证明它略高于实际结果。

PHP 似乎非常擅长自动修复浮点表示错误,以使它们的行为符合用户的预期。这就是第一行产生 true 的原因,尽管结果略低于 3(例如 2.9999999999[something])。我未能诱使 PHP 对结果进行舍入或截断为 2。

但是,fmod 似乎在计算期间没有应用这些修复。从 10 / 3 中,它得到的值略低于 3,将其向下取整为 2,然后返回 10 - 2 * 10 / 3,该值略小于 10 / 3 的实际结果,但看起来像 10 / 3(第三行)。

不幸的是,这不是预期的结果。有关高质量修复,请参阅其他注释。
jphansen at uga dot edu
19 年前
fmod() 并不反映计算器的 mod 函数。例如,fmod(.25, .05) 将返回 .05 而不是 0,因为 floor()。使用上述示例,您可以通过在自定义 fmod() 中用 round() 替换 floor() 来获得 0。

<?
function fmod_round($x, $y) {
$i = round($x / $y);
return $x - $i * $y;
}

var_dump(fmod(.25, .05)); // float(0.05)
var_dump(fmod_round(.25, .05)); // float(0)
?>
timo underscore teichert at yahoo dot de
9 年前
此函数的行为似乎随着时间的推移而发生了变化。

<?php

echo fmod(3,5);
// php 5.3.2 输出 -2
// php 5.3.8 输出 3

echo fmod(2,5);
// php 5.3.2 输出 2
// php 5.3.8 输出 2

?>

- Timo
cory at lavacube dot net
18 年前
我认为这是不正确的。

尝试使用您的补丁进行测试:
<?php

echo duration( mktime(0, 0, 0, 1, 0, 2006)-time() );

?>

截至目前,这将读取:
1 个月,22 天,24 小时,49 分钟,15 秒

这完全不正确,因为现在是 12 月 9 日。

这里真正的缺陷在于“年”和“月”期间的计算方式。大多数月份的长度都不一样……

非常感谢 SnakeEater251 指出这一点。

获得略微更准确结果的最快速方法是使用基于一个“真”年(即 365.25 天)的平均值。

将年和月更改为:
'year' => 31557600, // 一个“真”年(365.25 天)
'month' => 2629800, // 一个“真”年除以 12 :-)

我将致力于开发一个真正的修复程序,以实现精确的精度。;-)

- Cory Christison
dePijd
14 年前
此类经过了多个单元测试,并修复了在 bugs.php.net 中发现的所有错误。

<?php
抽象类 MyNumber {
public static function
isZero($number, $precision = 0.0000000001)
{
$precision = abs($precision);
return -
$precision < (float)$number && (float)$number < $precision;
}
public static function
isEqual($number1, $number2)
{
return
self::isZero($number1 - $number2);
}
public static function
fmod($number1, $number2)
{
$rest = fmod($number1, $number2);
if (
self::isEqual($rest, $number2)) {
return
0.0;
}
if (
mb_strpos($number1, ".") === false) {
$decimals1 = 0;
} else {
$decimals1 = mb_strlen($number1) - mb_strpos($number1, ".") - 1;
}
if (
mb_strpos($number2, ".") === false) {
$decimals2 = 0;
} else {
$decimals2 = mb_strlen($number2) - mb_strpos($number2, ".") - 1;
}
return (float)
round($rest, max($decimals1, $decimals2));
}
}
?>
radoslaw dot roszkowski at gmail dot com
6 年前
不要依赖这个函数,例如

$a = "7.191"
$b = "2.397000"

if(fmod(floatval($a), floatval($b)) === 0.0) {
..
// 为假,因为
//float(4.4408920985006E-16) != 0.0
KRAER
10 年前
为了在基于 php 的 bash 中创建一个可以恢复的素数列表,我使用了 fmod() 和这里在 php 评论中提供的另外两个用户的代码片段。

这将输出
"素数;最后一个素数和当前素数之间的差"

因此功劳归于他们。我只做了日志文件输出。

这将一直运行到 fmod 支持的最高值。只需输入 $end 值。并对日志文件执行 touch 命令,然后执行 chmod 666,这样 php 就可以访问它。

<?php
function tailCustom($filepath, $lines = 1, $adaptive = true) {

// 打开文件
$f = @fopen($filepath, "rb");
if (
$f === false) return false;

// 设置缓冲区大小
if (!$adaptive) $buffer = 4096;
else
$buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096));

// 跳转到最后一个字符
fseek($f, -1, SEEK_END);

// 读取它并在必要时调整行号
// (否则,如果文件不以空行结尾,结果将是错误的)
if (fread($f, 1) != "\n") $lines -= 1;

// 开始读取
$output = '';
$chunk = '';

// 当我们想要更多时
while (ftell($f) > 0 && $lines >= 0) {

// 弄清楚我们应该跳回多远
$seek = min(ftell($f), $buffer);

// 执行跳转(向后,相对于我们所在的位置)
fseek($f, -$seek, SEEK_CUR);

// 读取一块并将其预先添加到我们的输出中
$output = ($chunk = fread($f, $seek)) . $output;

// 跳回我们开始读取的位置
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);

// 减少我们的行计数器
$lines -= substr_count($chunk, "\n");

}

// 当我们拥有太多行时
// (由于缓冲区大小,我们可能读取了太多行)
while ($lines++ < 0) {

// 找到第一个换行符并删除之前的所有文本
$output = substr($output, strpos($output, "\n") + 1);

}

// 关闭文件并返回
fclose($f);
return
trim($output);

}

function
isPrime( $num )
{
for(
$i = 2; $i*$i <= $num; $i++ )
if( !
fmod($num,$i) )
return
FALSE;

return
TRUE;
}

$logfile = 'prim_save.log';

$lastline = explode(";", tailCustom($logfile));
$begin = ($lastline[0] +1);
$lastprime = $lastline[0];

$end = 999999999999999999999999999999999999;

$fp = fopen($logfile, 'a');
// 行格式 $i.';'.$difference.';'."\n"

for($i = $begin; $i<$end; $i++)
{
if(
isPrime($i) == TRUE)
{
$difference = $i - $lastprime;
fputs($fp,$i.';'.$difference.';'."\n");
$lastprime = $i;
}
}

fclose($fp);
?>
dan danschafer net
6 年前
警告:由于浮点数的工作方式,fmod() 和任何简单的替代方案在输入 $x 和 $y 之间存在巨大数量级差异或输入和输出值时都会出现问题。如果您需要处理大数或任意精度,最好使用 BC Math 或 GMP 等工具。

在解决 fmod() 的问题时,请记住 floor() 始终趋向于 -INF,而不是 0。这会导致常见的 fmod() 替代方案仅适用于正数
<?php
function fmod_positive_only($x, $y) {
return
$x - floor($x/$y) * $y;
}
?>
给定这些简单的输入值
fmod_positive_only(-5, 3) = 1 (错误)
-5 % 3 = -2 (正确)

正确地去除商的小数部分可以通过将商强制转换为整数类型(总是向零方向舍入)或动态选择 ceil() 或 floor() 来实现。动态选择 floor 或 ceil 以尝试保持精度是过度的。如果你的 $x 和 $y 值差异很大,以至于在强制转换时会出现溢出问题,那么它可能在任何情况下都会出现精度问题(见下面的警告)。

<?php
function fmod_overkill($x, $y) {
if (!
$y) { return NAN; }
$q = $x / $y;
$f = ($q < 0 ? 'ceil' : 'floor');
return
$x - $f($q) * $y;
}
?>

这是在给定“正常”数字时 fmod() 的“最佳”替代方案。
<?php
function fmod_alt($x, $y) {
if (!
$y) { return NAN; }
return
floatval($x - intval($x / $y) * $y);
}
?>

警告:即使你得到一个非零的响应,也要了解你的输入数字,以及 fmod() 何时会出错。对于较大的值或取决于你的输入变量类型,float 仍然可能没有足够的精度来获得正确的结果。以下是 fmod() 及其替代方案的一些问题。

PHP_INT_MAX = 9223372036854775807
fmod(PHP_INT_MAX, 2) = 0 (错误)
fmod_alt(PHP_INT_MAX, 2) = 0 (错误)
PHP_INT_MAX % 2 = 1 (正确)

fmod(PHP_INT_MAX, PHP_INT_MAX - 1) = 0 (错误)
fmod_alt(PHP_INT_MAX, PHP_INT_MAX - 1) = 1 (正确)
fmod_alt(PHP_INT_MAX, PHP_INT_MAX - 1.0) = 0 (错误)
PHP_INT_MAX % (PHP_INT_MAX - 1) = 1 (正确)
PHP_INT_MAX % (PHP_INT_MAX - 1.0) = 9223372036854775807 (错误)

fmod(PHP_INT_MAX, 131) = 98 (错误)
fmod_alt(PHP_INT_MAX, 131) = 359 (错误)
fmod_positive_only(PHP_INT_MAX, 131) = 0 (错误)
PHP_INT_MAX % 131 = 97 (正确)
alex at xelam dot net
20 年前
整数模运算

如果你想要两个整数的除法的余数,而不是浮点数,请使用“%”;例如

<?php
$a
= 4;
$b = 3;

print(
$a % $b);
?>

将输出“1”。
cory at simplesystems dot ca
18 年前
关于 Ryan Means 上一条注释的说明

与其使用 explode() 来获取小数点前的数字,不如使用 floor()... floor() 向下舍入小数部分,这正是所需的。

他的同一个示例使用 floor();

<?PHP
$totalsec
=XXXXXXX; // 用秒的整数替换 X

$daysarray = floor( $totalsec/86400 );

$partdays = fmod($totalsec, 86400);
$hours = floor( $partdays/3600 );

$parthours = fmod($partdays, 3600);
$min = floor( $parthours/60 );

$sec = fmod($parthours, 60);

echo
"days " . $days . "<br>";
echo
"hours " . $hours . "<br>";
echo
"minutes " . $min . "<br>";
echo
"seconds " . $sec . "<br>";
?>
ysangkok at gmail dot com
17 年前
请注意,这
<?php
function custom_modulo($var1, $var2) {
$tmp = $var1/$var2;

return (float) (
$var1 - ( ( (int) ($tmp) ) * $var2 ) );
}

$par1 = 1;
$par2 = 0.2;

echo
"fmod: ";
var_dump(fmod ( $par1 , $par2 ));
echo
"custom_modulo: ";
var_dump(custom_modulo ( $par1 , $par2 ));
?>

给出以下结果

fmod: float(0.2)
custom_modulo: float(0)

Fmod 无法提供预期的结果,因此我创建了自己的函数。
Adrien Gibrat
11 年前
有一个计算 gcm 的优雅方法
https://en.wikipedia.org/wiki/Greatest_common_divisor

// 计算 gcd 的递归函数(欧几里德方法)
function gcd ($a, $b) {
return $b ? gcd($b, $a % $b) : $a;
}
// 然后减少任何整数列表
echo array_reduce(array(42, 56, 28), 'gcd'); // === 14

如果你想使用浮点数,请使用近似值

function fgcd ($a, $b) {
return $b > .01 ? fgcd($b, fmod($a, $b)) : $a; // 使用 fmod
}
echo array_reduce(array(2.468, 3.7, 6.1699), 'fgcd'); // ~= 1.232

你可以在 PHP 5.3 中使用闭包

$gcd = function ($a, $b) use (&$gcd) { return $b ? $gcd($b, $a % $b) : $a; };
picaune at hotmail dot com
21 年前
NAN(.net 等价于 Double.NaN)表示“非数字”。
获得 NaN 的一些方法是模运算 0 和 0 的平方根。
verdy_p
11 年前
请注意,fmod 不等同于此基本函数

<?php
function modulo($a, $b) {
return
$a - $b * floor($a / $b);
}
?>

因为 fmod() 将返回一个与 $a 符号相同的数字。换句话说,floor() 函数是不正确的,因为它向 -INF 舍入,而不是向零舍入。

要以正确的方式模拟 fmod($a, $b),方法如下

<?php
function fmod($a, $b) {
return
$a - $b * (($b < 0) ? ceil($a / $b) : floor($a / $b)));
}
?>

请注意,如果 $b 为空,这两个函数都会抛出“除以零”错误。

上面第一个函数 modulo() 是数学函数,它对处理循环结构很有用(例如日历计算或三角函数

- fmod($a, 2*PI) 返回 [0..2*PI) 中的值,如果 $a 为正
- fmod($a, 2*PI) 返回 [-2*PI..0] 中的值,如果 $a 为负
- modulo($a, 2*PI) 始终返回 [0..2*PI) 中的值,与 $a 的符号无关
matrebatre
16 年前
我总是使用这个

function modulo($n,$b) {
return $n-$b*floor($n/$b);
}

它似乎能正常工作。
SnakeEater251
18 年前
关于 cory at lavacube dot net 给出的代码的说明。
如果你不使用 floor 而使用 round,你会得到更好的结果。随着你不断增加更多的时间,你会注意到输出的时间会差很多。

所以,与其使用 $temp = floor( $int_seconds / $length );
我们应该使用 $temp = round( $int_seconds / $length );

<?php

function duration( $int_seconds=0, $if_reached=null )
{
$key_suffix = 's';
$periods = array(
'year' => 31556926,
'month' => 2629743,
'day' => 86400,
'hour' => 3600,
'minute' => 60,
'second' => 1
);

// 用于隐藏更高时间段的 0
$flag_hide_zero = true;

// 进行循环操作
foreach( $periods as $key => $length )
{
// 计算
$temp = round( $int_seconds / $length );

// 确定 temp 是否符合输出条件
if( !$flag_hide_zero || $temp > 0 )
{
// 存储在数组中
$build[] = $temp.' '.$key.($temp!=1?'s':null);

// 将标志设置为 false,允许更低时间段的 0
$flag_hide_zero = false;
}

// 获取剩余秒数
$int_seconds = fmod($int_seconds, $length);
}

// 返回输出,如果非空,则将数组元素合并成字符串,否则输出 $if_reached
return ( !empty($build)?implode(', ', $build):$if_reached );
}

?>
linkboss at gmail dot com
14 年前
你也可以使用取模运算符 '%',它返回相同的结果

<?php
$var1
= 5;
$var2 = 2;

echo
$var1 % $var2; // 返回 1
echo fmod($var1,$var2); // 返回相同的值
?>
konstantin at rekk dot de
20 年前
如果你需要将一个整数在为零时变为零,在不为零时变为 1,可以使用

$sign = (integer)(boolean)$integer;

而不是

$sign = $integer > 0 ? 1 : 0;

它在 100 次操作中(至少在我的机器上)更快。
cory at lavacube dot net
18 年前
一种更正式的生成持续时间字符串的方法

<?php

function duration( $int_seconds=0, $if_reached=null )
{
$key_suffix = 's';
$periods = array(
'year' => 31556926,
'month' => 2629743,
'day' => 86400,
'hour' => 3600,
'minute' => 60,
'second' => 1
);

// 用于隐藏更高时间段的 0
$flag_hide_zero = true;

// 进行循环操作
foreach( $periods as $key => $length )
{
// 计算
$temp = floor( $int_seconds / $length );

// 确定 temp 是否符合输出条件
if( !$flag_hide_zero || $temp > 0 )
{
// 存储在数组中
$build[] = $temp.' '.$key.($temp!=1?'s':null);

// 将标志设置为 false,允许更低时间段的 0
$flag_hide_zero = false;
}

// 获取剩余秒数
$int_seconds = fmod($int_seconds, $length);
}

// 返回输出,如果非空,则将数组元素合并成字符串,否则输出 $if_reached
return ( !empty($build)?implode(', ', $build):$if_reached );
}

?>

简单使用
<?php

echo duration( mktime(0, 0, 0, 1, 1, date('Y')+1) - time(), '如果持续时间已满足,则输出一些花哨的消息...' );

?>

享受吧。:-)
rocan
19 年前
john at digitizelife dot com

好吧,我不确定你的评论如何适用于 fmod..

但是,有一个更简单的处理这种情况的方法..

它被称为位字段(位掩码)

例如:

/* 类别 */
bin dec cat
0001 - 1 - 蓝色
0010 - 2 - 红色
0100 - 4 - 绿色
1000 - 8 - 黄色

/* 权限 */
0010 - 2 - Bob
0101 - 5 - John
1011 - 11 - Steve
1111- 15 - Mary

要找出每个用户的权限,你只需要执行按位与操作

$steve_auth=11;

function get_perm($auth)
{
$cats["蓝色"]=1;
$cats["红色"]=2;
$cats["绿色"]=4;
$cats["黄色"]=8;
$perms=array();
foreach($cats as $perm=>$catNum)
{
if($auth & $catNum)
$perms[$perm]=true;

}

return $perms;
}

print_r(get_perm($steve_auth));
/*
返回
数组
(
[蓝色] => 1
[红色] => 1
[黄色] => 1
)
*/

这比你的素数想法简单得多,实际上,你甚至不需要在任何测试中对用户的权限进行任何测试,你可以直接使用按位与运算符来进行测试。

你可能需要阅读以下内容

http://en.wikipedia.org/wiki/Bitmask
http://uk2.php.net/manual/en/language.operators.bitwise.php
To Top