PHP如何检查一个变量是否为整数

43

我有以下这段PHP代码:

$entityElementCount = (-($highScore-$totalKeywordCount))/0.29;

我的问题是如何检查$entityElementCount是否为整数(2、6、...)或小数(2.33、6.2、...)。

谢谢!


除了其他答案之外,对于字符串,请使用ctype_digit($num) - Beyondo
22个回答

62
if (floor($number) == $number)

5
尝试这个:$number = (-(4.42-5))/0.29;经过计算后,$number = 2,但上面的条件没有说明它是一个整数。 - spacemonkey
2
尝试在$number上使用var_dump。它是一个浮点数,这就是为什么它不起作用的原因。浮点数是不精确的。 - JAL
8
正确的用法是 if(is_numeric($number) && floor($number) == $number) ... 因为 floor('banana') 的结果为 'banana',因此条件语句会返回 true。 - pachanka
1
我想知道为什么有这么多的投票。如果我发布了这个答案,我会将其删除。 - Milo LaMar
@spacemonkey 错了,它不是一个整数。经过计算,$number 是 2.000000000000000444089209850062616169452667236328125(至少在64位php上),Tyler的代码运行良好。- 尝试 $number = (-(4.42-5))/0.29; var_dump(number_format($number,100)); 以查看所有数字的 $number(大多数函数,如 echo/var_dump 等都不显示所有数字,但是 number_format,使用正确的参数,可以显示所有数字)。 - hanshenrik
1
@Shafizadeh 很好的观点。我制作了一个改进版,不会犯那个错误。 - hanshenrik

46

虽然这已经过时了,但我想分享一些我刚发现的东西:

使用fmod函数并检查是否为0。

$entityElementCount = (-($highScore-$totalKeywordCount))/0.29;
if (fmod($entityElementCount,1) !== 0.0) {
    echo 'Not a whole number!';
} else {
    echo 'A whole number!';
}

fmod与%不同,因为如果你有一个分数,%在我这里似乎不起作用(它返回0......例如,echo 9.4 % 1;将输出0)。使用fmod,您将获得小数部分。例如:

echo fmod(9.4, 1);

将输出0.4


1
@Flavius 谢谢!我在一个答案被接受后才发布了这个帖子。我把它放在这里是因为我认为它非常有用。随着时间的推移,它可能会得到像你这样的人更多的赞 :) - Joseph
1
这个答案似乎是最好的。 - Moe Epo
如果您想要进行类型匹配(===!==),正如大多数安全顾问建议的那样,fmod 函数会返回一个浮点数,因此您需要执行类似于 fmod($a,1)=== 0.0 的操作来确保它是整数。我也不完全确定这是否准确。很遗憾 fmod 返回一个浮点数。 - danielson317
1
@danielson317 谢谢你的提示。我已经好几年没碰 PHP 了,所以我不知道它有这种类型的等号运算符。至于 fmod 返回浮点数是不幸的,为什么呢?返回浮点数是函数的目的(fmod = "float modulus")。另外,我测试了一下你的理论,你是对的,需要写成 !== 0.0。我会更新答案的。 - Joseph
@Joseph 浮点数本质上是一个估计值。如果返回一个双精度数,表达式将保证准确,而浮点数可能看起来像0.0,甚至打印为0.0,但由于处理中的某些估计,它可能不等于===0.0。实际上,PHP并不是为货币设计的,这实际上并没有什么区别。请记住,精度可能会根据php.ini中的“precision”而改变。 - danielson317
@danielson317 噢,好的,我现在明白你的意思了 - 那很有道理。感谢解释! - Joseph

18
$entityElementCount = (-($highScore-$totalKeywordCount))/0.29;
if (ctype_digit($entityElementCount) ){
    // (ctype_digit((string)$entityElementCount))  // as advised.
    print "whole number\n";
}else{
    print "not whole number\n";
}

4
抱歉我之前回答得太仓促了。在使用 ctype_digit 函数之前,你需要将 $entityElementCount 转换为字符串,这样上面的代码才能正常工作。 - spacemonkey
3
因此,如果(ctype_digit((string)$entityElementCount))就可以了。 - JAL
10
注意:在这个例子中,ctype_digit('10.00')返回FALSE,表示“不是整数” - Philipp
@Philipp,你能试着挑战一下我的解决方案吗? - hanshenrik

16

我会像这样使用intval函数:

if($number === intval($number)) {

}

测试:

