probat
probat

Reputation: 1532

Round a floating point number to n digits ignoring leading zeros and preserving them

Floating Point = 11.0000123456789

I want to round the above floating point number to 11.00001235. So I want the base portion of the float rounded to 4 digits while ignoring and preserving the leading zeros and adding back in the significand at the end.

I have the following, it is short and sweet but feels a little bit like a work around.

import decimal

decimal.getcontext().prec = 4
significand, base = str(11.0000123456789).split('.')
fp = significand + str(decimal.Decimal('.' + base) + 0)[1:]  # I need to add 0 here for it to work
print(fp)

I can't really find an answer to my specific question. I want to know what the most pythonic way of achieving this is or if what I have is decent enough. It feels a little shoddy to me.

Edit: the number of leading zeros is unknown and I chose four for the example. So I don't think string formatting will work.

Upvotes: 2

Views: 632

Answers (3)

Luc Hayward
Luc Hayward

Reputation: 31

An issue with @Rotem's answer is that it won't account for all leading 0s (number between 0-1), this small edit fixes that.

def round_to_N_ignoring_leading_zeros(ending_values_to_keep, number):
"""
rounds the number off to N trailing decimals, ignoring leading values (including 0)
1.000016 -> 1.00001600
0.000016 -> 0.00001600
11.0000123456789 -> 11.00001235

:param ending_values_to_keep:
:param number:
:return:
"""
import re
altered = False
if number < 1:
    altered = True
    number += 1
regex = r"0[1-9]"
float_part = str(number).split(".")[1]
float_limiter = re.search(regex, float_part).start() if float_part.startswith("0") else -1
number -= 1
return eval(f'{number:2.{float_limiter + 1 + ending_values_to_keep}f}')

Upvotes: 0

Rotem Tal
Rotem Tal

Reputation: 769

Edit: for an unknown number of 0's I don't see a better alternative than

import re
some_float = 11.00001203456789
how_many_to_keep = 4
regex = r"0[1-9]"
float_part = str(some_float).split(".")[1]
float_limiter = re.search(regex,float_part).start() if float_part.startswith("0") else -1
print(f'{some_float:2.{float_limiter+1+how_many_to_keep}f}')

Which is evaluating the number of 0 in real time using the expression float_limiter+1+how_many_to_keep, and use as a the format parameter. this has gotten a bit ugly but now returns the correct answer on all cases. The output is

11.00001235

Upvotes: 1

Kushan Gunasekera
Kushan Gunasekera

Reputation: 8566

I think this is the most pythonic way to solve your problem. Try this,

>>> import re
>>>
>>> float_val = 11.0000123456789
>>> keep_places = 4
>>>
>>> regEX = f'[0-9]+\.[0]+{"[0-9]" * keep_places}'
>>> expected_val = re.search(regEX, str(float_val)).group()
>>>
>>> print(expected_val)
'11.00001234'

The easiest way is create the Regular expression operations and this is how I use it to solve your question. Let's break it into 4 parts.

  1. [0-9]+
  2. \.
  3. [0]+
  4. [0-9] * keep_places

(1). [0-9] mean select only one digit in between 0 and 9. We need multiple digit before the decimal point. That's why we add [0-9]+. If you don't have any integers before the decimal point, then add * instead of +, it check zero or more occurrences. If you're using + mean you must have one or more values.

(2). . this dot is a special character to select any character except newline character from the given string. We need to capture decimal point which also a dot. If our string has these kind of special characters(in our case a decimal point) we use \ to match those characters. That's why we use \., it now can match our decimal point.

(3). [0]+, this will match all the zeros in our string.

(4). [0-9] * keep_places, this is just a string multiplication. For this question keep_places is 4 which mean we get this kind of output [0-9][0-9][0-9][0-9]. But in Regular expression operations, it select only four digit which each digit in between 0 and 9. One more thing, for this question it doesn't matter you're using [0-9] or [1-9]. Because we already capture zeros, it start to find other integers from 1.

If you print regEX, all togeather you'll see [0-9]+\.[0]+[0-9][0-9][0-9][0-9] kind of string. It matches <any_number><dot><all_zeros_before_the__non_zero_integer><4_digit_after_last_zero> type of pattern only. You can test this expression online.

Upvotes: 2

Related Questions