0

I'm working on a script that needs to output numbers in a precision determined by length and not just decimal places. Let's say I want a max of 7 decimals but whole numbers above 7 digits are ok although they can't have decimals if the total amount of digits is 7 or higher.

So the rules are:

- If a float has more than 7 digits of whole numbers, it loses it's decimals
- If a float has less than 7 digits of whole numbers, it gets a length of 7 total digits including decimals
- If a floats has no digits of whole numbers, it keeps 7 decimals

For example:

a = 10000.02
b = 1.000002
c = 100000000000

are all correct.

How do I handle numbers (all float) in this format?

For example:

d = 892432589.54680
e = 382.39810758264057251
f = 452545871.1643548
g = 10254.968743541742
h = 165783438364325.126

I was thinking about something along the lines of:

length_of_number = len(str(x))
    if length_of_number > 7:
        x.round(0)
    else:
        x.round(7 - length_of_number)

but then I get in trouble with situations like

x= 0.5313231568943218748

because the whole float is longer than 7 digits.

What to do?

Edit for asked examples:

12345678 is ok
1234567.1 is ok (but 1234567.12 not)
1.12345678 would become 1.123456
NoSplitSherlock
  • 605
  • 4
  • 19
  • You can't use commas like that in numeric literals. [Instead, as of Python 3.6, you can use underscores `_` to group digits in numeric literals](https://www.python.org/dev/peps/pep-0515/) – Patrick Haugh Feb 02 '18 at 01:29
  • 1
    Possible duplicate of [How to format a floating number to fixed width in Python](https://stackoverflow.com/questions/8885663/how-to-format-a-floating-number-to-fixed-width-in-python) – supersam654 Feb 02 '18 at 01:31
  • @patrick Oh sorry, I probably did that to make it more readable for humans. Forgetting that it's not how I handle the actual numbers in the script. I'll edit for clarity. – NoSplitSherlock Feb 02 '18 at 01:31
  • @supersam654 I don't want my decimals to have a fixed width, I want to have a maximum amount of decimals determined by the length of the float. – NoSplitSherlock Feb 02 '18 at 01:36
  • Are you looking at the number of digits, or the number of characters when truncating to seven? does `-0.12345678` get truncated to `-0.123456`, `-0.1234567`, or `-0.1234`? – Patrick Haugh Feb 02 '18 at 01:37
  • 3
    I'm having a problem understanding the expected results. Could you provide your expected outputs for `12345678`, `1234567.1`, and `1.12345678`, please? – viraptor Feb 02 '18 at 01:38
  • @PatrickHaugh Negative numbers are not an issue in this case. Just the digits. – NoSplitSherlock Feb 02 '18 at 01:43
  • Why is `1234567.1` okay? It has 8 digits even though there are 7 whole numbers? In the later sample you said `1.123456` should be the expected result of `1.12345678`. – r.ook Feb 02 '18 at 04:49

5 Answers5

0

I don't really get what you want, but perhaps this can help you?

round(num_here, 7)

From the documentation:

Return number rounded to ndigits precision after the decimal point. If ndigits is omitted or is None, it returns the nearest integer to its input.

For the built-in types supporting round() , values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done toward the even choice (so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2). Any integer value is valid for ndigits (positive, zero, or negative). The return value is an integer if called with one argument, otherwise of the same type as number.

For a general Python object number, round(number, ndigits) delegates to number.__round__(ndigits).

Note: The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it's a result of the fact that most decimal fractions can't be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.

Community
  • 1
  • 1
kiyah
  • 1,502
  • 2
  • 18
  • 27
0

I think what you are asking can be solved in how you determine your length and also by finding if there is a decimal in your string or not. For instance if you said

>>>a = 1325.6326
>>>len(str(a).split(".")[0])
4

This will give you the length of all your numbers to the left of the decimal place.

>>>b = '.' in str(a)
>>>print(b)
True

And this will tell you if there is a decimal place in your number.

Vilyanare
  • 44
  • 2
0
def truncate(n):
    s = str(n)
    is_decimal = '.' in s
    if len(s) <= 7 + is_decimal:
        return s
    if not is_decimal:
        return s
    fore, aft = s.split('.', 1)
    if len(fore) >= 7:
        return fore
    if n < 1:
        return '{:.7f}'.format(n)
    return f'{{:.{7-len(fore)}f}}'.format(n)

Here's one version, using string formatting to truncate the decimals. It turns out that string formatting actually rounds, instead of dropping characters, so string slicing may work better for you

def truncate(n):
    s = str(n)
    is_decimal = '.' in s
    if len(s) <= 7 + is_decimal:
        return s
    if not is_decimal:
        return s
    fore, aft = s.split('.', 1)
    if len(fore) >= 7:
        return fore
    if n < 1:
        return '.' + aft[:7]
    return '{}.{}'.format(fore, aft[:7-len(fore)])
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
0

I change your code slightly and turned it into a function to make it easier to test.

num = [892432589.54680,
  382.39810758264057251,
  452545871.1643548,
  10254.968743541742,
  165783438364325.126,
  0.5313231568943218748]

def trunc_num(x):
  num_int = int(x)
  length_of_number = len(str(num_int))
  if length_of_number >= 7:
    return num_int
  else:
    return round(x, 7 - length_of_number)

for n in num:
  print(trunc_num(n))

By changing the number into an int the decimals are dropped and we can check the length of the number with out the decimal in the way. If the int is greater than 7 digits we just return the int else we round the number as you were trying before.

Output

892432589
382.3981
452545871
10254.97
165783438364325
0.531323

If you need to account for negative numbers than you can just add lstrip after converting it to a string.

length_of_number = len(str(num_int).lstrip('-'))
hcoat
  • 2,633
  • 1
  • 22
  • 29
0

Here's a super simple version using string slicing:

def seven(x):
    ret = lambda x: float(x) if '.' in str(x) else int(x)
    return ret(str(x)[:max(str(x).find('.'), 8 + ('-' in str(x)))].strip('.'))

Output:

<class 'int'>           892432590
<class 'int'>          -892432590
<class 'float'>          382.3981
<class 'float'>         -382.3981
<class 'int'>           452545871
<class 'int'>          -452545871
<class 'float'>          10254.97
<class 'float'>         -10254.97
<class 'int'>     165783438364325
<class 'int'>    -165783438364325
<class 'float'>          0.531323
<class 'float'>         -0.531323

An alternative using str.format():

def seven(x):
    ret = lambda x: float(x) if '.' in x else int(x)
    return ret('{:.{cutoff}f}'.format(x, cutoff=7 - min(str(x).find('.') - ('-' in str(x)), 7)))
r.ook
  • 13,466
  • 2
  • 22
  • 39