This is a problem with either the IEEE float representation itself, or
with the way Python handles packing and unpacking that representation:
>>> import struct
>>> struct.unpack("d", struct.pack("d", 9.99))
(9.9900000000000002,)
>>> struct.unpack("d", struct.pack("d", 9.99)) == (9.99,)
True
The only way you can guarantee that what you're saving matches exactly
9.99 is to convert your Decimal instance to a string or to a Binary
representation that you know will accurately represent it. The problem
with this is that the database will no longer know that it is dealing
with a number, so other clients won't be able to properly handle it
and things like sorting might not work as expected.
The representation that JavaScript is using is exactly the same. You
can prove this by saving 9.99 in the Python driver and querying in
JavaScript, and vice-versa. The reason it looks like JavaScript is
handling this better is because it is doing some rounding when
displaying as a string. Python does the same thing when you explicitly
convert to a string, actually:
>>> 9.99
9.9900000000000002
>>> str(9.99)
'9.99'
So the difference is really only in how things are being displayed -
the underlying representations are the same in both languages.
> I understand that the issue lies with Python. It's just annoying that
> I'll have to work around this with the use of strings and casts or
> (ints and division) in the various languages I'm working with, whereas
> when working with MySQL we don't have to worry since the Python ORM we
> use (Storm) converts to/from Decimal() automatically.
Just took a quick look at the Storm code - they convert Decimal
instances to strings, as suggested previously. This is something I
don't want the driver to do automatically because there would be no
way of knowing whether a string in a result document should be treated
as a regular string or as a Decimal instance. Should be easy to add as
part of a higher layer like MongoKit though - something where you know
which fields are which types.