# double val to long val conversion issue

- Thread: https://externals.io/message/65740
- Original archive: https://news-web.php.net/php.internals/65740
- Started by Remi Collet on 2013-02-09T15:10:22+00:00
- Messages: 27
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/65740.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #65740 — Remi Collet — 2013-02-09T15:10:22+00:00

URL: https://externals.io/message/65740#65740

About
http://git.php.net/?p=php-src.git;a=commitdiff;h=79956330fe17cfd5f60de456497541b21a89bddf
(For now, I have reverted this fix)

Here some explanations.

LONG_MAX is 9223372036854775807 (0x7fffffffffffffff)
double representation of LONG_MAX is 9223372036854775808

(d > LONG_MAX) is evaluated in double space.
So is false for double which have the same value than (double)LONG_MAX.

So, for (double)LONG_MAX the cast used is
	(long)d

9223372036854775807 on ppc64
9223372036854775808 on x86_64 (gcc without optimization)
9223372036854775807 on x86_64 (gcc -O2)

PHP expected value is 9223372036854775808
(Btw, I don't understand why PHP, build on x86_64, with -O2, gives the
good result, some environment mystery)

Obviously, we could have different result on different platform,
compiler, architecture.

I will be very interested by result on other platform (mac, windows),
compiler (Visual C), architecture.

If we switch to the unsigned cast:
	(long)(unsigned long)d;

The result is always 9223372036854775808 (which is expected)

So my proposal, is to use the unsigned cast for (double)LONG_MAX value.

