Page 1 of 2 12 Last
  • Jump to page:
    #1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    16
    Rep Power
    0

    Question Mixed numbers and decimals?!?


    Okay. I have ran into the first problem in python that I havent been able to solve myself, and I am asking for your help. My problem is that I have created a very simple app that calculates a door cutlist for a cabinet shop. The user inputs the different dimensions of the door and the output tells them what to cut. Now the problem seem to be having is the output doesnt give the correct decimals. If you set the values by hand an not allow for user input it works fine. But using input() seems to throw it out the window.

    Also some of the dimensions need to be mixed numbers like 2 and 1/2 inches or 2 1/2 for the user input. But when I try this it gives me and invalid syntax error.

    My code is as follows:
    Code:
    from __future__ import division
    q = input("  Number of doors: ")
    w = input("            Width: ")
    l = input("           Length: ")
    c = input("        Cut depth: ")
    m = input("Machine clearance: ")
    s = input("      Stile width: ")
    r = input("       Rail width: ")
    sl = l
    rl = w-(2*s)+(2*c)
    pl = sl-(2*r)-(2*c)-(2*m)
    pw = w -(2*s)-(2*c)-(2*m)
    rq = 2*q
    sq = 2*q
    pq = q
    print "Cutlist for: ", q, "doors", w, "x", l
    print ""
    print "     Panels: ", pq, pw, "x", pl
    print "     Stiles: ", sq, s, "x", sl
    print "      Rails: ", rq, r, "x", rl
    Some test inputs for reproduction are:
    Number of doors: 1
    Width: 12
    Length: 12
    Cut Depth: 1/8
    Machine clearance: 1/32
    Stile width: 2.5
    Rail width: 2.5

    That will give you the output with incorrect decimals.
    For the Mixed number error change the 2.5 to 2 1/2


    Any suggestions would be greatly appreciated.
    Last edited by DFarist; March 21st, 2004 at 10:51 PM.
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    London, England
    Posts
    1,585
    Rep Power
    1373
    the problem is that by default the division operator between two integers returns an integer value. This is the same way that languages like C do it, so it has some historical precedent. To get a floating point number, one of the numbers needs to be a float. e.g.

    Code:
    >>> 1/2
    0
    >>> 1.0/2
    0.5
    >>> float(1)/2
    0.5
    >>>
    However the developers of Python realise this is a problem, and are in the process of changing it. They can't do this all in one go since it will break existing code, so from Python 2.2 this behaviour is deprecated. From version 2.4 the division operator on integers will return a floating point number, which is what you want. If you are using 2.2 or 2.3 then you can get this behaviour now by doing "from __future__ import division" at the start of your code:

    Code:
    >>> from __future__ import division
    >>> 1/2
    0.5
    See PEP 238 at http://www.python.org/peps/pep-0238.html for the full gory details.

    This will fix your problem, but will mean your program will fail if run on versions earlier than 2.2. This may or may not be a problem for you, depending on how you distribute it.

    This will also still not allow the user to enter values such as 2 1/2, since that is not a valid Python expression. It would need to be written as 2+1/2 or 2.5.

    Also, if you are going to run this on a server then using 'input' is not safe - the user could enter any Python statements, and take control of your system, delete files etc.

    The alternative is to write your own expression parser, which is a non-trivial task for a novice programmer but would give complete control over how the user's input is interpreted. If you decide to go this route, let us know and I will post some pointers to writing it.

    Regards,

    Dave - The Developers' Coach
  4. #3
  5. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    The problem is a bit more interesting - it seems that (on Python 2.3 at least) the input () function ignores the
    from __future__ import division command statement.
    Code:
    >>> from __future__ import division
    >>> 1/2
    0.5
    >>> input()
    1/2
    0
    >>> a = raw_input()
    1/2
    >>> a
    '1/2'
    >>> eval(a.strip())
    0.5
    However, as you can see above combining raw_input() with eval() seems to avoid the problem.

    Grim
  6. #4
  7. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    My guess - this is an oversight (bug) due the the fact that no one really uses input in code that others will use - because of the security issues.
  8. #5
  9. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    I don't get enough practice with regular expressions but here's my solution:

    It allows input in the form integer, floating point and integer with fraction. The fraction is deliberately constrained to only allow single digit numerators.
    Code:
    from __future__ import division
    import re
    
    imperial = re.compile(r"(\d*\s+\d/\d+)|(\d/\d+)")
    metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")
                        
    def NumericInput(prompt="Input value:"): 
        while True: 
            inp = raw_input(prompt).strip()
            match = imperial.match(inp)
            try: 
                if match: 
                    val = match.group().replace(" ", "+", 1)
                    value = eval(val)
                    break
                else: 
                    match = metric.match(inp)
                    if match: 
                        val = match.group()
                        value = eval(val)
                        break
            except: 
                pass
            print "Input format '1' , '1.5' or '1 1/2', retry..."
        return value
    
    q = NumericInput("  Number of doors: ")
    w = NumericInput("            Width: ")
    l = NumericInput("           Length: ")
    c = NumericInput("        Cut depth: ")
    m = NumericInput("Machine clearance: ")
    s = NumericInput("      Stile width: ")
    r = NumericInput("       Rail width: ")
    sl = l
    rl = w-(2*s)+(2*c)
    pl = sl-(2*r)-(2*c)-(2*m)
    pw = w-(2*s)-(2*c)-(2*m)
    rq = 2*q
    sq = 2*q
    pq = q
    print "Cutlist for: ", q, "doors", w, "x", l
    print ""
    print "     Panels: ", pq, pw, "x", pl
    print "     Stiles: ", sq, s, "x", sl
    print "      Rails: ", rq, r, "x", rl
    I'm sure it can be improved on
    Grim
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    16
    Rep Power
    0
    Grim Archon. That method worked exactly like I wanted it to. Thank you so much for your help. I have one more question though. Is there any way to get the output to also be in fractions and mixed numbers or am I just asking to much in that line? If you get tired of typing all this just let me know where to find out information, because I have been looking forever. Thanks again.
  12. #7
  13. Hello World :)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Mar 2003
    Location
    Hull, UK
    Posts
    2,537
    Rep Power
    69
    My function to handle numberic input, enjoy!

    Code:
    #!/usr/bin/env python
    
    import re
    
    def nuput(prompt = 'Enter a number: '):
    
        #Parses raw input depending on user input; this can be an int, float or
        #Fraction. If an error occurs while parsing then raise a 'TypeError' for
        #error catching purposes.
    
        numbers = []
    
        try:
            for number in raw_input(prompt).split():
    
                    #Loops over each int, float and fraction in the raw_input()
                    #call and checks that the input fits given patterns.
    
                    if len(number) > 1:
                            if re.search('^(\d+/\d+)$', number):
    
                                #Checks if the the string 'number' is a fraction.
                                #Unpacks numbers and assigns the values to 'one'
                                #and 'two'.
    
                                one, two = map(float, number.split('/'))
    
                                #Appends a string of float / float to 'numbers'.
                                numbers.append('%f/%f' % (one, two))
    
                            elif re.search('^(\d*\.\d+)$', number):
                                numbers.append(number)
    
                    elif number.isdigit(): numbers.append(number)
    
            #Returns an evaluted expression of the values passed to nuput().
            return eval('+'.join(numbers))
    
        except:
            raise TypeError, 'Input must be Decimal, Float or Fraction.'
    Spot an error, let me know.

    Mark.
    programming language development: www.netytan.com Hula

  14. #8
  15. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    It was an fun twist on the temperature conversion problem

    This version of the code attempts to match half, 4ths,5ths,8ths,10ths,16ths and 32ths. If it can't then it just prints floating point.
    Code:
    from __future__ import division
    import re
    
    imperial = re.compile(r"(\d*\s+\d/\d+)|(\d/\d+)")
    metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")
    
    #Generate Fractions
    fractions = {}
    for n in range(1, 32): 
        fractions["%0.5f"%(n/32.0)] = "%s/32"%n
    for n in range(1, 16): 
        fractions["%0.5f"%(n/16.0)] = "%s/16"%n
    for n in range(1, 10): 
        fractions["%0.5f"%(n/10.0)] = "%s/10"%n
    for n in range(1, 8): 
        fractions["%0.5f"%(n/8.0)] = "%s/8"%n
    for n in range(1, 5): 
        fractions["%0.5f"%(n/5.0)] = "%s/5"%n
    for n in range(1, 4): 
        fractions["%0.5f"%(n/4.0)] = "%s/4"%n
    for n in range(1, 3): 
        fractions["%0.5f"%(n/3.0)] = "%s/3"%n
    fractions['0.00000'] = ''
    fractions["0.50000"] = "1/2"
    #End of Fractions
    
        
    def NumericInput(prompt="Input value:"): 
        while True: 
            inp = raw_input(prompt).strip()
            match = imperial.match(inp)
            try: 
                if match: 
                    val = match.group().replace(" ", "+", 1)
                    value = eval(val)
                    break
                else: 
                    match = metric.match(inp)
                    if match: 
                        val = match.group()
                        value = eval(val)
                        break
            except: 
                pass
            print "Input format '1' , '1.5' or '1 1/2', retry..."
        return value
    
    def ImpPrint(value): 
        neg = ''
        if value<0: 
            neg = '-'
            value = -value
        integer = int(value)
        remainder = "%0.5f"%(value-integer)
        if fractions.has_key(remainder): 
            return "%s%d %s"%(neg, integer, fractions[remainder])
        else: 
            return "%s%d.%s"%(neg, integer, remainder)
    
        
    q = NumericInput("  Number of doors: ")
    w = NumericInput("            Width: ")
    l = NumericInput("           Length: ")
    c = NumericInput("        Cut depth: ")
    m = NumericInput("Machine clearance: ")
    s = NumericInput("      Stile width: ")
    r = NumericInput("       Rail width: ")
    sl = l
    rl = w-(2*s)+(2*c)
    pl = sl-(2*r)-(2*c)-(2*m)
    pw = w-(2*s)-(2*c)-(2*m)
    rq = 2*q
    sq = 2*q
    pq = q
    print "Cutlist for: %4d doors  %-8s x %-8s"%(q, ImpPrint(w), ImpPrint(l))
    print ""
    print "     Panels: %4d        %-8s x %-8s"%(pq, ImpPrint(pw), ImpPrint(pl))
    print "     Stiles: %4d        %-8s x %-8s"%(sq, ImpPrint(s), ImpPrint(sl))
    print "      Rails: %4d        %-8s x %-8s"%(rq, ImpPrint(r), ImpPrint(rl))
    If you don't like the fractions I chose (do you use 16ths?) then just modify the fraction generator - (the order is important).

    I hope this helps

    Grim
  16. #9
  17. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    Originally Posted by netytan
    My function to handle numberic input, enjoy!

    Code:
    #!/usr/bin/env python
    
    import re
    
    def nuput(prompt='Enter a number: '): 
    
        #Parses raw input depending on user input; this can be an int, float or
        #Fraction. If an error occurs while parsing then raise a 'TypeError' for
        #error catching purposes.
    
        numbers = []
    
        try: 
            for number in raw_input(prompt).split(): 
    
                    #Loops over each int, float and fraction in the raw_input()
                    #call and checks that the input fits given patterns.
    
                    if len(number)>1: 
                            if re.search('^(\d+/\d+)$', number): 
    
                                #Checks if the the string 'number' is a fraction.
                                #Unpacks numbers and assigns the values to 'one'
                                #and 'two'.
    
                                one, two = map(float, number.split('/'))
    
                                #Appends a string of float / float to 'numbers'.
                                numbers.append('%f/%f'%(one, two))
    
                            elif re.search('^(\d*\.\d+)$', number): 
                                numbers.append(number)
    
                    elif number.isdigit(): numbers.append(number)
    
            #Returns an evaluted expression of the values passed to nuput().
            return eval('+'.join(numbers))
    
        except: 
            raise TypeError, 'Input must be Decimal, Float or Fraction.'
    Spot an error, let me know.

    Mark.
    Nice code Netytan -- in colour
    Last edited by netytan; March 22nd, 2004 at 08:01 PM.
  18. #10
  19. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    16
    Rep Power
    0
    Grim, when I ran your code I got an invalid syntax on the

    if value<0:

    It highlights the semi-colon. I am running Python 2.3 if that has anything to do with it.
    Also I hate to ask, but all this stuff looks latin to me. I understand the basic idea of these code snippets but maybe you could help me understand how to do my own. For future reference. Or atleast point me toward some reading material. That would be great.

    Again, thanks for all yalls help.
  20. #11
  21. Hello World :)
    Devshed Frequenter (2500 - 2999 posts)

    Join Date
    Mar 2003
    Location
    Hull, UK
    Posts
    2,537
    Rep Power
    69
    The problem here is that devshed doesnt display html entities, change &gt; with > and &lt; with < and the program should
    work fine .

    Also, a good place to start would be the Python tutorial at Python.org, what exactly are you looking for?

    Mark.
    programming language development: www.netytan.com Hula

  22. #12
  23. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    16
    Rep Power
    0
    Okay, I tried fixing the code like you said but it gave me a semanic error. Anyway, I will just live with the decimal output because I am amazed that we could get the fractions working. As far as the tutorial goes. I know the basics of Python, so the beginners tutorials wont really help that much. I am looking for things that explain the stuff like:
    Code:
    imperial = re.compile(r"(\d*\s+\d/\d+)|(\d/\d+)")
    metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")
    I have no clue what all those 'd's and 's's mean. I can kinda follow but not very ?comprehendably? Other than that I can follow an reproduce yalls code with the exception of some minor syntax errors because of stupid mistakes. The logic is fine.


    Any pointers would be great, but that is what I am mainly confused on.

    Thanks
  24. #13
  25. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    Stupid me - just being too clever (I'm sure Dev Shed keeps changing it's encoding ). Anyway - code that works:
    Code:
    from __future__ import division
    import re
    
    imperial = re.compile(r"(\d*\s+\d/\d+)|(\d/\d+)")
    metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")
    
    fractions = {}
    fractions['0.00000'] = ''
    for frac in [32, 16, 10, 8, 5, 4, 3, 2]: 
        for n in range(1, frac): 
            fractions["%0.5f"%(n/float(frac))] = "%s/%s"%(n, frac)
        
    def NumericInput(prompt="Input value:"): 
        while True: 
            inp = raw_input(prompt).strip()
            match = imperial.match(inp)
            try: 
                if match: 
                    val = match.group().replace(" ", "+", 1)
                    value = eval(val)
                    break
                else: 
                    match = metric.match(inp)
                    if match: 
                        val = match.group()
                        value = eval(val)
                        break
            except: 
                pass
            print "Input format '1' , '1.5' or '1 1/2', retry..."
        return value
    
    def ImpPrint(value): 
        neg = ''
        if value < 0: 
            neg = '-'
            value = -value
        integer = int(value)
        remainder = "%0.5f"%(value-integer)
        if fractions.has_key(remainder): 
            return "%s%d %s"%(neg, integer, fractions[remainder])
        else: 
            return "%s%d.%s"%(neg, integer, remainder)
    
        
    q = NumericInput("  Number of doors: ")
    w = NumericInput("            Width: ")
    l = NumericInput("           Length: ")
    c = NumericInput("        Cut depth: ")
    m = NumericInput("Machine clearance: ")
    s = NumericInput("      Stile width: ")
    r = NumericInput("       Rail width: ")
    sl = l
    rl = w-(2*s)+(2*c)
    pl = sl-(2*r)-(2*c)-(2*m)
    pw = w-(2*s)-(2*c)-(2*m)
    rq = 2*q
    sq = 2*q
    pq = q
    print "Cutlist for: %4d doors  %-8s x %-8s"%(q, ImpPrint(w), ImpPrint(l))
    print ""
    print "     Panels: %4d        %-8s x %-8s"%(pq, ImpPrint(pw), ImpPrint(pl))
    print "     Stiles: %4d        %-8s x %-8s"%(sq, ImpPrint(s), ImpPrint(sl))
    print "      Rails: %4d        %-8s x %-8s"%(rq, ImpPrint(r), ImpPrint(rl))
    Fo help on regular expressions:
    http://www.amk.ca/python/howto/regex/

    Cheers,
    Grim
  26. #14
  27. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    16
    Rep Power
    0
    Okay, sorry for not replying for a while. I have testing at school and I am kinda stressed out. Anywho. I have been playing around with the code and finally came up with one that works like I wanted. It is really sloppy though and I would like advice on how to clean it up.

    Here it is:

    Code:
    from __future__ import division
    import re
    from string import *
    fraction = re.compile(r"(\d*\s\d+/\d+)|(\d+/\d+)")
    metric = re.compile(r"(\d*\.\d+)|(\d+)(?!.*/)")
    def nunput(prompt="Input value:"): 
        inp = raw_input(prompt).strip()
        frac = fraction.match(inp)
        metr = metric.match(inp)
        if frac:
            val = frac.group().replace(" ", "+")
            value = eval(val)
        elif metr:
            val = metr.group()
            value = eval(val)
        else:
            print "Please use '1', '1.5', or '1 1/2' format only please."
        return value
    
    def f(value):
        num = str(value).split('.')
        le = len(num)
        if le > 1:
            l = len(num[1])
            y = int('1'+('0'*l))
            x = int(num[1])
            n = str(num[0])
            d = reduce(x, y)
        else:
            n = str(num[0])
            d = ''
        return n+' '+d
    
    def reduce(x, y):
        num1 = x
        num2 = y
        divisor = hcf(num1, num2)
        while (divisor > 1):
            num1 = num1 / divisor
            num2 = num2 / divisor
            divisor = hcf(num1, num2)
        fnum1 = str(int(num1))
        fnum2 = str(int(num2))
        return fnum1+'/'+fnum2
    
    def hcf(a, b):
        num1 = a
        num2 = b
        if (num1 == 0 or num2 == 0):
            return 0
        remainder = num1 % num2
        while (remainder != 0):
            num1 = num2
            num2 = remainder
            remainder = num1 % num2
        return num2 
    
    #Done
    q = nunput("  Number of doors: ")
    w = nunput("            Width: ")
    l = nunput("           Length: ")
    c = nunput("        Cut depth: ")
    m = nunput("Machine clearance: ")
    s = nunput("      Stile width: ")
    r = nunput("       Rail width: ")
    sl = l
    rl = w-(2*s)+(2*c)
    pl = sl-(2*r)-(2*c)-(2*m)
    pw = w-(2*s)-(2*c)-(2*m)
    rq = 2*q
    sq = 2*q
    pq = q
    print "Cutlist for:", q, "doors", f(w), "x", f(l)
    print ""
    print "     Panels:", pq, '|', f(pw), 'x', f(pl)
    print "     Stiles:", sq, '|', f(s), 'x', f(sl)
    print "      Rails:", rq, '|', f(r), 'x', f(rl)
    Also feel free to use any of that code. Cause it took me forever to figure out how to get it to convert any decimal into a fraction and make a decimal a mixed fraction. Lord help. Well, like I said, any comments on this, good or bad, would be great.

    Thanks.

    Oops. One more thing. Is there anyway to get the output to line up in a chart or something. Since the length of the numbers vary I cant format my code to look right. Maybe there is something to do like center arrangement or matrices or something?
  28. #15
  29. Mini me.
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Location
    Cambridge, UK
    Posts
    783
    Rep Power
    13
    You need a loop on your input - no point telling the user they have a problem if they can't fix it.
    IMO a real world program will only output in standard fractions (like my example). Measure this with a ruler :
    Code:
      Number of doors: 1
    			Width: 12
    		   Length: 12
    		Cut depth: 1/8
    Machine clearance: 1/32
    	  Stile width: 2 1/3
    	   Rail width: 2 1/3
    Cutlist for: 1 doors 12  x 12 
    	 Panels: 1 | 7 2083333333/100000000000 x 7 2083333333/100000000000
    	 Stiles: 2 | 2 33333333333/100000000000 x 12 
    	  Rails: 2 | 2 33333333333/100000000000 x 7 58333333333/100000000000
    But as this is your homework!!!, you don't need real world.

    Use a formatted string to define the layout (like my example). Work out the longest answer and format the strings around it.

    Credit where it's due :Fractions (int/int) question...?

    Grim
Page 1 of 2 12 Last
  • Jump to page:

IMN logo majestic logo threadwatch logo seochat tools logo