Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP is_numeric - false when Exponent in string

Tags:

php

<?php

$val = '245E1';

var_dump($val); // return string(5) "245E1"

$n = is_numeric($val);
var_dump($n); // return bool(true)

Problem: is_numeric return TRUE

Questions:

  1. How to treat $val as string, not a number?

  2. How to disable exponent interpretation?

like image 386
Ing. Michal Hudak Avatar asked Sep 27 '22 18:09

Ing. Michal Hudak


2 Answers

From the manual on is_numeric(), you'll notice there are plenty of characters that can go in:

Numeric strings consist of optional sign, any number of digits, optional decimal part and optional exponential part. Thus +0123.45e6 is a valid numeric value. Hexadecimal (e.g. 0xf4c3b00c), Binary (e.g. 0b10100111001), Octal (e.g. 0777) notation is allowed too but only without sign, decimal and exponential part.

If you wish to check if a variable is type integer, you can use is_int(), and for floats, you can use is_float(). Note, though, that e.g. with is_int(), your variable must actually be of type integer, not just a numeric string. You could also use ctype_digit() for this, but then your variable type would have to be a string; or specifically, not an integer that matches an ASCII character's ord().

It may be easiest to just use if (preg_match('#^\d+(\.\d+)?$#', $str)) to validate your variable of which-ever type as having only digits. (Remove the expression in brackets from the pattern if optional decimals are not welcome.)

The other option is ctype_digit((string) $str) and casting the variable into a string type to avoid the ASCII mapping clash with integer types. This would not return true if you had decimal points.

100000 loops of ctype_digit: 0.0340 sec. 100000 loops of preg_match: 0.1120 sec. Use ctype_digit if you want digits only and intend to do this a lot. Otherwise; whatever fits your style.

like image 125
Markus AO Avatar answered Oct 03 '22 02:10

Markus AO


I had some struggles with this and came up with something:

function is_really_numeric($string) {
  return preg_match('#^(?!0[1-9])\d*\.?(?!\.)\d+$#', $string);
}

And in action:

<?php

  $val = '245E1';

  if (preg_match('#^(?!0[1-9])\d*\.?(?!\.)\d+$#', $val)) {
    settype($val, 'float');
  }

  var_dump($val);

?>

This would not interpret "0123", "24e51", ".", or "1.2.3.4" as a numeric value.

https://regex101.com/r/PtPM8l/3

like image 29
tim Avatar answered Oct 03 '22 01:10

tim