0

I am writing a graphing calculator in Python 3

However, this code here is giving me problems.

def domainGen(start,end,step):
    #generates a list of points for t, x, or theta to be equal to.
    #Acts like a floating point equivalent to "range" except it generates a whole list object, not just an iterator
    domain = [] #creates an empty list to store the domain
    minInt = start//step #rounding towards zero
    maxInt = end//step #rounding towards zero
    for n in range(minInt-1,maxInt + 1):
        if n*step >= start and n*step <= end:domain.append(n*step)
    return domain

The variables of this function are all user inputs and need to be floats or Decimal() values to allow for arbitrary precision.

However, when running this code I get a large number of floating point calculation errors, seemingly at random, to the numbers in the list "domain".

This is the output for domainGen(0,10,0.1):

[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1.0, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2.0, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3.0, 3.1, 3.2, 3.3000000000000003, 3.4000000000000004, 3.5, 3.6, 3.7, 3.8000000000000003, 3.9000000000000004, 4.0, 4.1000000000000005, 4.2, 4.3, 4.4, 4.5, 4.6000000000000005, 4.7, 4.800000000000001, 4.9, 5.0, 5.1000000000000005, 5.2, 5.300000000000001, 5.4, 5.5, 5.6000000000000005, 5.7, 5.800000000000001, 5.9, 6.0, 6.1000000000000005, 6.2, 6.300000000000001, 6.4, 6.5, 6.6000000000000005, 6.7, 6.800000000000001, 6.9, 7.0, 7.1000000000000005, 7.2, 7.300000000000001, 7.4, 7.5, 7.6000000000000005, 7.7, 7.800000000000001, 7.9, 8.0, 8.1, 8.200000000000001, 8.3, 8.4, 8.5, 8.6, 8.700000000000001, 8.8, 8.9, 9.0, 9.1, 9.200000000000001, 9.3, 9.4, 9.5, 9.600000000000001, 9.700000000000001, 9.8, 9.9]

I would like a function that can tell me the number of decimal places a user has specified their values to so that I can then round these numbers off to the same precision.

Is there a built in function that can help me?

If not, what is the most Pythonic way to solve this issue?

Notes:

I am aware there are perfectly good graph modules out there and want to do this myself as an exercise.

I have heard of the module "Decimal" but am not sure exactly how to use it and if it would help in this context.

Decimal Point precision is preferred but a decent workaround with Significant Figure precision could maybe be useful.

I am aware of this question but I understand why floating point errors occur, as I say, I need a function that tells me how many decimal points of precision a user has given me, so its not a duplicate.

