dec*(), *dec(), base_convert() and negative numbers

php.internals

Matt W

20 years ago
Hi all, I was just updating the dec[bin|hex|oct] functions to convert numbers >= 2^32 since the [bin|hex|oct]dec counterparts were updated way back in PHP 4.1. My question is if negative numbers should be handled any differently. Now negative longs are converted to unsigned which gives a weird result for regular users IMO. The *dec() functions ignore negative strings. Because of the way base_convert() works, if you pass a negative number (gets converted to string), the result will be from the absolute value instead of what you get with dec*(). *) Should the dec*() functions continue to convert negative longs to unsigned and use absolute value for doubles? *) Should absolute value always be used, like with base_convert()? *) Should everything be changed to support negative numbers going both directions (to or from base)? Thanks, Matt

Matt W

20 years ago
Hi again, I realize I might not get any answer about updating these little ol' functions :-/, but it'd be nice to hear something one way or the other so I can submit an acceptable patch with correct behavior. After thinking about it more, are negative numbers even "officially supported" with the dec[bin|hex|oct] functions? You won't get anything close to the original back with *dec(dec*(-123)). Since base_convert() uses the number's absolute value, I'm thinking the only reason for using "unsigned long" for the dec*() functions is to handle cases where a PHP double is between LONG_MAX and ULONG_MAX. If so, I think absolute value can be used everywhere. Or again, should I update the functions to correctly accept and return negative values? But I'm not sure if negative bin/hex/oct strings are desired (semantically). Thanks for any simple guidance! Matt ----- Original Message ----- From: "Matt W" Sent: Wednesday, July 26, 2006
> Hi all, > > I was just updating the dec[bin|hex|oct] functions to convert numbers >= > 2^32 since the [bin|hex|oct]dec counterparts were updated way back in PHP > 4.1. > > My question is if negative numbers should be handled any differently. Now > negative longs are converted to unsigned which gives a weird result for > regular users IMO. The *dec() functions ignore negative strings. Because > of the way base_convert() works, if you pass a negative number (gets > converted to string), the result will be from the absolute value instead
of

Michael Wallner

20 years ago
Matt W wrote:
> Hi again, >
[...] I don't see what you're trying to fix. $ cli -r 'var_dump((int)hexdec(dechex(-123)), (int)(float)sprintf("%u",-123), (int)0xffffff85);' int(-123) int(-123) int(-123) Regards,
-- Michael

Matt W

20 years ago
Hi Michael, ----- Original Message ----- From: "Michael Wallner"
> Matt W wrote: > > Hi again, > > > [...] > > I don't see what you're trying to fix. > > $ cli -r 'var_dump((int)hexdec(dechex(-123)),
(int)(float)sprintf("%u",-123), (int)0xffffff85);'
> int(-123) > int(-123) > int(-123)
The main "fix" (upgrade) is to let the dec*() functions convert numbers >= 2^32. But I'm *wondering* whether *this* should be changed: C:\math-old>php -r "var_dump(hexdec(dechex(-123)));" float(4294967173) Your (int) typecast changed that of course. If the dec*() functions handle numbers whose absolute value is >= 2^32, how is a negative double supposed to be handled? Absolute value, I assume. But that's different than negative int handling now (internal long converted to unsigned long). If double's were to be the same way, and there was an unsigned double type, passing -5000000000 would result in an insanely huge number after being made "unsigned" internally like now. That's why I'm assuming negative numbers aren't "really" supported now (not in any form with base_convert()), but it's simply a side effect of wanting to handle *positive* numbers between LONG_MAX and ULONG_MAX. e.g. when doing dechex(4294967173) that's a PHP double, but it gets converted to long (becomes -123), and finally unsigned long (recovering 4294967173). IOW, after convert_to_long() in dec*() it doesn't know if -123 or 4294967173 was passed. Make sense? (Actually, it doesn't, that's my point. :-)) So after more thinking, I figure when upgrading dec*() to handle any number, absolute value should be used (then int(123) would be returned in my example). Unless the all the functions are changed to properly accept/return negatives... I personally don't care either way on that.
> Regards, > -- > Michael
Matt

Michael Wallner

20 years ago
Matt W wrote:
> That's why I'm assuming negative numbers aren't "really" supported > now (not in any form with base_convert()), but it's simply a side > effect of wanting to handle *positive* numbers between LONG_MAX and > ULONG_MAX. e.g. when doing dechex(4294967173) that's a PHP double, > but it gets converted to long (becomes -123), and finally unsigned > long (recovering 4294967173). IOW, after convert_to_long() in dec*() > it doesn't know if -123 or 4294967173 was passed. Make sense? > (Actually, it doesn't, that's my point. :-))
For your computer 0xffffff85 is -123 and 4294967173. It's just how you look on it.
> So after more thinking, I figure when upgrading dec*() to handle any > number, absolute value should be used (then int(123) would be > returned in my example). Unless the all the functions are changed to > properly accept/return negatives... I personally don't care either > way on that.
No way. Why should hexdec(dechex(-123)) return +123?
-- Michael

