in reply to Re^3: Weird behavior of int()
in thread Weird behavior of int()

Hence why they don't see anything wrong with int() returning Inf/Nan.

Are you thinking that int($x) should always return an IV ?
It will only ever return an IV if $x, having had its fractional part removed, then fits into an IV.

The int() function does not alter the values of Infs or Nans, and the values of int(NaN) and int(Inf) are not within that IV range - as too, is the case with (eg) int($x) for all $x >  2 ** 64.
In all of those cases, int() returns an NV.

Cheers,
Rob

Replies are listed 'Best First'.
Re^5: Weird behavior of int()
by NERDVANA (Priest) on May 21, 2024 at 20:44 UTC
    Yeah that's sort of what I'm going for. I feel like 'int' ought to have a stronger contract of doing what's written on the label. If it can't convert to an int, it ought to return undef or 0, not just ignore the attempt, under the Principle of Least Astonishment.
      I feel like 'int' ought to have a stronger contract of doing what's written on the label.

      The documentation for int() says that it "Returns the integer portion of EXPR".
      It's using "integer" in the mathematical sense of "whole number", not in the programming sense of "IV".
      It just so happens that with the most commonly used builds of perl (where IV precision >= NV precision) every EXPR that (in numeric context) contains a fractional portion will also truncate to a value that fits into an IV ... so I can see how the confusion might arise.
      But, for example, we don't want int(2 ** 65) to start returning '0' or 'undef' or IV_MAX just because it's too big to fit into an IV.

      If you switch to a perl where IV precision < NV precision (such as perls whose IV size is 4 bytes, or whose NV type is __float128), then you'll encounter lots of cases where EXPR contains values with a fractional portion, and yet has an integer portion that's too big to fit into an IV.
      On those builds, and for those values of EXPR, int(EXPR) will happily and silently return just the "whole number" portion of EXPR as an NV.

      Cheers,
      Rob
        I guess the unspoken implied behavior of a function named "int()" can be interpreted more ways than I expected. When I use "int()" in perl, what I actually wanted was C's cast-to-int or JavaScript's parseInt(), followed by making assumptions that I have an integer safe for any integer purposes.

        Having learned about these edge cases, now I need to go back through all the controllers I've written and fix:

        my $count= int($c->request->params->{count}); $c->detach(HTTP => 422, ["Invalid count"]) if $count < 0 || $count > $limit;
        because passing NaN to the controller would let a non-integer value leak through to the code beyond.
      I feel like 'int' ought to have a stronger contract of doing what's written on the label. If it can't convert to an int, it ought to return undef or 0, not just ignore the attempt

      int doesn't contract to return an IV; it contracts to return the integer portion of the expression given. An infinitely large floating point would convert to an infinitely large integer, so infinity is still the logical conclusion (IMO).

      In my mind, perl's int is equivalent to c's math.h:'trunc', not the int typecast in c.

      #include <math.h> #include <stdio.h> int main() { double x; x = 3.14; printf("trunc(%f) = %f\n", x, trunc(x)); x = -2.718; printf("trunc(%f) = %f\n", x, trunc(x)); x = +INFINITY; printf("trunc(%f) = %f\n", x, trunc(x)); x = -INFINITY; printf("trunc(%f) = %f\n", x, trunc(x)); x = +NAN; printf("trunc(%f) = %f\n", x, trunc(x)); x = -NAN; printf("trunc(%f) = %f\n", x, trunc(x)); }
      output:
      trunc(3.140000) = 3.000000 trunc(-2.718000) = -2.000000 trunc(inf) = inf trunc(-inf) = -inf trunc(nan) = nan trunc(nan) = nan

      The docs for int could be improved to say what it does on those edge cases (the trunc docs that I linked do explicitly define those behaviors), but I personally think that int returns the right thing for those inputs, and for me, it is the Answer of Least Astonishment.

        Your particular trunc might have that behaviour, but trunc general might not.

        The spec simply describes trunc as follows:

        The trunc functions round their argument to the integer value, in floating format, nearest to but no larger in magnitude than the argument.

        The spec simply describes what it returns as follows:

        The trunc functions return the truncated integer value.

        Passing an infinity or a NaN is therefore undefined behaviour. Anything could happen.