var_dump(10 === intval(10));     // prints "bool(true)"
var_dump("10" === intval("10")); // prints "bool(false)"
var_dump(10.5 === intval(10.5)); // prints "bool(false)"
var_dump("0x539" === intval("0x539")); // prints "bool(false)"

其他解决方案

1)

if(floor($number) == $number) {   // Currently most upvoted solution: 

测试:

$number = true;
var_dump(floor($number) == $number); // prints "bool(true)" which is incorrect.

2)

if (is_numeric($number) && floor($number) == $number) {

特殊情况:

$number = "0x539";
var_dump(is_numeric($number) && floor($number) == $number); // prints "bool(true)" which depend on context may or may not be what you want

3)

if (ctype_digit($number)) {

测试:

var_dump(ctype_digit("0x539")); // prints "bool(false)"
var_dump(ctype_digit(10)); // prints "bool(false)"
var_dump(ctype_digit(0x53)); // prints "bool(false)"

13

如Chacha所说,最基本的方法是

if (floor($number) == $number)

然而,浮点数类型无法精确地存储数字,这意味着1可能会被存储为0.999999997。这当然会导致上面的检查失败,因为它将被舍入为0,即使对于您的目的而言,它足以被视为整数。因此,尝试使用以下代码:

if (abs($number - round($number)) < 0.0001)

1
考虑到浮点数的特性,我认为这是最好的解决方案。只需选择适合您任务的精度级别即可。 - JAL
失败于“123foobar”(它认为123foobar是一个整数)- 我真的很喜欢精度调整。 - hanshenrik

7

我测试了所有提出的解决方案,使用了许多有问题的值,它们在至少一个测试用例中都失败了。使用is_numeric($value)检查$value是否为数字可以减少许多解决方案的失败次数,但并不能将任何解决方案变成终极解决方案:

$test_cases = array(0.29, 2, 6, 2.33, 6.2, '10.00', 1.4, 10, "10", 10.5, "0x539", true,
    false, 0x53, 9.4, "ten", "100", 1, 0.999999997, 0, 0.0001, 1.0, 0.9999999,
    (-(4.42-5))/0.29);

function is_whole_number($value) {
    // Doing this prevents failing for values like true or "ten"
    if (!is_numeric($value)) {
        return false;
    }

    // @ghostdog74's solution fails for "10.00"
    // return (ctype_digit((string) $value));

    // Both @Maurice's solutions fails for "10.00"
    // return ((string) $value === (string) (int) $value);
    // return is_int($value);

    // @j.hull's solution always returns true for numeric values
    // return (abs($value) % 1 == 0 ? true : false);

    // @ MartyIX's solution fails for "10.00"
    // return ($value === intval($value));

    // This one fails for (-(4.42-5))/0.29
    // return (floor($value) == $value);

    // This one fails for 2
    // return ctype_digit($value);

    // I didn't understand Josh Crozier's answer

    // @joseph4tw's solution fails for (-(4.42-5))/0.29
    // return !(fmod($value, 1) != 0);

    // If you are unsure about the double negation, doing this way produces the same
    // results:
    // return (fmod($value, 1) == 0);

    // Doing this way, it always returns false 
    // return (fmod($value, 1) === 0);

    // @Anthony's solution fails for "10.00"
    // return (is_numeric($value) && is_int($value));

    // @Aistina's solution fails for 0.999999997
    // return (abs($value - round($value)) < 0.0001);

    // @Notinlist's solution fails for 0.999999997
    // return (round($value, 3) == round($value));
}

foreach ($test_cases as $test_case) {
    var_dump($test_case);
    echo ' is a whole number? ';
    echo is_whole_number($test_case) ? 'yes' : 'no';
    echo "\n";
}

我认为像 @Aistina 和 @Notinlist 提出的解决方案是最好的,因为它们使用误差阈值来确定一个值是否为整数。需要注意的是,对于表达式 (-(4.42-5))/0.29,它们都能够正常工作,而其他所有解决方案在该测试用例中均失败。

我决定使用 @Notinlist 的解决方案,因为它更易读:

function is_whole_number($value) {
    return (is_numeric($value) && (round($value, 3) == round($value)));
}

我需要测试数值是否为整数、货币或百分比,我认为精度的2位数字就足够了,所以@Notinlist的解决方案符合我的需求。

运行此测试:

$test_cases = array(0.29, 2, 6, 2.33, 6.2, '10.00', 1.4, 10, "10", 10.5, "0x539", true,
    false, 0x53, 9.4, "ten", "100", 1, 0.999999997, 0, 0.0001, 1.0, 0.9999999,
    (-(4.42-5))/0.29);

function is_whole_number($value) {
    return (is_numeric($value) && (round($value, 3) == round($value)));
}

foreach ($test_cases as $test_case) {
    var_dump($test_case);
    echo ' is a whole number? ';
    echo is_whole_number($test_case) ? 'yes' : 'no';
    echo "\n";
}

生成以下输出:
float(0.29)
 is a whole number? no
int(2)
 is a whole number? yes
int(6)
 is a whole number? yes
float(2.33)
 is a whole number? no
float(6.2)
 is a whole number? no
string(5) "10.00"
 is a whole number? yes
float(1.4)
 is a whole number? no
int(10)
 is a whole number? yes
string(2) "10"
 is a whole number? yes
float(10.5)
 is a whole number? no
string(5) "0x539"
 is a whole number? yes
bool(true)
 is a whole number? no
bool(false)
 is a whole number? no
int(83)
 is a whole number? yes
float(9.4)
 is a whole number? no
string(3) "ten"
 is a whole number? no
string(3) "100"
 is a whole number? yes
int(1)
 is a whole number? yes
float(0.999999997)
 is a whole number? yes
int(0)
 is a whole number? yes
float(0.0001)
 is a whole number? yes
float(1)
 is a whole number? yes
float(0.9999999)
 is a whole number? yes
float(2)
 is a whole number? yes

你的算法主要问题在于 0.999999997 不是个整数。 - danielson317
正如我所说,我需要测试值是否为整数、货币或百分比,因此我采用了2位精度。如果您需要调整精度,只需更改 round($value, 3) 即可。使用 round($value, 9) 会产生 float(0.999999997) 是整数吗?不是 - Antônio Medeiros
请记住,所有这些都受php.ini中的precision设置的影响。如果您将其设置为8或更少,则float(0.999999997)是一个整数。但是,如果您将其设置为20,则float(0.999999997) => 0.99999999699999997382。默认值是14,所以仍会产生差异。您的算法在统计方面非常有效,这也是它在此处的目的,但不适用于货币或输入验证。 - danielson317

6

如果你知道它将是数字(意味着它永远不会是一个整数转换为字符串,比如"ten""100"),你可以直接使用is_int()

$entityElementCount = (-($highScore-$totalKeywordCount))/0.29;
$entityWholeNumber = is_int($entityElementCount);

echo ($entityWholeNumber) ? "Whole Number!" : "Not a whole number!";

不错,但这里的三元运算符是多余的。只需要 $entityWholeNumber = is_int($entityElementCount) 就足够了。 - Iain Fraser
1
你的两个代码示例完全相同,第二个只是以一种相当繁琐的方式将is_int调用的结果存储在一个变量中。此外,这检查的是变量是否为整数,而不是其所包含的值是否为整数。 - Aistina
1
整数始终是整数。如果他想知道它是整数还是非整数,那就意味着它是整数或浮点数。代码示例应该执行相同的操作。 - Anthony
按照定义,整数就像是整数,但它们可以包括负号。整数没有负号。 - ghostdog74
@ghostdog74 - 整数可以是负数。你想的是自然数,它也不能包括0。而且用户没有问它是否是整数和实数,只是整数。整数和全数是一样的。更不用说任何提供的舍入技术也不会检查数字是否小于0了。 - Anthony
请勿在没有引用字典的情况下使用“根据定义”这个词。按照定义,整数是所有整数,包括所有自然数及其负数。来源:http://en.wikipedia.org/wiki/Integer - Anthony

3
if(floor($number) == $number)

这不是一个稳定的算法。当数值在数学上为1.0时,其数值可以为0.9999999。如果对其应用floor()函数,则结果为0,而不等于0.9999999。

你需要猜测一个精度半径,例如3位小数。

if(round($number,3) == round($number))

3

我想到的另一种hacky方法是 ceil($value) === floor($value)。如果一个数字是整数,这应该总是正确的,即使比较10和10.000,甚至可以用于将数字强制转换为字符串进行比较,例如ceil("10.0") === floor(10)


3
    $num = 2.0000000000001;
    if( $num == floor( $num  ) ){
        echo('whole');
    }else{
        echo('fraction');
    }

2.0000000000001 | 分数

2.1 | 分数

2.00 | 整数

2 | 整数


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