Decimal points in python [closed] - python

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I have some floating point values, these all values are dynamically generated:
float_a = 12.200
float_b = 14.0
float_c = 14.01880
I want to remove the decimal point from them.
Expected Result
"12200"
"140"
"1401880"
I must be careful as these are not fixed-point values and I don't have any control on how many decimal places my input will go up to. More examples from the comments:
1.10
1.110
2.0
2.1340
Expected result:
"110"
"1110"
"20"
"21340"

This question seems to be a permutation of another question that has already been answered:
The concept of leading zeros is a display concept, not a numerical
one. You can put an infinite number of leading zeros on a number
without changing its value. Since it's not a numeric concept, it's not
stored with the number.
You have to decide how many zeros you want when you convert the number
to a string. You could keep that number separately if you want.
SOURCE: Python Force python to keep leading zeros of int variables
It looks like the only way to do what you are asking is if your initial "Float" is in string form, otherwise the trailing 0's will be dropped. If you manage to get the "Float" as a string (before it ever becoming a Float), then you can use the int('12.200'.replace('.', '')) method mentioned above

If you're starting off with a string, which I think you are. I would use something like:
>>> int('12.200'.replace('.',''))
12200
It just removes the . and parses the resulting string as an int. Otherwise just cast whatever you have to str first.

It is just a formatting issue. Either of these work to get trailing zeroes:
>>> '{:.3f}'.format(1.2)
'1.200'
>>> '%0.3f' % (1.2,)
'1.200'
Then:
>>> '{:.3f}'.format(12.2).replace('.','')
'12200'
To get what you want from your examples:
vals=(1.10, 1.110, 2.0, 2.134)
def wid(f):
w=1
while True:
s='{:.{w}f}'.format(f, w=w)
if s[-1]=='0' or w>5:
break
w+=1
return w
for e in vals:
s='{:.{w}f}'.format(e, w=wid(e)).replace('.','')
print '{:10} => {}'.format(e, s)
Prints:
1.1 => 110
1.11 => 1110
2.0 => 20
2.134 => 21340

If you want to remove the decimal, why not multiply by 1000?
float_a = 12.200
print str(int(float_a*1000))
When you have
float_a = 1.110
float_b = 1.1100
float_c = 1.11000
Python is discarding the trailing zeros, and the above 3 are the exact same. These trailing zeros are non-significant digits, and so if you want to actually preserve them, your original data type will need to be a String.
If you make that change, then it is simple to do
float_a = "1.110"
print float_a.replace('.', '') # "1110"

If you're asking how to turn float_a = 12.200 into the string "12200", simply multiply it by 1000 before converting it into a string, for example:
print(str(float_a * 1000))
However, if the number contains more than 4 decimal places, you'll still have decimals.
However, since you're talking about it "always removing the 0's", I suspect you may have missed a . in your expected output. In this case, to display a float with a fixed number of decimals, just use Python's built-in string formatting:
float_a = 12.200
expected_result = '%.2f' % float_a
print expected_result
> 12.20
If this doesn't make sense, please clarify your question a bit and I'll edit! :)
Edit
Upon your further clarification of the question, it seems that you want to define a float with trailing zeros and have them preserved and the float converted to a string with the leading zeros preserved. I'm afraid this is impossible if your input type is float, since float won't preserve those trailing zeros to begin with:
In [1]: float_a = 3.1400
In [2]: float_a
Out[2]: 3.14
This is a "limitation" of the data type and there's nothing you can do about it. You want a fixed-point type or something to handle "decimal numbers", not a float.

Related