Matt W

20 years ago
Hi Michael, ----- Original Message ----- From: "Michael Wallner" Sent: Thursday, July 27, 2006
> Matt W wrote: > > > That's why I'm assuming negative numbers aren't "really" supported > > now (not in any form with base_convert()), but it's simply a side > > effect of wanting to handle *positive* numbers between LONG_MAX and > > ULONG_MAX. e.g. when doing dechex(4294967173) that's a PHP double, > > but it gets converted to long (becomes -123), and finally unsigned > > long (recovering 4294967173). IOW, after convert_to_long() in dec*() > > it doesn't know if -123 or 4294967173 was passed. Make sense? > > (Actually, it doesn't, that's my point. :-)) > > For your computer 0xffffff85 is -123 and 4294967173. It's just how > you look on it.
Oh, I know. But that still doesn't answer the question of how to handle negative *double's*.
> > So after more thinking, I figure when upgrading dec*() to handle any > > number, absolute value should be used (then int(123) would be > > returned in my example). Unless the all the functions are changed to > > properly accept/return negatives... I personally don't care either > > way on that. > > No way. Why should hexdec(dechex(-123)) return +123?
Because it makes more sense to users than 4294967173? :-) They don't care what's stored as 0xffffff85, blah blah, just what they actually see. Oh, and doing the same to/from using -123 and base_convert(), you'll get +123! IMO, it was *wrong* to convert a negative to unsigned long. Absolute value should've been used like with base_convert (though that may be by chance rather than design :-)). Of course, it could all be very consistent/logical, to me, by accepting/returning negatives. But what are the thoughts on negative base 2-36 strings? No-no's?
> -- > Michael
I appreciate your replies. I'll be gone for a while now, so I won't be bothering you with another e-mail. :-P Matt

Matt W

20 years ago
Hi, More thoughts... I forgot to say last time that the manual doesn't mention the dec*() parameter being treated as unsigned. The *printf() specifiers b/o/x/X already do this (although the manual doesn't say that either, only for %u). After looking through the comments for dechex(), there's another thing: 32- vs 64-bit. On a 64-bit system, I think dechex(-123) would currently return ffffffffffffff85, which when reversed on my system gives php -d precision=20 -r "var_dump(hexdec('ffffffffffffff85'));" float(18446744073709552000) Are you saying, Michael, that the negative behavior should be left as-is for people to see "how the number is stored by the computer?" I think just using the *printf() specifiers would better. IMO, the dec*() and *dec() functions should work the same as the corresponding to/from base with base_convert() (absolute value), and vice-versa. BTW, can base_convert() simply return an actual number instead of a string when tobase=10? Again, to be the same as *dec() and avoid conversion to string if it's just going to be used in numeric context. Matt ----- Original Message ----- From: "Michael Wallner" Sent: Thursday, July 27, 2006

Matt W

20 years ago
Hi again, OK, after my last message, I found when the dec*() functions started converting to unsigned long (Bug #6761). To make dechex(~8) etc. work. Now I know that behavior can't change. :-) Hopefully this is my last e-mail about this, if I can please find out about 2 simple things: 1) How should a negative *double*, whether it's < LONG_MIN or typecast (double) -123, be handled? *) Continue converting to long/unsigned long, like now? *) Use absolute value? (Only if double!) Either way, base_convert() should continue to use absolute value for all numbers, I assume. 2) CAN base_convert() be changed to return an actual number instead of a string if tobase=10? Seems a waste to convert a base 10 result to string like now... :-? Thanks! Matt ----- Original Message ----- From: "Matt W" Sent: Friday, July 28, 2006
> Hi, > > More thoughts... > > I forgot to say last time that the manual doesn't mention the dec*() > parameter being treated as unsigned. The *printf() specifiers b/o/x/X > already do this (although the manual doesn't say that either, only for
%u).
> > After looking through the comments for dechex(), there's another thing:
32-
> vs 64-bit. On a 64-bit system, I think dechex(-123) would currently
return
> ffffffffffffff85, which when reversed on my system gives > > php -d precision=20 -r "var_dump(hexdec('ffffffffffffff85'));" > float(18446744073709552000) > > Are you saying, Michael, that the negative behavior should be left as-is
for