On Sun, 10 Feb 2013 12:00:01 +0100, Gustavo Lopes <glo...@nebm.ist.utl.pt> wrote:

My concern is that we may also have different behavior when we go outside the unsigned long range.

As to whether ppc64 or x86_64 is correct when casting (double)LONG_MAX to a signed long, the answer is this that since the double is outside long range, the behavior is undefined. If we want more predictability, we have to do the conversion ourselves and probably take a small performance hit.

If we want to eliminate the undefined behavior, we should do (on archs with 64-bit longs):

(long)(unsigned long)(fmod(d, pow(2., 64.)))

Can you test this program on ppc64:

#include <stdio.h>
#include <math.h>

long convert(double d)
{
    return (long)(unsigned long)(fmod(d, pow(2., 64.)));
}

int main(int argc, char *argv[])
{
    double d;

    if (argc > 1 && sscanf(argv[1], "%lg", &d)) {
        printf("%ld %ld\n", convert(d), (long)(unsigned long)d);
    }
    return 0;
}

I get this:
$ gcc -O3 -lm conv.c && ./a.out 9223372036854775808
-9223372036854775808 -9223372036854775808
$ gcc -O3 -lm conv.c && ./a.out 4e21
-2943463994972700672 0
$ gcc -O3 -lm conv.c && ./a.out 4e19
3106511852580896768 0

Which seems correct when verified with mathematica:

In[9]:= c =
  Function[o,
   o // N // SetPrecision[#, \[Infinity]] & // Mod[#, 2^64] & //
    Piecewise[{{#, # < 2^63}}, -2^64 + #] &];

In[10]:= c /@ {2^63, 4*^21, 4*^19}

Out[10]= {-9223372036854775808, -2943463994972700672, \
3106511852580896768}

The cast to long from unsigned long is still implementation-defined behavior, but all twos complement archs should work like this (i.e. don't change the representation). In any case, it's not undefined behavior.

--
Gustavo Lopes

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to