Python - Format all float values in a list to the same amount of decimal places [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I am new to coding and would like to round a list of float values to the same decimal place based upon a float value in the list that has the most decimal places. For example, say my_list = [23.40, 12.45, 54.21]. How can I go about printing the values such that the zero at the end of 23.40 is printed out as well? In my example, I know that you could round to 2 decimal places, but what if that were not the case (ex. my_list = [23.40, 12.4523, 87.123])?
Sincerely,
Nova
This is related to formatting float values to a string with given precision.
Below is a step-by-step example on how to print all list values with the maximum decimal precision.
my_list = [23.40, 12.4523, 87.123]
# generate a string repr
my_list_str = [str(x) for x in my_list]
# find the max num of decimal places
max_decimal = max([ len(x) - x.find('.') - 1 for x in my_list_str])
print("max_decimal", max_decimal)
# generate a format string
fmt_str = f"%0.{max_decimal}f"
print("fmt_str", fmt_str)
# format all numbers to same decimal places
my_list_str = [fmt_str % x for x in my_list]
print("my_list_str", my_list_str)
# join all strings
res = "[" + ", ".join(my_list_str) + "]"
print(res)
Output
max_decimal 4
fmt_str %0.4f
my_list_str ['23.4000', '12.4523', '87.1230']
[23.4000, 12.4523, 87.1230]
The code can be reduced to lesser lines using higher order functions and more list comprehensions, but the example should give a general idea on the steps.
The first step is to find the maximum precision of your list of points. To do that, you need a way to find the precision of a number. Here is a function call that can do that (but see note at bottom of post):
def decimal_precision(x):
if isinstance(x, int): # if x is an integer there are no decimals
return(0)
# if it is a float count the number of values after the decimal
x_str = str(x) # start by converting x to a string
x_split = x_str.split('.') # the split command creates a list, dividing the string at '.'
n_decimals = len(x_split[1]) # the length of element 1 of x_split will be the number of decimals
return(n_decimals)
Now we need to see which entry in your list has the most decimals
my_list = [23.40, 12.4523, 87.123]
max_precision = 0
for entry in my_list:
prec = decimal_precision(entry)
if prec > max_precision:
max_precision = prec
Finally, printing the values:
for entry in my_list:
print(f'{entry:.{max_precision}f}')
# 23.4000
# 12.4523
# 87.1230
NOTE: This can actually be a trickier question to answer than at first glance because of how floating point arithmetic works. See for example, this post. That post is about a decade old, though, and current versions of Python (I'm using 3.8.8) seem to do something to try to improve this under the hood. Above, I assumed to use a simpler approach for estimating precision than the one suggested in the accepted answer for the post referenced above. If you ran into issues due to floating point arithmetic, you'd might want to consider a more elaborate function.
This problem can be solved using two list comprehensions: one list comprehensions to determine the number of decimal places, and one list comprehension to construct strings containing the numbers.
my_list = [23.40, 12.4523, 87.123]
dec = [len(str(elem).split(".")[1]) for elem in my_list]
str_list = [f'{elem:.{max(dec)}f}' for elem in my_list]
print(str_list)
# Output:
# ['23.4000', '12.4523', '87.1230']

How to return the fractional part of a number? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
How can I get the fractional part of a number?
For example, I have a list of floats num = [12.73, 9.45] and want to get only the numbers after the decimal point, 73 and 45 in this case. How do I go about doing this?
One approach is using pure(ish) maths.
The short answer:
num = [12.73, 9.45]
[int((f % 1)*100) for f in num]
>>> [73, 44]
Explanation:
The modulo operator returns the remainder once whole division is complete (to over-simplify).
Therefore this, returns the decimal value; the fractional part of the number.
12.73 % 1
>>> 0.7300000000000004
To get the decimal value as a integer, you can use:
int((12.73 % 1)*100)
>>> 73
Just wrap this in a loop for all required values ... and you have the 'short answer' above.
num = [12.73, 9.45];
result = list(map(lambda x: int(str(x).split('.')[1]),num))
print(result)
and want to get only the numbers after the period,
There is no such thing. Numbers don't have digits; the string representation of the numbers has digits. And even then, floating-point numbers are not precise; you may be shown 0.3 in one context and 0.30000000000000004 in another, for the same value.
It sounds like what you are actually after is the fractional part of the numbers. There are many ways to do this, but they all boil down the same idea: it is the result when you divide (as a floating-point number) the input by 1.
For a single value, it looks like:
fractional_part = value % 1.0
or
# This built-in function performs the division and gives you
# both quotient and remainder.
integer_part, fractional_part = divmod(value, 1.0)
or
import math
fractional_part = math.fmod(value, 1.0)
or
import math
# This function is provided as a special case.
# It also gives you the integer part.
# Notice that the results are the other way around vs. divmod!
fractional_part, integer_part = math.modf(value)
To process each value in a list in the same way, use a list comprehension.

Round negative float numbers to floats [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I am trying to round some negative floating numbers, the format i want is like the below print/format way, not the round. The problem is that the print approach is not "clean", since it outputs a string with spaces, I got this code from another similar question here in Stackoverflow. The question is how to format/round the numbers like the print below. Thank you
theta = 0.33161255787892263
math = 0 + (1-0) * (1-math.cos(theta))**5
round(math,8) # Result: 4.8e-07 #
print("{:18.8f}".format(math)) # Result: ' 0.00000048' #
You say "I want the result of the print but in a float number not string" and "The result returns to a negative power floating point number, i want to keep the decimal format as the string". But you can't do that because you have no control over the internal representation of a float: they are all stored in a binary form of scientific notation. See the Wikipedia article on floating-point numbers for details.
So it doesn't matter whether you do
v = 0.00000048
or
v = 4.8e-07
both of those statements have an identical effect.
Note that many fractional numbers that terminate when written in decimal may repeat when written in binary. The only fractions that terminate when written in binary are of the form n / (2 ** b), where n and b are integers. Thus even an innocuous-looking number like 0.2 doesn't terminate when converted to binary. (See the Wiki link for a fuller explanation). Because of this issue it's generally not a good idea to round floating-point numbers until you've finished all calculations with them.
If you convert a string to float and back again it has to be converted from decimal to binary and back again. So such an operation shouldn't be used in an attempt to "clean up" a number because of the possible rounding errors at each conversion step.
Of course, sometimes you do need to apply rounding to a float that you are going to continue calculating with, but if so, you should proceed with caution and make sure you really do understand what you're doing to your data.
...
There are a few other strange things with the code you posted.
math = 0 + (1-0) * (1-math.cos(theta))**5
Firstly, you should not use the name of a module that you've imported as a variable name. After the above statement is executed math now refers to the result of the calculation, not the math module, so if you tried to do x = math.cos(0.5) you'd get an error. Similarly, don't use int, str, list, etc as variable names.
Secondly, the 0 + (1-0) * is virtually useless. So the above statement could be re-written as
result = (1 - math.cos(theta)) ** 5
And the whole code snippet would become
#! /usr/bin/env python
import math
theta = 0.33161255787892263
result = (1 - math.cos(theta)) ** 5
print round(result, 8)
print("{0:.8f}".format(result))
output
4.8e-07
0.00000048

Strings, ints and leading zeros

I need to record SerialNumber(s) on an object. We enter many objects. Most serial numbers are strings - the numbers aren't used numerically, just as unique identifiers - but they are often sequential. Further, leading zeros are important due to unique id status of serial number.
When doing data entry, it's nice to just enter the first "sequential" serial number (eg 000123) and then the number of items (eg 5) to get the desired output - that way we can enter data in bulk see below:
Obj1.serial = 000123
Obj2.serial = 000124
Obj3.serial = 000125
Obj4.serial = 000126
Obj5.serial = 000127
The problem is that when you take the first number-as-string, turn to integer and increment, you loose the leading zeros.
Not all serials are sequential - not all are even numbers (eg FDM-434\RRTASDVI908)
But those that are, I would like to automate entry.
In python, what is the most elegant way to check for leading zeros (*and, I guess, edge cases like 0009999) in a string before iterating, and then re-application of those zeros after increment?
I have a solution to this problem but it isn't elegant. In fact, it's the most boring and blunt alg possible.
Is there an elegant solution to this problem?
EDIT
To clarify the question, I want the serial to have the same number of digits after the increment.
So, in most cases, this will mean reapplying the same number of leading zeros. BUT in some edge cases the number of leading zeros will be decremented. eg: 009 -> 010; 0099 -> 0100
Try str.zfill():
>>> s = "000123"
>>> i = int(s)
>>> i
123
>>> n = 6
>>> str(i).zfill(n)
'000123'
I develop my comment here, Obj1.serial being a string:
Obj1.serial = "000123"
('%0'+str(len(Obj1.serial))+'d') % (1+int(Obj1.serial))
It's like #owen-s answer '%06d' % n: print the number and pad with leading 0.
Regarding '%d' % n, it's just one way of printing. From PEP3101:
In Python 3.0, the % operator is supplemented by a more powerful
string formatting method, format(). Support for the str.format()
method has been backported to Python 2.6.
So you may want to use format instead… Anyway, you have an integer at the right of the % sign, and it will replace the %d inside the left string.
'%06d' means print a minimum of 6 (6) digits (d) long, fill with 0 (0) if necessary.
As Obj1.serial is a string, you have to convert it to an integer before the increment: 1+int(Obj1.serial). And because the right side takes an integer, we can leave it like that.
Now, for the left part, as we can't hard code 6, we have to take the length of Obj1.serial. But this is an integer, so we have to convert it back to a string, and concatenate to the rest of the expression %0 6 d : '%0'+str(len(Obj1.serial))+'d'. Thus
('%0'+str(len(Obj1.serial))+'d') % (1+int(Obj1.serial))
Now, with format (format-specification):
'{0:06}'.format(n)
is replaced in the same way by
('{0:0'+str(len(Obj1.serial))+'}').format(1+int(Obj1.serial))
You could check the length of the string ahead of time, then use rjust to pad to the same length afterwards:
>>> s = "000123"
>>> len_s = len(s)
>>> i = int(s)
>>> i
123
>>> str(i).rjust(len_s, "0")
'000123'
You can check a serial number for all digits using:
if serial.isdigit():

Float converted to 2.dp reverts to original number of decimal places when inserted into a string

I have created the following snippet of code and I am trying to convert my 5 dp DNumber to a 2 dp one and insert this into a string. However which ever method I try to use, always seems to revert the DNumber back to the original number of decimal places (5)
Code snippet below:
if key == (1, 1):
DNumber = '{r[csvnum]}'.format(r=row)
# returns 7.65321
DNumber = """%.2f""" % (float(DNumber))
# returns 7.65
Check2 = False
if DNumber:
if DNumber <= float(8):
Check2 = True
if Check2:
print DNumber
# returns 7.65
string = 'test {r[csvhello]} TESTHERE test'.format(r=row).replace("TESTHERE", str("""%.2f""" % (float(gtpe))))
# returns: test Hello 7.65321 test
string = 'test {r[csvhello]} TESTHERE test'.format(r=row).replace("TESTHERE", str(DNumber))
# returns: test Hello 7.65321 test
What I hoped it would return: test Hello 7.65 test
Any Ideas or suggestion on alternative methods to try?
It seems like you were hoping that converting the float to a 2-decimal-place string and then back to a float would give you a 2-decimal-place float.
The first problem is that your code doesn't actually do that anywhere. If you'd done that, you would get something very close to 7.65, not 7.65321.
But the bigger problem is that what you're trying to do doesn't make any sense. A float always has 53 binary digits, no matter what. If you round it to two decimal digits (no matter how you do it, including by converting to string and back), what you actually get is a float rounded to two decimal digits and then rounded to 53 binary digits. The closest float to 7.65 is not exactly 7.65, but 7.650000000000000355271368.* So, that's what you'd end up with. And there's no way around that; it's inherent to the way float is stored.
However, there is a different type you can use for this: decimal.Decimal. For example:
>>> f = 7.65321
>>> s = '%.2f' % f
>>> d = decimal.Decimal(s)
>>> f, s, d
(7.65321, '7.65', Decimal('7.65'))
Or, of course, you could just pass around a string instead of a float (as you're accidentally doing in your code already), or you could remember to use the .2f format every time you want to output it.
As a side note, since your DNumber ends up as a string, this line is not doing anything useful:
if DNumber <= 8:
In Python 2.x, comparing two values of different types gives you a consistent but arbitrary and meaningless answer. With CPython 2.x, it will always be False.** In a different Python 2.x implementation, it might be different. In Python 3.x, it raises a TypeError.
And changing it to this doesn't help in any way:
if DNumber <= float(8):
Now, instead of comparing a str to an int, you're comparing a str to a float. This is exactly as meaningless, and follows the exact same rules. (Also, float(8) means the same thing as 8.0, but less readable and potentially slower.)
For that matter, this:
if DNumber:
… is always going to be true. For a number, if foo checks whether it's non-zero. That's a bad idea for float values (you should check whether it's within some absolute or relative error range of 0). But again, you don't have a float value; you have a str. And for strings, if foo checks whether the string is non-empty. So, even if you started off with 0, your string "0.00" is going to be true.
* I'm assuming here that you're using CPython, on a platform that uses IEEE-754 double for its C double type, and that all those extra conversions back and forth between string and float aren't introducing any additional errors.
** The rule is, slightly simplified: If you compare two numbers, they're converted to a type that can hold them both; otherwise, if either value is None it's smaller; otherwise, if either value is a number, it's smaller; otherwise, whichever one's type has an alphabetically earlier name is smaller.
I think you're trying to do the following - combine the formatting with the getter:
>>> a = 123.456789
>>> row = {'csvnum': a}
>>> print 'test {r[csvnum]:.2f} hello'.format(r=row)
test 123.46 hello
If your number is a 7 followed by five digits, you might want to try:
print "%r" % float(str(x)[:4])
where x is the float in question.
Example:
>>>x = 1.11111
>>>print "%r" % float(str(x)[:4])
>>>1.11

Categories

Resources