I tagged this question as javascript because even though I currently wrote this in Python, if it would be easier to implement in Javascript, I could easily implement it in Javascript.
My assignment is to make a significant figure calculation checker for the chemistry department . What that means is that the student enters their data into the fields, the web app will perform predefined operations on their fields and keep track of significant figures and see if their answer has the proper number of significant figures.
When I broke the problem down into what I figure is a good work flow, I realized that I would need a way for either Python (the backend since this is a web app made in Django) or Javascript (cause you could always validate it on the front end no problem) to determine the number of significant digits. I did a little research and came across this question which tells me that I needed to work with python strings instead of floats. My current python code feels ALMOST complete, but there is still one major challenge I face
import re
def find_sigfigs(x):
# change the 'E' to lower case if the student typed it in as uppercase
x = x.lower()
if ('e' in x):
myStr = x.split('e')
# this function assumes that the number on the left of the 'e' is
# the number of sigfigs. That would be true for user input but not
# true when python converts a float to scientific notation
return len( (re.search('[0-9]+', myStr[0])).group() )
else:
# put it in e format and return the result of that
### problem: python makes me hard code the number of sigfigs as '2'
### without the 2 there it always defaults to 6
return find_sigfigs('%.*e' %(2,float(x)))
>>> find_sigfigs('1.3e-4')
>>> 2
>>> find_sigfigs('1234')
>>> 3
>>> find_sigfigs('123456')
>>> 3
>>> find_sigfigs('1.2345e3')
>>> 5
then without the 2
return find_sigfigs('%.e' %(float(x)))
#Because it changes it to 1.234000e3
>>> find_sigfigs('1234')
>>> 7
#Because it changes it to 1.234560e5
>>> find_sigfigs('123456')
>>> 7
So simply put, my problem is that I need a simple way to count the sigfigs when it is not explicitly declared by the student (aka when it is in scientific notation). Is there some easy way that I could drop every zero before the 'e' until it gets to the first non zero digit. I guess, I need to start from the back of the split string and remove zeros until it gets to a nonzero digit?
EDIT: So after a little more fiddling, I hope this is an appropriate solution to the problem. I tested it several times, but not too rigorously (aka it probably works but who knows! I'm not too good at sigfigs...)
def find_sigfigs(x):
'''Returns the number of significant digits in a number. This takes into account
strings formatted in 1.23e+3 format and even strings such as 123.450'''
# change all the 'E' to 'e'
x = x.lower()
if ('e' in x):
# return the length of the numbers before the 'e'
myStr = x.split('e')
return len( myStr[0] ) - 1 # to compenstate for the decimal point
else:
# put it in e format and return the result of that
### NOTE: because of the 8 below, it may do crazy things when it parses 9 sigfigs
n = ('%.*e' %(8, float(x))).split('e')
# remove and count the number of removed user added zeroes. (these are sig figs)
if '.' in x:
s = x.replace('.', '')
#number of zeroes to add back in
l = len(s) - len(s.rstrip('0'))
#strip off the python added zeroes and add back in the ones the user added
n[0] = n[0].rstrip('0') + ''.join(['0' for num in xrange(l)])
else:
#the user had no trailing zeroes so just strip them all
n[0] = n[0].rstrip('0')
#pass it back to the beginning to be parsed
return find_sigfigs('e'.join(n))
Use String Formatting to Round a Number to the Given Significant Digit in Python. In Python, the %g specifier in string formats a float rounded to a specified significant figure.
' + str(p) + 'e') % f) allows you to adjust the number of significant digits!
I think there's a simpler solution that doesn't require recursion. Also, the above solution only works when passing in strings. It seems odd to me to ask for the significant digits in a string so feels like the function should do that transformation internally or at least support passing strings and numbers.
Here's what I came up with:
def find_sigfigs(number):
"""Returns the number of significant digits in a number"""
# Turn it into a float first to take into account stuff in exponential
# notation and get all inputs on equal footing. Then number of sigfigs is
# the number of non-zeros after stripping extra zeros to left of whole
# number and right of decimal
number = repr(float(number))
tokens = number.split('.')
whole_num = tokens[0].lstrip('0')
if len(tokens) > 2:
raise ValueError('Invalid number "%s" only 1 decimal allowed' % (number))
if len(tokens) == 2:
decimal_num = tokens[1].rstrip('0')
return len(whole_num) + len(decimal_num)
return len(whole_num)
Am I missing some edge cases?
I think regular expressions are a bit of an overkill here, but your method should work and I'm sure it's not a performance issue.
I think you're on the right track with what you describe at the end. I would use split('e')
followed by rstrip('0')
, which will remove 'trailing zeros'. You can then put the string back together if you want to keep the recursive call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With