- if (d > LONG_MAX) {
+ if (d >= LONG_MAX) {

From my tests, this doesn't change anything on x86_64, and improves
ppc64 (same result, so at least 9 unit tests succeed)

Any comments ?

If you agree, I will apply this patch again.

Regards,
Remi

---

## #65741 — Remi Collet — 2013-02-09T15:17:30+00:00

URL: https://externals.io/message/65740#65741 · In reply to: #65740

Le 09/02/2013 16:10, Remi Collet a écrit :

> http://git.php.net/?p=php-src.git;a=commitdiff;h=79956330fe17cfd5f60de456497541b21a89bddf

Also see https://bugs.php.net/bug.php?id=64142

---

## #65742 — Pierre Joye — 2013-02-09T15:47:35+00:00

URL: https://externals.io/message/65740#65742 · In reply to: #65740

hi Remi


> About
> http://git.php.net/?p=php-src.git;a=commitdiff;h=79956330fe17cfd5f60de456497541b21a89bddf
> (For now, I have reverted this fix)
>
> Here some explanations.
>
> LONG_MAX is 9223372036854775807 (0x7fffffffffffffff)
> double representation of LONG_MAX is 9223372036854775808
>
> (d > LONG_MAX) is evaluated in double space.
> So is false for double which have the same value than (double)LONG_MAX.
>
> So, for (double)LONG_MAX the cast used is
>         (long)d
>
> 9223372036854775807 on ppc64
> 9223372036854775808 on x86_64 (gcc without optimization)
> 9223372036854775807 on x86_64 (gcc -O2)
>
> PHP expected value is 9223372036854775808
> (Btw, I don't understand why PHP, build on x86_64, with -O2, gives the
> good result, some environment mystery)
>
> Obviously, we could have different result on different platform,
> compiler, architecture.
>
> I will be very interested by result on other platform (mac, windows),
> compiler (Visual C), architecture.
>
> If we switch to the unsigned cast:
>         (long)(unsigned long)d;

> Any comments ?

IIRC, on windows/visualC, no matter if it is x86 or x64, long is
always 32bits, so it won't change the size of long.


Cheers,
--
Pierre

@pierrejoye

---

## #65744 — Terry Ellison — 2013-02-09T23:02:23+00:00

URL: https://externals.io/message/65740#65744 · In reply to: #65742

> hi Remi
>
> 
> > […]
> IIRC, on windows/visualC, no matter if it is x86 or x64, long is
> always 32bits, so it won't change the size of long.
See http://en.wikipedia.org/wiki/LLP64#64-bit_data_models for a good 
description of this mess.  AFAIK many packages that target both 32 and 
64 bit environments MS and *nix, define explicitly adopt XXX_int32, 
XXX_uint32, XXX_int64, XXX_uint64, ... datatypes and use wrappers to map 
these onto the appropriate visualC / gcc types.  As far as I can see, 
PHP doesn't and seems to use long and int almost interchangeably which 
causes problems as LP64/I32LP64 and LLP64/IL32P64 are very different.  
This is one reason for 64-bit support on Windows being problematic.

It would be good for PHP to have a road map to removed data 
model-specific potholes, say by 5.6 or 5.7.

//Terry

---

## #65746 — Stas Malyshev — 2013-02-10T03:25:15+00:00

URL: https://externals.io/message/65740#65746 · In reply to: #65744

Hi!

> these onto the appropriate visualC / gcc types.  As far as I can see, 
> PHP doesn't and seems to use long and int almost interchangeably which 

PHP indeed does not use fixed-size types in zvals, etc. but it
definitely does not "use long and int almost interchangeably". In almost
any place where int is used instead of long or vice versa (unless it is
a specific small value that is nowhere near limits of either int or long
and used in very restricted context) - it is a bug and should be fixed.
If you know of such places, please name them or even better, submit a
bug report pointing them out.
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #65747 — Stas Malyshev — 2013-02-10T04:54:49+00:00

URL: https://externals.io/message/65740#65747 · In reply to: #65740

Hi!

> About
> http://git.php.net/?p=php-src.git;a=commitdiff;h=79956330fe17cfd5f60de456497541b21a89bddf
> (For now, I have reverted this fix)
> 
> Here some explanations.
> 
> LONG_MAX is 9223372036854775807 (0x7fffffffffffffff)
> double representation of LONG_MAX is 9223372036854775808

I see what's going on here. We have precision loss due to the fact that
the range of double (53 bits) is smaller than the range of long (63
bits) on 64-bit system. When it's converted back to long, I guess it has
to be expanded back to 63 bits.

> 9223372036854775807 on ppc64
> 9223372036854775808 on x86_64 (gcc without optimization)
> 9223372036854775807 on x86_64 (gcc -O2)
> 
> PHP expected value is 9223372036854775808

Not sure how value of long can be expected to be 9223372036854775808 -
9223372036854775808 is not representable in signed long. If you do
(long)(unsigned long), you'd get -9223372036854775808.

> (Btw, I don't understand why PHP, build on x86_64, with -O2, gives the
> good result, some environment mystery)

With -O2, gcc probably pre-calculates the result of the operation if you
use simple test program. So, if I write:

double b = LONG_MAX;
printf("%ld", (long)b);

Then without -O2, I'm getting:

        movsd   -16(%rbp), %xmm0
        cvttsd2siq      %xmm0, %rsi

But with -O2, it's just:

        movabsq $9223372036854775807, %rsi

So the difference in results may stem from the fact that the
calculations go in different way here.

> Obviously, we could have different result on different platform,
> compiler, architecture.
> 
> I will be very interested by result on other platform (mac, windows),
> compiler (Visual C), architecture.
> 
> If we switch to the unsigned cast:
> 	(long)(unsigned long)d;
> 
> The result is always 9223372036854775808 (which is expected)

Wait, long can not be 9223372036854775808, how the result of long
conversion can be that?

> From my tests, this doesn't change anything on x86_64, and improves

Well, for the value of (double)LONG_MAX the code actually changes
substantially, and if I test this code:

double b = LONG_MAX;
printf("%ld %ld", (long)(unsigned long)b, (long)b);

Then with -O2 I get different results:

-9223372036854775808 9223372036854775807

But that may be an optimization artifact. With real PHP, the result of
converting 9223372036854775807 to double and then back to int seems to
always be -9223372036854775808 on Intel, with or without the patch. I
don't have access to ppc64 machine so no idea what's going on there.
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #65748 — Terry Ellison — 2013-02-10T06:32:44+00:00

URL: https://externals.io/message/65740#65748 · In reply to: #65746

>
> > […]
> PHP indeed does not use fixed-size types in zvals, etc. but it
> definitely does not "use long and int almost interchangeably". In almost
> any place where int is used instead of long or vice versa (unless it is
> a specific small value that is nowhere near limits of either int or long
> and used in very restricted context) - it is a bug and should be fixed.
> If you know of such places, please name them or even better, submit a
> bug report pointing them out.
Stan, you are right to correct me.  Sorry.  However, I still feel that 
the implicit assumption is that sizeof(long) == 2*sizeof(int) and this 
isn't the case with visualC, and PHP internal data structures compiled 
with visualC and gcc are significantly different; for example hash keys 
are 32 bits long on Windows and 64bits on *nix.  Why aren't they 32bits, 
say, on both? (as there is no performance benefit is having 64bit hash 
keys when the maximum size of a hash table is an int).

---

## #65749 — Stas Malyshev — 2013-02-10T06:50:29+00:00

URL: https://externals.io/message/65740#65749 · In reply to: #65748

Hi!

> Stan, you are right to correct me.  Sorry.  However, I still feel that
> the implicit assumption is that sizeof(long) == 2*sizeof(int) and this

I'm not sure where this assumption exists. Could you be more specific?
PHP uses long for most integer values, and int for some internal things
and sometimes those intersect, but usually there is either conversion or
we can be pretty sure the value, while being nominally long, is in range
of int (such as when it is an option with fixed set of values, etc.).
However, there were certainly bugs in the past in this area, and may be
still some, so eliminating them of course would be good. But right know
I don't know of any code that makes this specific assumption.

> isn't the case with visualC, and PHP internal data structures compiled
> with visualC and gcc are significantly different; for example hash keys
> are 32 bits long on Windows and 64bits on *nix.  Why aren't they 32bits,

Yes, they are different, because long size is different, and PHP uses
long (more specific, ulong) to store hash values. This is because
numeric values are long, and it's easier to use the same type for both
than bother with converting back and forth. As you noted, the difference
for hashing is minimal since the value is anyway brought to hash size,
and hashes of size more than 32-bit LONG_MAX aren't very practical.
However, it matters in other parts of the code.
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #65750 — Remi Collet — 2013-02-10T07:09:04+00:00

URL: https://externals.io/message/65740#65750 · In reply to: #65747

Le 10/02/2013 05:54, Stas Malyshev a écrit :
> Hi!
> 
> > […]
> 
> I see what's going on here. We have precision loss due to the fact that
> the range of double (53 bits) is smaller than the range of long (63
> bits) on 64-bit system. When it's converted back to long, I guess it has
> to be expanded back to 63 bits.
> 
> > […]
> 
> Not sure how value of long can be expected to be 9223372036854775808 -
> 9223372036854775808 is not representable in signed long. If you do
> (long)(unsigned long), you'd get -9223372036854775808.

Yes; I mean -9223372036854775808 (0x8000000000000000)

(sorry, I have use %lu format instead of %ld in my tests)

> 
> > […]
> 
> With -O2, gcc probably pre-calculates the result of the operation if you
> use simple test program. So, if I write:
> 
> double b = LONG_MAX;
> printf("%ld", (long)b);
> 
> Then without -O2, I'm getting:
> 
>         movsd   -16(%rbp), %xmm0
>         cvttsd2siq      %xmm0, %rsi
> 
> But with -O2, it's just:
> 
>         movabsq $9223372036854775807, %rsi
> 
> So the difference in results may stem from the fact that the
> calculations go in different way here.
> 
> > […]
> 
> Wait, long can not be 9223372036854775808, how the result of long
> conversion can be that?

Yes, -9223372036854775808 (0x8000000000000000)


> > […]
> 
> Well, for the value of (double)LONG_MAX the code actually changes
> substantially, and if I test this code:
> 
> double b = LONG_MAX;
> printf("%ld %ld", (long)(unsigned long)b, (long)b);
> 
> Then with -O2 I get different results:
> 
> -9223372036854775808 9223372036854775807
> 
> But that may be an optimization artifact.

Yes I also think.
But is this couldn't occurs in real PHP, with another compiler or
another gcc version ?

> With real PHP, the result of
> converting 9223372036854775807 to double and then back to int seems to
> always be -9223372036854775808 on Intel, with or without the patch. I
> don't have access to ppc64 machine so no idea what's going on there.
> 

That's exactly the problem.
On Intel, we always get  -9223372036854775808 (when 9223372036854775807
could be expected, with -O2, from you previous test)

On ppc64, we always get 9223372036854775807.

This is exactly what I'd like to fix.

And, as you said, the patch doesn't change result on Intel.

I could make this change conditionnal on PPC, but I think it will also
protect us from any compiler optimization change.

Remi

---

## #65751 — Remi Collet — 2013-02-10T07:52:37+00:00

URL: https://externals.io/message/65740#65751 · In reply to: #65750

One more test (to get rid of compiler optimization)

int main (int argc, char *argv[]) {
	double d;
	
	if (argc>1 && sscanf(argv[1], "%lg", &d)) {
		printf("  double=%.20lg\n", d);		
		printf("  signed=%lx\n", (long)d);
		printf("unsigned=%lx\n", (unsigned long)d);
	}
	return 0;
}

On x86_64 (so, with or without -O2)


$ gcc -O2 -Wall math2.c -o math2 && ./math2 9223372036854775807
  double=9223372036854775808
  signed=8000000000000000
unsigned=8000000000000000


On ppc64

$ gcc -O2 -Wall math2.c -o math2 && ./math2 9223372036854775807
  double=9223372036854775808
  signed=0x7fffffffffffff
unsigned=8000000000000000


Remi.

---

## #65752 — Stas Malyshev — 2013-02-10T08:35:41+00:00

URL: https://externals.io/message/65740#65752 · In reply to: #65751

Hi!

> On x86_64 (so, with or without -O2)
> 
> 
> $ gcc -O2 -Wall math2.c -o math2 && ./math2 9223372036854775807
>   double=9223372036854775808
>   signed=8000000000000000
> unsigned=8000000000000000
> 
> 
> On ppc64
> 
> $ gcc -O2 -Wall math2.c -o math2 && ./math2 9223372036854775807
>   double=9223372036854775808
>   signed=0x7fffffffffffff
> unsigned=8000000000000000

So, looks like on ppc64 double->long conversion works differently than
on x86_64. Maybe best solution would be to make the patch with ifdef for
ppc64 so that it would do the same as Intel... Though I'm not sure if
somebody on this platform would expect PHP behave as C does, or as Intel
counterpart does. I'd say I  personally probably would prefer
Intel-compatible behavior since most software would be expected to be
tested on Intel. But formally I'm not sure it's even wrong behavior...
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #65753 — Pierre Joye — 2013-02-10T08:52:11+00:00

URL: https://externals.io/message/65740#65753 · In reply to: #65744

hi Terry.


> 
>
> hi Remi
>
> 
>
> About
> http://git.php.net/?p=php-src.git;a=commitdiff;h=79956330fe17cfd5f60de456497541b21a89bddf
> (For now, I have reverted this fix)
>
> Here some explanations.
>
> LONG_MAX is 9223372036854775807 (0x7fffffffffffffff)
> double representation of LONG_MAX is 9223372036854775808
>
> (d > LONG_MAX) is evaluated in double space.
> So is false for double which have the same value than (double)LONG_MAX.
>
> So, for (double)LONG_MAX the cast used is
>         (long)d
>
> 9223372036854775807 on ppc64
> 9223372036854775808 on x86_64 (gcc without optimization)
> 9223372036854775807 on x86_64 (gcc -O2)
>
> PHP expected value is 9223372036854775808
> (Btw, I don't understand why PHP, build on x86_64, with -O2, gives the
> good result, some environment mystery)
>
> Obviously, we could have different result on different platform,
> compiler, architecture.
>
> I will be very interested by result on other platform (mac, windows),
> compiler (Visual C), architecture.
>
> If we switch to the unsigned cast:
>         (long)(unsigned long)d;
>
> Any comments ?

> IIRC, on windows/visualC, no matter if it is x86 or x64, long is
> always 32bits, so it won't change the size of long.

Yes, that's the case, see my other reply in this thread.

> It would be good for PHP to have a road map to removed data model-specific
> potholes, say by 5.6 or 5.7.

I do not think this change is small enough to be done in 5.x but
definitively in the next major. We have to keep in mind the impacts of
such a change on the code base and all external projects, be
extensions or libraries used by PHP.

In addition, buffers size should use (u)size_t and not int32/64.

Cheers,
-- 
Pierre

@pierrejoye

---

## #65754 — Gustavo Lopes — 2013-02-10T11:00:01+00:00

URL: https://externals.io/message/65740#65754 · In reply to: #65752

On Sun, 10 Feb 2013 09:35:41 +0100, Stas Malyshev <smalyshev@sugarcrm.com>  
wrote:

> So, looks like on ppc64 double->long conversion works differently than
> on x86_64. Maybe best solution would be to make the patch with ifdef for
> ppc64 so that it would do the same as Intel... Though I'm not sure if
> somebody on this platform would expect PHP behave as C does, or as Intel
> counterpart does. I'd say I  personally probably would prefer
> Intel-compatible behavior since most software would be expected to be
> tested on Intel. But formally I'm not sure it's even wrong behavior...

I don't think the ifdef is needed at all. Like I said in the bug report,  
I'm pretty sure that the patch is correct.

The idea of casting first to unsigned long when d > (double)LONG_MAX is to  
use the extra range of unsigned long; if the double is within unsigned  
long, the result is well-defined. But that test has a hidden assumption:  
if d == (double)LONG_MAX, then we can cast to (long) because d is within  
the long range. This assumption is wrong because (double)LONG_MAX is  
larger than LONG_MAX -- the value 9223372036854775808.0 is the closest  
double to 9223372036854775807. Therefore, changing the test to >= seems to  
be the correct course of action.

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.

-- 
Gustavo Lopes

---

## #65755 — Terry Ellison — 2013-02-10T12:25:03+00:00

URL: https://externals.io/message/65740#65755 · In reply to: #65749

> > […]
> Yes, they are different, because long size is different,
This my point: programmers from the *nix world tend to assume that longs 
are longer than ints.  In the MS world they are synonymous. This is one 
reason why code that has been developed in the *nix can be difficult to 
get working reliably in the MS world.
> and PHP uses
> long (more specific, ulong) to store hash values. This is because
> numeric values are long,
I don't follow this reasoning.  "Numeric value" in this context is a PHP 
(application space) concept.  Hashes are internal to the Zend EE and are 
never exposed to the PHP programmer, so this is a case of comparing 
apples and pears.  All that having a 64-bit hash does (when used % 
nTableSize which smaller than maxint) is to add 8 bytes to every Bucket 
entry stored by the EE for no practical benefit.

(It's 8 bytes because ulong h; uint nKeyLength; <long boundary> takes 16 
bytes but uint32 h; uint nKeyLength; <long boundary> takes 8 because of 
data alignment.)

> and it's easier to use the same type for both
> than bother with converting back and forth. As you noted, the difference
> for hashing is minimal since the value is anyway brought to hash size,
> and hashes of size more than 32-bit LONG_MAX aren't very practical.
> However, it matters in other parts of the code.

---

## #65756 — Gustavo Lopes — 2013-02-10T14:58:28+00:00

URL: https://externals.io/message/65740#65756 · In reply to: #65754

On Sun, 10 Feb 2013 12:00:01 +0100, Gustavo Lopes <glopes@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

---

## #65757 — Remi Collet — 2013-02-10T15:19:46+00:00

URL: https://externals.io/message/65740#65757 · In reply to: #65756

Le 10/02/2013 15:58, Gustavo Lopes a écrit :

> 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:

$ gcc -O3 -lm conv.c && ./a.out 9223372036854775808
-9223372036854775808 -9223372036854775808

$ gcc -O3 -lm conv.c && ./a.out 4e21
-2943463994972700672 -1

$ gcc -O3 -lm conv.c && ./a.out 4e19
3106511852580896768 -1


> 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


Remi.

---

## #65762 — Gustavo Lopes — 2013-02-10T19:27:48+00:00

URL: https://externals.io/message/65740#65762 · In reply to: #65757

On Sun, 10 Feb 2013 16:19:46 +0100, Remi Collet <remi@fedoraproject.org>  
wrote:

> Le 10/02/2013 15:58, Gustavo Lopes a écrit :
>
> > […]
>
> $ gcc -O3 -lm conv.c && ./a.out 9223372036854775808
> -9223372036854775808 -9223372036854775808
>
> $ gcc -O3 -lm conv.c && ./a.out 4e21
> -2943463994972700672 -1
>
> $ gcc -O3 -lm conv.c && ./a.out 4e19
> 3106511852580896768 -1
>
>
> > […]

This was I was afraid. That bug was just the tip of the iceberg. I suggest  
we do change the the > operator to >= like you proposed, but also that we  
add the fmod call. The code I gave earlier had a bug btw, as fmod can give  
a negative number. I changed it to this:

long convert(double d)
{
     double dmod = fmod(d, pow(2., 64.));
     if (dmod < 0) {
         dmod += pow(2., 64.);
     }
     return (long)(unsigned long)dmod;
}

I tested the doubles around -4e21 and it worked fine:

$ ./a.out -4000000000000001048576
2943463994971652096 -9223372036854775808
$ ./a.out -4000000000000000524288
2943463994972176384 -9223372036854775808
$ ./a.out -4000000000000000000000
2943463994972700672 -9223372036854775808
$ ./a.out -3999999999999999475712
2943463994973224960 -9223372036854775808
$ ./a.out -3999999999999998951424
2943463994973749248 -9223372036854775808

In[36]:= c /@ Table[-4.*^21 + i*Ulp[-4.*^21], {i, -2, 2}]

Out[36]= {2943463994971652096, 2943463994972176384, \
2943463994972700672, 2943463994973224960, 2943463994973749248}

Any reservations?

-- 
Gustavo Lopes

---

## #65767 — Remi Collet — 2013-02-11T06:33:16+00:00

URL: https://externals.io/message/65740#65767 · In reply to: #65762

Le 10/02/2013 20:27, Gustavo Lopes a écrit :

> This was I was afraid. That bug was just the tip of the iceberg. I
> suggest we do change the the > operator to >= like you proposed, 

I will do it later today.

> but  also that we add the fmod call. The code I gave earlier had a bug btw,
> as fmod can give a negative number. I changed it to this:
> 
> long convert(double d)
> {
>     double dmod = fmod(d, pow(2., 64.));
>     if (dmod < 0) {
>         dmod += pow(2., 64.);
>     }
>     return (long)(unsigned long)dmod;
> }
> 
> I tested the doubles around -4e21 and it worked fine:

On ppc64,

$ gcc -O2 -Wall conv.c -o conv -lm

$ ./conv -4000000000000001048576
2943463994971652096 -9223372036854775808
$ ./conv  -4000000000000000524288
2943463994972176384 -9223372036854775808
$ ./conv -4000000000000000000000
2943463994972700672 -9223372036854775808
$ ./conv -3999999999999999475712
2943463994973224960 -9223372036854775808
$ ./conv -3999999999999998951424
2943463994973749248 -9223372036854775808

$ ./conv 9223372036854775808
-9223372036854775808 -9223372036854775808
$ ./conv 4e21
-2943463994972700672 -1
$ ./conv 4e19
3106511852580896768 -1

> 
> $ ./a.out -4000000000000001048576
> 2943463994971652096 -9223372036854775808
> $ ./a.out -4000000000000000524288
> 2943463994972176384 -9223372036854775808
> $ ./a.out -4000000000000000000000
> 2943463994972700672 -9223372036854775808
> $ ./a.out -3999999999999999475712
> 2943463994973224960 -9223372036854775808
> $ ./a.out -3999999999999998951424
> 2943463994973749248 -9223372036854775808
> 
> In[36]:= c /@ Table[-4.*^21 + i*Ulp[-4.*^21], {i, -2, 2}]
> 
> Out[36]= {2943463994971652096, 2943463994972176384, \
> 2943463994972700672, 2943463994973224960, 2943463994973749248}
> 
> Any reservations?

For which values ? Outside LONG_MIN .. ULONG_MAX ?

Remi.

---

## #65768 — Stas Malyshev — 2013-02-11T07:05:43+00:00

URL: https://externals.io/message/65740#65768 · In reply to: #65767

Hi!

> > […]
> 
> I will do it later today.

>= may be fine, but I am concerned about fmod change for 5.4. If we do
it, I'm afraid we may change some scenario, so I'd prefer to make it 5.5
only.

-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #65769 — Remi Collet — 2013-02-11T08:43:36+00:00

URL: https://externals.io/message/65740#65769 · In reply to: #65762

Le 10/02/2013 20:27, Gustavo Lopes a écrit :

> Any reservations?

Do we really want/need to change the current behavior.

Cast from double, outside of integer range, result in a fix value.

The only problem is that the value is not the same on all arch.

x86_64 always return 0x8000...

ppc64 returns
 LONG_MAX (0x7fff...) for signed value >= LONG_MAX
 LONG_MIN (0x8000...) for signed value <= LONG_MIN
ULONG_MAX (0xffff...) for unsigned value >= ULONG_MAX

NB. The current fix applied only fix LONG_MAX value.
Cast for >= ULONG_MAX is not fixed.

Note (my "very" personal opinion) I think ppc64 behavior is better as it
preserve the sign of the result... ;)

But Intel seems the "standard"...


Remi.

---

## #65770 — Gustavo Lopes — 2013-02-11T21:57:23+00:00

URL: https://externals.io/message/65740#65770 · In reply to: #65769

On Mon, 11 Feb 2013 09:43:36 +0100, Remi Collet <remi@fedoraproject.org>  
wrote:

> Le 10/02/2013 20:27, Gustavo Lopes a écrit :
>
> > […]
>
> For which values ? Outside LONG_MIN .. ULONG_MAX ?

It should be valid for the whole domain, but for performance reasons it  
would be better to limit it to values at least outside the (LONG_MIN,  
LONG_MAX) interval. Testing also against LONG_MIN has a measurable  
performance hit in my tests (+40%), but that should not matter (on my old  
laptop, you can still run the inlined function 1e9 times per second for  
values in the long range, i.e., it takes around 1 ns).

> Do we really want/need to change the current behavior.
>
> Cast from double, outside of integer range, result in a fix value.
>
> The only problem is that the value is not the same on all arch.

Sure, that is also an option, but since we already have a wrapping  
behavior in the LONG_MAX..ULONG_MAX range, it would make sense to keep a  
similar behavior throughout the double domain, I think. It would be  
inconsistent to have it wrap on LONG_MAX, but for values smaller than  
LONG_MIN or larger than ULONG_MAX give a fixed value.

-- 
Gustavo Lopes

---

## #65771 — Remi Collet — 2013-02-12T11:19:47+00:00

URL: https://externals.io/message/65740#65771 · In reply to: #65770

If you find some interests on looking at the remaining ppc64 issues,
I have uploaded the 16 failed tests results

http://blog.famillecollet.com/public/reports/failures-5.4.11-ppc64.tgz
(this is 5.4.11, but with ppc64 patches applied, 46 failures before)

Remi.

---

## #65904 — Gustavo Lopes — 2013-02-17T23:38:24+00:00

URL: https://externals.io/message/65740#65904 · In reply to: #65768

On Mon, 11 Feb 2013 08:05:43 +0100, Stas Malyshev <smalyshev@sugarcrm.com>  
wrote:

> > […]
>
> > […]
> it, I'm afraid we may change some scenario, so I'd prefer to make it 5.5
> only.

If no one objects, I'll merge this into 5.5 and master:

https://github.com/cataphract/php-src/compare/dval_to_lval


-- 
Gustavo Lopes

---

## #65920 — Stas Malyshev — 2013-02-19T02:07:37+00:00

URL: https://externals.io/message/65740#65920 · In reply to: #65904

Hi!

> If no one objects, I'll merge this into 5.5 and master:
> 
> https://github.com/cataphract/php-src/compare/dval_to_lval

Maybe add UNEXPECTED around the if condition? Since it's marked as
zend_always_inline I imagine it's supposed to be performance-sensitive...

Also, skip comments in tests seem to be wrong:

+if (PHP_INT_SIZE != 4)
 	 6	
+   die("skip for machines with 32-bit longs");

But actually it skips for longs NOT being 32-bit. Same with other test.

Also, I'm not sure I understand what 64-bit test is supposed to return
and why - why negative number is converted to positive one? What exactly
this change is supposed to improve?
-- 
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227

---

## #66008 — Gustavo Lopes — 2013-02-19T23:53:17+00:00

URL: https://externals.io/message/65740#66008 · In reply to: #65920

On Tue, 19 Feb 2013 03:07:37 +0100, Stas Malyshev <smalyshev@sugarcrm.com>  
wrote:

> > […]
>
> Maybe add UNEXPECTED around the if condition? Since it's marked as
> zend_always_inline I imagine it's supposed to be performance-sensitive...

I can add that.

> Also, skip comments in tests seem to be wrong:
>
> +if (PHP_INT_SIZE != 4)
>  	 6	
> +   die("skip for machines with 32-bit longs");
>
> But actually it skips for longs NOT being 32-bit. Same with other test.

run-tests.php strips off the skip and shows only "for machines with  
32-bit". The "skip" is necessary for the test to be skipped.

> Also, I'm not sure I understand what 64-bit test is supposed to return
> and why - why negative number is converted to positive one? What exactly
> this change is supposed to improve?

The least significant bytes are preserved. Take int(-2056257536) and  
int(2943463994971652096):

In[6]:= BitAnd[2^32 - 1, {-2056257536, 2943463994971652096}]

Out[6]= {2238709760, 2238709760}


-- 
Gustavo Lopes

---

Next page: https://externals.io/message/65740.md?page=2
