[CentralOH] Better Problem Description.

Brian Costlow brian.costlow at gmail.com
Mon Dec 9 21:43:20 CET 2013


Here is a sort-of pythonic implementation of your algorithm. There's some
non idiomatic stuff here because I think it's easier for a new Python
person to follow. It takes a string which contains a representation of a
decimal number as input. This will only give correct output if the number
has four places to the right of the decimal. It will also have issues if
the string passed in is not a number. And one more thing...read on

# Used as a simple hash to lookup new last digit
ROUNDING_LOOKUP = { '0':'0', '1':'0', '2':'0', '3':'5', '4':'5',
    '5':'5', '6':'5', '7':'5', '8':'0', '9':'0', }

def bogdan_round(num_as_string):
    '''Round to 0.0005 step

    Input must be a string representing a decimal number, with
    four places to right of decimal. Anything else WILL break.
    Negative values WILL NOT move in correct direction'''

    # part that won't change, uses slice notation, given 1.2345 as input
becomes 1.23
    prefix = num_as_string[:-2]
    # given 1.2345 becomes 4
    next_to_last_digit = num_as_string[-2:-1]
    # given 1.2345 becomes 5
    last_digit = num_as_stringx[-1:]
    next_to_last_as_int = int(next_to_last_digit)

    if next_to_last_as_int > 7:
        if next_to_last_as_int == 9:
            next_to_last_digit = '0'
            next_to_last_digit = str(next_to_last_as_int + 1)
    return prefix + next_to_last_digit + ROUNDING_LOOKUP[last_digit]

Except there is a huge bug here.

1.2346 returns 1.2345
1.2349 returns 1.2350
1.2399 returns 1.2300 NOT the correct 1.2400 which is what happens when you
try to play with isolated digits as a string.

To fix that requires traversing the rest of the string in a loop adjusting
digits. It's better to do math. Returning to my earlier solution, with a
couple of tweaks.

All computers have imprecision when doing floating point math, because of
precision limitations moving between decimal and the actual binary
implementation of the numbers. This is not unique to Python, but any
language that abstracts the idea of a float and targets multiple

Python has a Decimal library that allows you to do fixed point decimal
math, or do floating point that will notify you if precision is lost. This
does the same as the above, plus it handles ints and floats as input,
negative values:

# Use the decimal lib
from decimal import Decimal as D

# Since we are rounding to closest 0.0005, we can't just use the quant
# but we have to multiply so we can do integer rounding then divide back
MULTIPLIER = D('2000')

# Number can be int, string, or in Python 2.7 or >, a float
def custom_round(number):
    number = D(number) # create a decimal type
    # multiply so we can round to nearest int
    adjusted_number = number * MULTIPLIER
    # round, divide back, and return result in one step
    return adjusted_number.to_integral_value() / MULTIPLIER

Here's the function in action, showing the result:

>>> custom_round('1.2340')
>>> custom_round('1.2341')
>>> custom_round('1.2342')
>>> custom_round('1.2343')
>>> custom_round('1.2344')
>>> custom_round('1.2345')
>>> custom_round('1.2346')
>>> custom_round('1.2347')
>>> custom_round('1.2348')
>>> custom_round('1.2349')

On Mon, Dec 9, 2013 at 12:56 PM, Louis Bogdan <looiebwv at gmail.com> wrote:

> First off Brian, thank you for your interest in helping me.  So let's
> start from the beginning.  The number comes from a keyboard input that is
> requested with 4 decimal input and is modified with an imbedded formula.
> In this application the keyboard number can vary from integer to integer
> plus 4 decimal.  This modified number, "num", is used to control a stepper
> motor where each step equals 0.0005" of linear motion.  My function rounds
> "num" in such a manner that the maximum error is 0.0002", which in this
> application is acceptable.  The initial number may be modified as much as
> 30 times in a cycle.  These modifications are so handled that in
> engineering parlance I "baseline" dimension rather that "chain" so that
> each increment is within 0.0002" of true location rather than a possible
> maximum accumulative error of 0.0066".  All that I can and have handled.
> What I need is to put my "rounding" function into Python language. One
> comment I received was to use only integers.  That would be no problem, we
> would adjust the units and tens digit as necessary and accomplish the same
> thing. It would be: motor steps equals num(decimal)/0.0005 or
> num(integer)/5 as I have shown in one of my emails.  As far as the comment
> of "other" accuracies can be totally ignored in the present discussion.
> Now I know that Python does rounding which I don't understand and have not
> found any decent, understandable explanation how it does it.  I understand
> rounding, even way back when I was proficient with the abacus.
> I hope this is some claification and if not, let me know.  Lou
> On Mon, Dec 9, 2013 at 10:53 AM, Brian Costlow <brian.costlow at gmail.com>wrote:
>> Hi Louis,
>> I read through all the group postings real quick this morning, and here's
>> my take.
>> What Jim is asking for (he's our resident elder curmudgeon, although in
>> your case he could probably be your grandson.) is to step back and describe
>> the problem in English.
>> You have given us an algorithm, and asked us to help you code it in
>> Python. That algorithm is best encoded in Python via string manipulation,
>> which is not really what you should be doing if you are trying to do math.
>> So, we want a generic description of the problem because we may find a
>> better algorithm entirely.
>> We also need to know where the input is coming from and where it is used
>> after, because if you have floating point values coming in, and going out,
>> then not only is string manipulation 'extra' work, it may bite you.
>> Last, it's unclear whether the 0.0005 step value ever needs to change.
>> Thus Jim's comment about magic numbers. I think your line that said this: "other
>> accuracies can be obtained with positional variations" implies that 0.0005
>> step might change and be some other value. But it's not clear.
>> In my code I sent you earlier, 'custom_round' IS a subroutine (we usually
>> say procedure or function in python-speak) that does exactly what you need.
>> It doesn't do string manipulation, it does real math, but it will accept a
>> string or any numeric type as input.
>> You can also modify it so that the 0.0005 is changeable.There are a
>> couple of design approaches to doing that, and the one you should use
>> partly depends on the rest of your program design.
>> Coding something in any language is not merely figuring a psuedo-code
>> algorithm and then forcing the language to fit your algorithm.
>> It's learning the idioms and built-in tools of the language.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/centraloh/attachments/20131209/88afaaa6/attachment.html>

More information about the CentralOH mailing list