Caculate age

Bengt Richter bokr at oz.net
Tue Feb 4 01:31:33 EST 2003


On Mon, 03 Feb 2003 15:10:54 +0100, Laura Creighton <lac at strakt.com> wrote:

>
>Ai!  I went and looked at the archives at http://mail.python.org/pipermail/python-list/2003-February
>
>and I cannot find my article.  No wonder you want my head.  It is probably
>sitting at home in my lap top, happily waiting for mh to confirm that I
>want to send it or something.  But that doesn't make it any better.
>
>Here is what happened.
>
>It was tired.  I started a reply that used a list, as I was thinking about
>what would Ben want if people input the same dates into his problem.  Then
>I decided that this was a bad idea.  In the meantime I had written 
>'lists of numbers make bad keys for dictionaries'. I changed things 
>removing the lists.  I really botched the comment.
>
>Ben: numbers make fine keys:
but see some caveats below re floating point
numbers, which always come with caveats ;-)
>
>>>> mydict={}
>>>> for i in range(5):
>...     mydict[i] = i
>...
>>>> print mydict.keys()
>[4, 3, 2, 1, 0]
>>>> print mydict.values()
>[4, 3, 2, 1, 0]
>
>but lists do not:
>
>>>> i = [4, 3, 2, 1, 0]
>>>> mydict[i]=i
>Traceback (most recent call last):
>  File "<stdin>", line 1, in ?
>TypeError: unhashable type
>
>Of course, you weren't trying to do that anyway.
>
>I most humbly and abjectly apologise for all the confusion I have caused.
No need to go overboard ;-)

But since I hinted at some funny stuff with number keys, I probably have
some guilt. Maybe I should illustrate what I was referring to for Ben. E.g.,

 >>> mydict={}
 >>> mydict[1.0] = 'one'
 >>> mydict
 {1.0: 'one'}
 >>> mydict[1] = 'integer one'
 >>> mydict
 {1.0: 'integer one'}

Some might be surprised that that wasn't {1: 'integer one'}

 >>> mydict[.1+.9] = 'close to one'
 >>> mydict
 {1.0: 'close to one'}

That turned out to be close enough that the FPU took two inexact values and
produced an exact one when rounding to create the double representation in
memory. But a computed floating point value, while ok as a key, may not be
the exact key one was expecting.

 >>> from math import sqrt
 >>> mydict[sqrt(2.0)**2/2.0] = 'not close enough'
 >>> mydict
 {1.0000000000000002: 'not close enough', 1.0: 'close to one'}

That made a distinct key. If you compute the same way on the same platform
(CPU/FPU, libs, language, version) you can probably expect to get the same
key value and be able to access the same data binding as before.

 >>> mydict[sqrt(2.0)**2/2.0] = 'but repeatable'
 >>> mydict
 {1.0000000000000002: 'but repeatable', 1.0: 'close to one'}

Floating point numbers represented by the hardware, and the shorter
representations in memory, all are distinct bit patterns and represent
exact numerical values. The misleading part is that decimal printed
representations are exact too in the ideal mathematical sense, but there
may be no floating point hardware bit pattern available that represents
the same mathematical value as the decimal representation, so the closest
available one is chosen.

The bottom line is to know what you are doing when you use floating point
equality comparisons, whether you are doing it yourself or implicitly in
a dictionary lookup.

If you start with an integer as key, the key value is preserved also
 >>> mydict[2] = 'two'
 >>> mydict
 {1.0000000000000002: 'but repeatable', 1.0: 'close to one', 2: 'two'}
 >>> mydict[2.0] = 'floating two'
 >>> mydict
 {1.0000000000000002: 'but repeatable', 1.0: 'close to one', 2: 'floating two'}

As it says in the online language reference,
"""
Numeric types used for keys obey the normal rules for numeric comparison:
if two numbers compare equal (e.g., 1 and 1.0) then they can be used
interchangeably to index the same dictionary entry. 
"""

<speculation>
IWT this would create a slight performance hit looking for a match when the
hash value is not there from a previous operation (i.e., do you potentially
have to hash an integer, convert to float and hash that, and maybe convert to
long and hash that and leave a trail of hashes all associated with the original
key? Some interesting implementation issues.) This is compared to strings,
that can't have equivalent values with different hashes AFAIK, unless maybe
there is a unicode equivalency that makes that possible? Hm...

 >>> mydict[u'x'] = 'ux'
 >>> mydict
 {1.0000000000000002: 'but repeatable', 1.0: 'close to one', 2: 'floating two', u'x': 'ux'}
 >>> mydict['x'] = 'x'
 >>> mydict
 {1.0000000000000002: 'but repeatable', 1.0: 'close to one', 2: 'floating two', u'x': 'x'}

It seems to be treated as an equivalent for at least one case. Perhaps it's just
a matter of hashing the underlying binary if ascii maps 1:1, in which case no speed hit.
</speculation>

Someone who actually knows the dict implementation will have to say, since I am too
hungry to dig in the source for this post ;-)

Regards,
Bengt Richter




More information about the Python-list mailing list