HiveBrain v1.2.0
Get Started
← Back to all entries
debugpythonMinor

Counting significant figures in a number

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
figuresnumbercountingsignificant

Problem

I decided to make something to count the significant figures in a number to see how easily it could be done, but I think there may have been a better or simpler way. Any advice even just for conventional purposes is appreciated!

```
def count_sig_figs(answer):
'''This fucntion will count the sigfigs used in the answer of a user'''
sig_fig_count = 0
num_list = list(answer)

for index in range(len(num_list)):
try:
fig = int(num_list[index])
if fig != 0:
sig_fig_count +=1
elif check_zero_sig(index, num_list, sig_fig_count):
sig_fig_count += 1
except:
continue
return sig_fig_count

def check_zero_sig(index, num_list, sig_fig_count):
'''Checks for significance in a zero from a list'''
try:
decimal = num_list.index('.')
if index > decimal and sig_fig_count > 0:
return True
except:
if index == 0:
return False
elif index == len(num_list):
return False
new_index = index+1

if num_list[new_index] == '.' and sig_fig_count > 0:
return True
elif num_list[new_index] == '.' and sig_fig_count == 0:
return False
elif num_list[new_index] != '.' and sig_fig_count > 0:
fig = int(num_list[new_index])
if fig != 0:
return True
else:
return check_zero_sig(new_index, num_list, sig_fig_count)
elif num_list[new_index] != '.' and sig_fig_count == 0:
fig = int(num_list[new_index])
if fig != 0:
return True
else:
return check_zero_sig(new_index, num_list, sig_fig_count)
else:
return False

def test():
print(count_sig_figs('1')) # 1 sig fig
print(count_sig_figs('10')) # 1 sig fig
print(count_sig_figs('100')) # 1 sig fig
print(count_sig_figs('1004')) # 4 sig figs
print(

Solution

num_list is better named digits; don't describe the type, describe the value.

test's output is unhelpful; the result should be much more obvious, especially when it fails. Keeping along the given lines, I might do something like

def test():
    def should_be(string, count):
        assert count_sig_figs(string) == count
        print("{!r} has {} sf.".format(string, count))

    should_be('1', 1)
    should_be('10', 1)
    should_be('100', 1)
    should_be('1004', 4)
    should_be('10004', 5)
    should_be('105', 3)
    should_be('01', 1)
    should_be('1.2035', 5)
    should_be('001.09508', 6)
    should_be('0.00110', 3)


Of course, a proper test suite is probably better.

This isn't idiomatic:

for index in range(len(digits)):
    try:
        fig = int(digits[index])
        if fig != 0:
            sig_fig_count +=1
        elif check_zero_sig(index, digits, sig_fig_count):
            sig_fig_count += 1
    except:
        continue
return sig_fig_count


Bare try...excepts are bad. Even

except (ValueError, IndexError):


is bad. Primarily this is because you're spreading out the checks where they don't belong and it should instead be

try:
    fig = int(digits[index])
except ValueError:
    continue

if fig != 0:
    sig_fig_count +=1
else:
    try:
        zero_sig = check_zero_sig(index, digits, sig_fig_count)
    except IndexError:
        continue
    if zero_sig:
        sig_fig_count += 1


But then because... why is check_zero_sig throwing an IndexError? Surely that's a bug.

Before we get there, quickly change this to

for index, digit in enumerate(digits):
    if digit == ".":
        continue
    if digit != '0':
        sig_fig_count += 1
    else:
        sig_fig_count += check_zero_sig(index, digits, sig_fig_count)


That's pretty much what we want expressed more simply.

check_zero_sig's except also needs updating:

try:
    decimal = digits.index('.')
except ValueError:
    ...
else:
    return index > decimal and sig_fig_count > 0


I don't get why you don't just mutate index instead of using new_index.

This can be simplified:

if digits[index] == '.' and sig_fig_count > 0:
    return True
elif digits[index] == '.' and sig_fig_count == 0:
    return False


to

if digits[index] == '.':
    return bool(sig_fig_count)


The

elif digits[index] != '.' and sig_fig_count > 0:


and

elif digits[index] != '.' and sig_fig_count == 0:


branches are the same.

This then simplifies to

if digits[index] == '.':
    return bool(sig_fig_count)
elif digits[index] != '.':
    digit = int(digits[index])
    return bool(digit) or check_zero_sig(index, digits, sig_fig_count)
else:
    return False


which is just

if digits[index] == '.':
    return bool(sig_fig_count)
else:
    digit = int(digits[index])
    return bool(digit) or check_zero_sig(index, digits, sig_fig_count)


Then we see

elif index == len(digits):
    return False


cannot fire until after index += 1. This lets us fix our IndexError.

Back to count_sig_figs. The documentation is not written idiomatically. Documentation should be to the point, direct and not talk in third person.

def count_sig_figs(digits):
    '''Return the number of significant figures of the input digit string'''
    sig_fig_count = 0
    for index, digit in enumerate(digits):
        if digit == ".":
            continue
        if digit != '0':
            sig_fig_count += 1
        else:
            sig_fig_count += check_zero_sig(index, digits, sig_fig_count)
    return sig_fig_count


Back to check_zero_sig, which is now

def check_zero_sig(index, digits, sig_fig_count):
    '''Checks for significance in a zero from a list'''
    try:
        decimal = digits.index('.')
    except ValueError:
        if index == 0:
            return False
        index += 1
        if index == len(digits):
            return False
        if digits[index] == '.':
            return bool(sig_fig_count)
        else:
            digit = int(digits[index])
            return bool(digit) or check_zero_sig(index, digits, sig_fig_count)
    else:
        return index > decimal and sig_fig_count > 0


The if digits[index] == '.' check can't fire; we're in the except. Then

digit = int(digits[index])
return bool(digit) or check_zero_sig(index, digits, sig_fig_count)


can be

return digit != '0' or check_zero_sig(index, digits, sig_fig_count)


and the recursion, which would end up re-calling .index, can just be

if index == 0:
    return False
return any(digit != '0' for digit in digits[index+1:])


index == 0 is a very strange special case; you deal with 01 but not 001 or 00001. I suggest removing the special-case and using sig_fig_count.

Now we have

```
def check_zero_sig(index, digits, sig_fig_count):
'''
Returns if a zero digit at a given position is significant,
given a count of significant d

Code Snippets

def test():
    def should_be(string, count):
        assert count_sig_figs(string) == count
        print("{!r} has {} sf.".format(string, count))

    should_be('1', 1)
    should_be('10', 1)
    should_be('100', 1)
    should_be('1004', 4)
    should_be('10004', 5)
    should_be('105', 3)
    should_be('01', 1)
    should_be('1.2035', 5)
    should_be('001.09508', 6)
    should_be('0.00110', 3)
for index in range(len(digits)):
    try:
        fig = int(digits[index])
        if fig != 0:
            sig_fig_count +=1
        elif check_zero_sig(index, digits, sig_fig_count):
            sig_fig_count += 1
    except:
        continue
return sig_fig_count
except (ValueError, IndexError):
try:
    fig = int(digits[index])
except ValueError:
    continue

if fig != 0:
    sig_fig_count +=1
else:
    try:
        zero_sig = check_zero_sig(index, digits, sig_fig_count)
    except IndexError:
        continue
    if zero_sig:
        sig_fig_count += 1
for index, digit in enumerate(digits):
    if digit == ".":
        continue
    if digit != '0':
        sig_fig_count += 1
    else:
        sig_fig_count += check_zero_sig(index, digits, sig_fig_count)

Context

StackExchange Code Review Q#122284, answer score: 2

Revisions (0)

No revisions yet.