in reply to How to determine whether a number has a decimal component?

What is the origin of $number?

If it is read from a textual representation (STDIN, File), personally I would chose method 1. to scan for a dot followed by digits where at least one digit is not zero (something along $number =~ /\.0*[1-9]\d*/). Scanning for a dot allone is not sufficient.

If the origin of $number is a computation, than you have to take care of rounding issues when you apply method 2. Comparing $number with int($number) for equalness might fail due to rounding issues. So it might be more robust to do a comparison along abs($number - int($number)) < $epsilon (see Why do floating point numeric equality/inequality tests fail mysteriously?).

Update: Please note GrandFather's correct comment about the special case of the comparison $number == int($number). Indeed, abs($number - int($number)) itself may lead to false positives when $epsilon is ill chosen (see below).

E.g., on my computer, the following snipped produces a nice table of the floating point representation of various 'integers' together with some interesting floats:
for my $a (-2..10, 0.2, -0.2, 1 + 0.25e-15) { $_ = unpack("b*", pack("d", $a)); # double to bit-pattern s/(.{8})/$1 /g; tr[0][.]; # grouping&fmt. $_ = reverse $_; # formatting s/^(..)(.{12})(.*)/Sgn:$1 Exp: $2 Mant:(1,)$3/; print "$a\t -- $_\n"; }

-2   -- Sgn: 1  Exp: 1...... ....  Mant: (1,).... ........ ........ ........ ........ ........ ........
-1 -- Sgn: 1 Exp: .111111 1111 Mant: (1,).... ........ ........ ........ ........ ........ ........
0 -- Sgn: . Exp: ....... .... Mant: (1,).... ........ ........ ........ ........ ........ ........
1 -- Sgn: . Exp: .111111 1111 Mant: (1,).... ........ ........ ........ ........ ........ ........
2 -- Sgn: . Exp: 1...... .... Mant: (1,).... ........ ........ ........ ........ ........ ........
3 -- Sgn: . Exp: 1...... .... Mant: (1,)1... ........ ........ ........ ........ ........ ........
4 -- Sgn: . Exp: 1...... ...1 Mant: (1,).... ........ ........ ........ ........ ........ ........
5 -- Sgn: . Exp: 1...... ...1 Mant: (1,).1.. ........ ........ ........ ........ ........ ........
6 -- Sgn: . Exp: 1...... ...1 Mant: (1,)1... ........ ........ ........ ........ ........ ........
7 -- Sgn: . Exp: 1...... ...1 Mant: (1,)11.. ........ ........ ........ ........ ........ ........
8 -- Sgn: . Exp: 1...... ..1. Mant: (1,).... ........ ........ ........ ........ ........ ........
9 -- Sgn: . Exp: 1...... ..1. Mant: (1,)..1. ........ ........ ........ ........ ........ ........
10 -- Sgn: . Exp: 1...... ..1. Mant: (1,).1.. ........ ........ ........ ........ ........ ........
0.2 -- Sgn: . Exp: .111111 11.. Mant: (1,)1..1 1..11..1 1..11..1 1..11..1 1..11..1 1..11..1 1..11.1.
-0.2 -- Sgn: 1 Exp: .111111 11.. Mant: (1,)1..1 1..11..1 1..11..1 1..11..1 1..11..1 1..11..1 1..11.1.
1 -- Sgn: . Exp: .111111 1111 Mant: (1,).... ........ ........ ........ ........ ........ .......1
See the last line representing the smalles double greater than 1.0 ((1,)0000....00001)? (Not only) here, the given $epsilon-test would lead to a false positive if $epsilon was chosen too "big". In case int() is properly implemented (a valid assumption for modern CPUs and implementations, see IEEE_754), the integer part can be extracted without loss of precision from the float representation by shifting and masking the mantissa. For a real integer, this operation will lead to identical bit patterns that can be compared for equality.
my $a = 1+0.25e-15;# hidden bit and last bit of mantissa: 1 print "Test : is int.? correct?\n"; print "a != int(a) : no yes\n" if $a != int($a); $d = abs($a - int($a)); print "abs(a-int(a) < 1e-15: yes no!\n" if $d<1e-15; print "abs(a-int(a) < 1e-40: yes ???\n" if $d<1e-40; print "abs(a-int(a) > 0 : no yes\n" if $d>0; printf("epsilon < %g required (here!)\n", $d); __END__ Test : is int.? correct? a != int(a) : no yes abs(a-int(a) < 1e-15: yes no! abs(a-int(a) > 0 : no yes epsilon < 2.22045e-16 required (here!)
OK, the comparison of $a and int($a) yields the correct result ($a is not an integer). But choosing the wrong $epsilon (e.g., here: 1e-15) would lead to a false positive - a typical characterstic when comparing against a limit - the limit controls the distribution of errors of first and second kind.
In the light of the OPs, question, it is better to directly compare $number with int($number) than doing the $epsilon-trick. Though, when comparing to different floats ($number1 and $number2), comparing the delta against a tolerance is advisable. Here is a nice article about Comparing Floating Point Numbers.

Replies are listed 'Best First'.
Re^2: How to determine whether a number has a decimal component?
by GrandFather (Saint) on Jan 04, 2009 at 21:06 UTC
    Comparing $number with int($number) for equalness might fail due to rounding issues.

    Not true in the narrow context of the equality test. If you have already rounded $number to a suitable precision then the equality test will always be correct. When they can be represented at all, integers are stored and manipulated precisely.


    Perl's payment curve coincides with its learning curve.
Re^2: How to determine whether a number has a decimal component?
by Xenofur (Monk) on Jan 04, 2009 at 17:07 UTC
    Wow, that's the first time i've seen Perl do something truly dumb.

    Anyhow, the origin shouldn't matter. I'm looking for methods that work in all circumstances on the very simple question of "Is this an int or a float?" As such the comparison thing doesn't matter much either, since i doubt it'll store pre-decimal components in a manner that would make them come out as a decimal when retrieved from ram.

    Edit: So why do people vote this down? Can't handle it when people speak their mind about Perl? Did i say something really stupid without realizing? I'm not trolling here, so at least let me know if and when I'm wrong.

      The problem with floating point (and binary floating point and decimal fractions) is not strictly Perl's fault.

      Any calculation in floating point can end up accumulating rounding errors. This will happen in any language.

      Conversion of a decimal fraction to binary floating point introduces a small "representation error" most of the time, because most decimal fractions turn out to be recurring binary ones. Conversion from binary to decimal, for example in print tends to round to some number of decimal digits, so "representation error" is usually hidden. This rounding on output can also mask some rounding errors. Any system that uses binary floaing point will suffer from this issue -- and most do !

      If all you want to know is whether the value you have in your hands has a non-zero fractional part, then $x - int($x) will tell you. But, depending on how the value $x was arrived at, be prepared for this to tell you there is a fraction, despite print $x showing none... for example:

      my $x = (1 - 0.99) * 100 ; print $x, ($x - int($x)) ? " has fraction" : " no fraction", "\n" ;
      ...answering your later question, you may get false positives -- things which don't look as if they have fraction bits may be reported as having some.

      Incidentally int($x) in Perl does not force its result into an integer form. If $x is a very large floating value, so large that there cannot be any fraction bits, it will simply return the original value of $x. If $x is a large floating value, with one or more fraction bits, but an integer part too big for integer form, it will discard the fraction bits but still return a floating point value. You are in no danger of things going wrong (as they would do if int tried to return, say, a 32-bit integer).

        Just want to note that example may be fixed as follows:

        my $x = (1 - 0.99) * 100 ; print $x, ("$x" - int($x)) ? " has fraction" : " no fraction", "\n" ;
        Thanks for the more detailed explanation, i didn't know about the print rounding. As for getting false positives, not a problem at all for my case. :)

      If by "something truly dumb" you mean "equalness might fail due to rounding issues"

      that ain't Perl, it's a function of performing "real" arithmetic using finite precision and is unavoidable (most numbers can't be represented using finite precision).

      Why the down votes? I don't know, but it may be people are assuming you didn't read Why do floating point numeric equality/inequality tests fail mysteriously? and the material linked from that node and said something "truly dumb" in consequence. ;)


      Perl's payment curve coincides with its learning curve.
      A reply falls below the community's threshold of quality. You may see it by logging in.