[Tutor] Convert doesn't work... I'm stumped

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Wed Mar 16 09:32:50 CET 2005



On Tue, 15 Mar 2005, Jacob S. wrote:

> Okay, not a very descriptive subject, but here goes...
>
> This is the code

Hi Jacob,



> from decimal import Decimal as D

Ok, I think I see why you're using this, but you have to be aware that the
decimal module itself is susceptible to imprecision:

    http://www.python.org/doc/lib/decimal-notes.html

so, even with the decimal module, we still run the potential for roundoff
errors.


I think you may be looking for something like a "rational" module.
There's an unofficial one that comes with the Python source code, in the
Demo/classes directory as 'rat.py'.  Let me see if I can get a link to
it... ok, here it is:

http://cvs.sourceforge.net/viewcvs.py/*checkout*/python/python/dist/src/Demo/classes/Rat.py

You may find this module more useful.  I really hope something like this
migrates into the Standard Library some day... *grin*



Let's take a look at some other parts of the code.

> dic = {## Metric ##
>        'm':(lambda x:x,lambda x:x),
>        'km':(lambda x:1000*x,lambda x:x/1000),
>        'cm':(lambda x:x/100,lambda x:100*x),
>        'mm':(lambda x:x/1000,lambda x:1000*x),


The lambdas might be slightly scary to some folks.  I'm usually for
lambdas, but this seems a bit too much.  *grin*



Another way you can write this is just to store the constants.  And,
although the 'dic' above stores pairs of ratios, you might be able to live
with just one of them.

A table that stores a similar amount of information might be something
like this:

###
meterRatios = { 'm'  : D(1),           ##     1 meter  == 1 meter
                'km' : D(1000),        ##  1000 meters == 1 kilometer
                'cm' : D(1)/D(100),    ##  .001 meters == 1 centimeter
                'mm' : D(1)/D(1000),   ## .0001 meters == 1 millimeter
                 }
###

The reason this stores equivalent data is because it stores the ratio of
one meter to the unit in question.  I'm being perhaps a little too
paranoid in building decimals by calling D() n both the numerator and
denominator, but oh well.  *grin*

We can also do it without explicitely defining 'from meter' stuff, because
the ratios that define going "to meters" and going "from meters" are
inverses of each other.  That is, from the table above, we can see that
since there's one meter to a thousanth of a millimeter, we can go the
other way and say that one thousand millimeters is one meter.  (1/x)


Here's one way to write a fromMeterToUnit function:

###
def inverse(number):
    """Returns 1 / number."""
    return D(1) / number

def fromMeterToUnit(meters, unit):
    """Converts meters to a specific unit."""
    return meters * inverse(meterRatios[unit])
###



Let's just double check to see that this works:

###
>>> fromMeterToUnit(10, 'm')
Decimal("10")
>>> fromMeterToUnit(10,  'km')
Decimal("0.010")
>>> fromMeterToUnit(10,  'cm')
Decimal("1.0E+3")
>>> fromMeterToUnit(10,  'mm')
Decimal("1.0E+4")
###

Ok, looks good so far.  And a similar definition for a 'fromUnitToMeter'
isn't too much more work, and can reuse the same numbers in the table.

(By the way, several other posters have mentioned that "unit tests" would
be a really good idea to have.  Do you know about them already?  I'm sure
many of us on the list can show examples and help you learn them if you'd
like.)

The big win here is that we don't have to repeat the same numbers: we want
to avoid the chance of accidently mistyping a ratio --- we want to reduce
redundancy in data if we can.  And if we have to correct some numbers
later on, it's nice if we can correct it just once, and not in several
places.

I think that Javier Ruere found the bug in one of the ratios, but I wanted
to comment on the code's style, so that the main ideas of the converter
stand out clearly.


Best of wishes to you!






More information about the Tutor mailing list