Disgusting
  • 123
  • 6
  • `round` is a built in function, yes – OneCricketeer Jul 09 '18 at 12:25
  • Possible duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – scharette Jul 09 '18 at 12:26
  • I know how to round something to a known decimal point precision. What I need is a function that tells me to what precision a user input has been given. – Disgusting Jul 09 '18 at 12:27
  • I also understand _why_ floating point errors occur, as I say, I need a function that tells me how many decimal points of precision a user has given me, so its not a duplicate. – Disgusting Jul 09 '18 at 12:27
  • **seemingly at random**. They are not random at all. **I have heard of the module "Decimal" but am not sure exactly how to use it and if it would help in this context.**, then go ahead and read the documentation. – BcK Jul 09 '18 at 12:28
  • 1
    `int` is completely pointless in `minInt = int(start//step)` since `//` already returns an int. – John Coleman Jul 09 '18 at 12:28
  • I am in the process of reading the documentation, but its a lot and I thought there might be a simpler way of dealing with the issue. – Disgusting Jul 09 '18 at 12:28
  • Thanks John, will change that. – Disgusting Jul 09 '18 at 12:29
  • 1
    Now, are you trying to reinvent `range` here or what ? – BcK Jul 09 '18 at 12:34
  • If someone enters a string containing a single decimal point then it is trivial to count the number of characters in the string after that decimal point. Just write a 2-line function to do so. But -- I really don't think that it makes much sense to do so. It is extremely doubtful that the code behind e.g. a Texas Instruments graphing calculator pays attention to the number of keystrokes a user enters after a decimal point and uses it in its internal representation of numbers. Why should your code? – John Coleman Jul 09 '18 at 12:34
  • I am trying to reinvent range but with floating point numbers. – Disgusting Jul 09 '18 at 12:34
  • By the way -- please don't edit code (other than to fix typos and indentation problems due to pasting) after you have asked a question. Most people find evolving questions to be annoying. – John Coleman Jul 09 '18 at 12:36
  • If the goal is range with floats, maybe https://stackoverflow.com/questions/7267226/range-for-floats will be helpful? – dashiell Jul 09 '18 at 12:37
  • @JohnColeman Could you maybe put that code in an answer? Its so that I don't have all these floating point errors messing up more precise calculations and I won't edit the code again. – Disgusting Jul 09 '18 at 12:38
  • @dashiell This question may well be a duplicate of THAT question. Apologies. – Disgusting Jul 09 '18 at 12:41
  • But since this question is more about calculating the precision of a number in decimal points I don't know if I should leave this up. – Disgusting Jul 09 '18 at 12:42
  • It is on the level of parsing the string input that you can attempt to infer the user's intended precision, not on the level of the above function (which would need to be modified to take another parameter which specifies the precision) -- but I think that what you propose to do is not mathematically motivated. Separate out input/output concerns from internal representations. I would be surprised if the default float isn't adequate for the *computational* needs of the program. – John Coleman Jul 09 '18 at 12:43
  • @dashiell Just ran the code from the answers to that question and I'm left with the same issue. – Disgusting Jul 09 '18 at 12:45
  • @JohnColeman Do you mean round the numbers at the last minute? – Disgusting Jul 09 '18 at 12:48
  • @JohnColeman Also, that "2-line function" would be really helpful in an answer. – Disgusting Jul 09 '18 at 12:49

1 Answers1

1

If someone enters a float or int literal you could do something like this to it:

def get_float(s):
    if '.' in s:
        return float(s), len(s) - s.index('.') - 1
    else:
        return float(s), 0

This function returns a tuple consisting of the corresponding float and the number of characters after the decimal point. It would need to be modified if you want to properly handle float literals which include e or E.

For example:

>>> get_float('2')
(2.0, 0)
>>> get_float('2.1')
(2.1, 1)
>>> get_float('.001')
(0.001, 3)
>>> get_float('-0.001')
(-0.001, 3)

As a practical problem, is it really safe to assume that a user really just cares about 2 decimal places of accuracy because they entered '2.01' rather than '2.01000'? If you want to have a user-specified precision, why not have the user explicitly provide that precision?

John Coleman
  • 51,337
  • 7
  • 54
  • 119
  • Thanks, I am not particularly up on my string functions yet. Implicit precision allows for a sleeker user interface but you may well be right :/ – Disgusting Jul 09 '18 at 12:59
  • @Douglas The advantage of simply using the default float is that for the sort of things you do with a graphing calculator it gives *more* precision than the casual user requires. Using that precision in the internal calculations is a good way to mitigate round-off error. The above function could be used to *record* the user's precision but to use that strictly for *formatting* output. Just use the floats in the computations themselves. – John Coleman Jul 09 '18 at 13:05
  • Why would using this precision internally result in round off error when that's already what's happening? – Disgusting Jul 09 '18 at 13:07
  • I thought by forcing it to round to base 10 instead of base 2 it would make the calculations _more_ accurate. – Disgusting Jul 09 '18 at 13:08
  • @Douglas If you do a complicated mathematical calculation and round to one decimal place after each step, pretty soon that one decimal place is no longer accurate. If you use 14-15 decimal places of accuracy (which is what a float does), then you could trust the first decimal place (and then some) even after thousands of computational steps. `round()` throws away information. If you throw away information, accuracy won't *improve*. Numerical analysis and round-off error is a deep topic. Naive uses of `round()` doesn't provide a reliable way to handle it. – John Coleman Jul 09 '18 at 13:15
  • That makes sense, thank you. I will simply use this function to calculate the precision and round everything at the end. – Disgusting Jul 09 '18 at 13:22