Skip to content

Numbers from text

Level: 201 · a number arrives in a file, a form field or a command-line argument

One line: Perl reads a number from the front of any string — "42abc" + 0 is 42, "0x1A" is 0, "1_000" is 1 — and warns without stopping; "nan", "inf" and "42\n" all pass looks_like_number; /^\d+$/ accepts "42\n" and the Arabic-Indic ٣; and sprintf '%.2f', 2.675 is 2.67, because the double nearest 2.675 is just below it.

Measured

Verified output of numbers_pl.pl — regenerated by tools/run_examples.py, never hand-typed.

--- what + 0 makes of a string ---
  string         + 0      warns   looks_like_number
  "42"           42       no      yes
  " 42 "         42       no      yes
  "42\n"         42       no      yes
  "42abc"        42       yes     no
  "3.14.15"      3.14     yes     no
  "abc"          0        yes     no
  ""             0        yes     no
  "0x1A"         0        yes     no
  "1_000"        1        yes     no
  "1e3"          1000     no      yes
  "٣"            0        yes     no
  "nan"          NaN      no      yes
  "inf"          Inf      no      yes
  "0 but true"   0        no      yes

--- hex and oct read prefixes; + 0 never does ---
  hex('1A')     = 26
  hex('0x1A')   = 26
  oct('0x1A')   = 26
  oct('0b101')  = 5
  oct('755')    = 493
  oct('0o755')  = 493

--- is this string an integer? ---
  string   /^\d+$/    /\A\d+\z/    /\A[0-9]+\z/
  "42"     yes        yes          yes
  "42\n"   yes        no           no
  "٣"      yes        yes          no
  "4 2"    no         no           no

--- floating point is binary, and perl prints 15 significant digits ---
  0.1 + 0.2                = 0.3
  0.1 + 0.2 == 0.3         ? no
  sprintf '%.17g', 0.1+0.2 = 0.30000000000000004
  sprintf '%.2f', 2.675    = 2.67
  sprintf '%.20f', 2.675   = 2.67499999999999982236   (the double nearest 2.675)
  sprintf '%.2f', 1.005    = 1.00
  sprintf '%.0f', 0.5      = 0
  sprintf '%.0f', 1.5      = 2
  sprintf '%.0f', 2.5      = 2
  int(-3.7)                = -3

--- integers are exact until they are not ---
  9007199254740992 + 1      = 9007199254740993
  2**53 + 1                 = 9.00719925474099e+15   (** returns a floating-point number)
  '18446744073709551615'+0  = 18446744073709551615
  '18446744073709551616'+0  = 1.84467440737096e+19

What + 0 accepts

Perl converts a string to a number wherever a number is needed, reading as much of the front as looks numeric:

  • Whitespace around the number is fine, a trailing newline included, and there is no warning.
  • Anything after the number is dropped, with a warning: 42abc is 42, 3.14.15 is 3.14.
  • Source-code spellings do not apply. 0x1A and 1_000 are numbers in a Perl program; in a string they are 0 and 1.
  • nan and inf are numbers, and looks_like_number agrees — so a check with looks_like_number lets both through.
  • 0 but true is special-cased: numerically 0, true as a string, and exempt from the warning, for functions that must return a true zero.
  • ٣ is not a number to + 0, even though \d matches it.

hex and oct

hex reads hexadecimal with or without 0x. oct reads a prefix and picks the base: 0x hex, 0b binary, 0o or a leading digit octal. Use them when the string is meant to be in that base; + 0 never is.

Is it an integer?

  • /^\d+$/ accepts "42\n", because $ matches before a final newline, and accepts ٣, because \d is Unicode — see the Unicode bug.
  • /\A\d+\z/ fixes the newline — \z is the true end of the string — but still accepts ٣.
  • /\A[0-9]+\z/ is the check that means what it says.

Floating point

A number with a fraction is a binary double. Perl prints it with 15 significant digits, so 0.1 + 0.2 prints 0.3 and is not equal to 0.3%.17g shows the rest. sprintf '%.2f', 2.675 rounds the double actually stored — %.20f shows it begins 2.674999… — down to 2.67. And a value exactly halfway rounds to the even neighbour: 0.5 to 0, 1.5 and 2.5 both to 2. int truncates towards zero.

Integers

Perl keeps whole numbers as 64-bit integers while it can, so 9007199254740992 + 1 is exact, one past where a double stops counting in ones. ** returns a double, so 2**53 + 1 is not exact. The largest unsigned 64-bit integer converts exactly; one more and the string becomes a double, printed as 1.84467440737096e+19.

What to do instead

sub parse_int ($s) {
    return $s =~ /\A[+-]?[0-9]+\z/a ? $s + 0 : undef;
}

Check the whole string before converting, and never trust + 0 to tell you it failed. For money, count in integer cents, or use Math::BigFloat, which ships with perl.

If you are coming from another language

  • C. atoi is + 0: it reads the front and cannot report failure. strtol tells you where it stopped. See Parsing a number from text ↗.
  • Rust. "42abc".parse::<i32>() is an error, never a 42. See Parsing out of a string ↗.
  • Python. int("42abc") raises ValueError; int(" 42\n") is 42; and int("٣") is 3 — Python converts every Unicode decimal digit, where Perl reads 0.
  • Java. The other direction — a number written as text — follows the locale: Format follows the locale ↗.

See also