Python round - Two deciding decimals [duplicate] - python

This question already has answers here:
How to round a number to significant figures in Python
(26 answers)
Closed 1 year ago.
I want to round numbers so that two numbers are used after the last zero, or decimal if the number is greater than 1. I've looked up the documentation for round, but can't seem to find this feature.
If I use round, it always round to n decimals places regardless.
This is the output I'm looking for:
42.0068 --> 42.01
0.00251 --> 0.0025
420.246 --> 420.25
0.192 -> 0.19
0.00000000128 --> 0.0000000013

The kind of rounding you're asking for isn't a common operation.
In terms of common operations you have two cases (ignoring sign since all your examples are positive):
If the value is less than 1, round to significant figures (eg per the link in #quamrana's comment on the question).
Else (ie the value is at least 1), round to decimal places in the usual way.
Your final code would reasonably be a function with a simple if-else:
def round_special(value):
if value < 1:
return round_to_sf(value, 2)
else:
return round_to_dp(value, 2)

You could try with string manipulation. It's not that elegant, but seems to work. It's up to you to complete this code to handle exceptions..
if number>1:
result = round(number,2)
else:
str_number = str(number)
i = str_number.rfind('0')
result = float(str_number[:i+3])

Related

Taking just two decimals without rounding it

Basically, I have a list of float numbers with too many decimals. So when I created a second list with two decimals, Python rounded them. I used the following:
g1= ["%.2f" % i for i in g]
Where g1 is the new list with two decimals, but rounded, and g is the list with float numbers.
How can I make one without rounding them?
I'm a newbie, btw. Thanks!
So, you want to truncate the numbers at the second digit?
Beware that rounding might be the better and more accurate solution anyway.
If you want to truncate the numbers, there are a couple of ways - one of them is to multiply the number by 10 elevated to the number of desired decimal places (100 for 2 places), apply "math.floor", and divide the total back by the same number.
However, as internal floating point arithmetic is not base 10, you'd risk getting more decimal places on the division to scale down.
Another way is to create a string with 3 digits after the "." and drop the last one - that'd be rounding proof.
And again, keep in mind that this converts the numbers to strings - what should be done for presentation purposes only. Also, "%" formatting is quite an old way to format parameters in a string. In modern Python, f-strings are the preferred way:
g1 = [f"{number:.03f}"[:-1] for number in g]
Another, more correct way, is, of course, treat numbers as numbers, and not play tricks on adding or removing digits on it. As noted in the comments, the method above would work for numbers like "1.227", that would be kept as "1.22", but not for "2.99999", which would be rounded to "3.000" and then truncated to "3.00".
Python has the decimal modules, which allows for arbitrary precision of decimal numbers - which includes less precision, if needed, and control of the way Python does the rounding - including rounding towards zero, instead of the nearest number.
Just set the decimal context to the decimal.ROUND_DOWN strategy, and then convert your numbers using either the round built-in (the exact number of digits is guaranteed, unlike using round with floating point numbers), or just do the rounding as part of the string formatting anyway. You can also convert your floats do Decimals in the same step:
from decimals import Decimal as D, getcontext, ROUND_DOWN
getcontext().rounding = ROUND_DOWN
g1 = [f"{D(number):.02f}" for number in g]
Again - by doing this, you could as well keep your numbers as Decimal objects, and still be able to perform math operations on them:
g2 = [round(D(number, 2)) for number in g]
Here is my solution where we don't even need to convert the number's to string to get the desired output:
def format_till_2_decimal(num):
return int(num*100)/100.0
g = [-5.427926, -12.222018, 7.214379, -16.771845, -6.1441464, 10.1383295, 14.740516, 5.9209185, -9.740783, -10.098338]
formatted_g = [format_till_2_decimal(num) for num in g]
print(formatted_g)
Hope this solution helps!!
Here might be the answer you are looking for:
g = [-5.427926, -12.222018, 7.214379, -16.771845, -6.1441464, 10.1383295, 14.740516, 5.9209185, -9.740783, -10.098338]
def trunc(number, ndigits=2):
parts = str(number).split('.') # divides number into 2 parts. for ex: -5, and 4427926
truncated_number = '.'.join([parts[0], parts[1][:ndigits]]) # We keep this first part, while taking only 2 digits from the second part. Then we concat it together to get '-5.44'
return round(float(truncated_number), 2) # This should return a float number, but to make sure it is roundded to 2 decimals.
g1 = [trunc(i) for i in g]
print(g1)
[-5.42, -12.22, 7.21, -16.77, -6.14, 10.13, 14.74, 5.92, -9.74, -10.09]
Hope this helps.
Actually if David's answer is what you are looking for, it can be done simply as following:
g = [-5.427926, -12.222018, 7.214379, -16.771845, -6.1441464, 10.1383295, 14.740516, 5.9209185, -9.740783, -10.098338]
g1 = [("%.3f" % i)[:-1] for i in g]
Just take 3 decimals, and remove the last chars from the result strings. (You may convert the result to float if you like)

How to find and truncate a list to the nearest power of 2 (in the most pythonic way) [duplicate]

This question already has answers here:
find largest power of two less than X number?
(5 answers)
Closed 6 years ago.
I have a list called data, and I want to truncate it so that it's length is a power of 2. The current method I use is the following:
n = 1 # dummy variable
while len(data)/(2**n) > 1:
n += 1
nearest_n = n - 1
data_trunc = data[0:2**nearest_n]
This does the job; however, it uses a dummy integer n for the while loop, which isn't very "pythonic".
Can someone provide a more pythonic, or efficient method?
Reasoning:
The reason I'm truncating data is because I want to perform an FFT. You can decrease the FFT compute time by using this trick, with little effect on the results.
An FFT works by breaking the data up into odd and even sets and the more times it can do this, the faster it computes. Hence, the more divisible the length is by 2, the more splits can be made, and the shorter the compute time is. In contrast, lists lengths that are prime numbers are the slowest.
I don't intend to spark a discussion on 'zero padding' vs. 'truncating' data for an FFT, but rather focus on the most pythonic way to find the nearest power of 2 for a given number.
int (math.log (len (data), 2))

Why doesn't python decimal library return the specified number of signficant figures for some inputs

NB: this question is about significant figures. It is not a question about "digits after the decimal point" or anything like that.
EDIT: This question is not a duplicate of Significant figures in the decimal module. The two questions are asking about entirely different problems. I want to know why the function about does not return the desired value for a specific input. None of the answers to Significant figures in the decimal module address this question.
The following function is supposed to return a string representation of a float with the specified number of significant figures:
import decimal
def to_sigfigs(value, sigfigs):
return str(decimal.Context(prec=sigfigs).create_decimal(value))
At first glance, it seems to work:
print to_sigfigs(0.000003141592653589793, 5)
# 0.0000031416
print to_sigfigs(0.000001, 5)
# 0.0000010000
print to_sigfigs(3.141592653589793, 5)
# 3.1416
...but
print to_sigfigs(1.0, 5)
# 1
The desired output for the last expression (IOW, the 5-significant figure representation of 1.0) is the string '1.0000'. The actual output is the string '1'.
Am I misunderstanding something or is this a bug in decimal?
The precision of a context is a maximum precision; if an operation would produce a Decimal with less digits than the context's precision, it is not padded out to the context's precision.
When you call to_sigfigs(0.000001, 5), 0.000001 already has some rounding error due to the conversion from source code to binary floating point. It's actually 9.99999999999999954748111825886258685613938723690807819366455078125E-7. Rounding that to 5 significant figures gives decimal.Decimal("0.0000010000").
On the other hand, 1 is exactly representable in binary floating point, so 1.0 is exactly 1. Since only 1 digit is needed to represent this in decimal, the context's precision doesn't require any rounding, and you get a 1-digit Decimal result.
Is it a bug? I don't know, I don't think the documentation is tight enough to make that determination. It certainly is a surprising result.
It is possible to fix your own function with a little more logic.
def to_sigfigs(value, sigfigs):
sign, digits, exponent = decimal.Context(prec=sigfigs).create_decimal(value).as_tuple()
if len(digits) < sigfigs:
missing = sigfigs - len(digits)
digits = digits + (0,) * missing
exponent -= missing
return str(decimal.Decimal((sign, digits, exponent)))

In python, how do I preserve decimal places in numbers? [duplicate]

This question already has answers here:
How can I force division to be floating point? Division keeps rounding down to 0?
(11 answers)
Closed 9 years ago.
I'd like to pass numbers around between functions, while preserving the decimal places for the numbers.
I've discovered that if I pass a float like '10.00' in to a function, then the decimal places don't get used. This messes an operation like calculating percentages.
For example, x * (10 / 100) will always return 0.
But if I manage to preserve the decimal places, I end up doing x * (10.00 / 100). This returns an accurate result.
I'd like to have a technique that enables consistency when I'm working with numbers that decimal places that can hold zeroes.
When you write
10 / 100
you are performing integer division. That's because both operands are integers. The result is 0.
If you want to perform floating point division, make one of the operands be a floating point value. For instance:
10.0 / 100
or
float(10) / 100
Do beware also that
10.0 / 100
results in a binary floating point value and binary floating data types cannot represent the true result value of 0.1. So if you want to represent the result accurately you may need to use a decimal data type. The decimal module has the functionality needed for that.
Division in python for float and int works differently, take a look at this question and it's answers: Python division.
Moreover, if you are looking for a solution to format a decimal floating point of your figures into string, you might need to use %f.
Python
# '1.000000'
"%f" % (1.0)
# '1.00'
"%.2f" % (1.0)
# ' 1.00'
"%6.2f" % (1.0)
Python 2.x will use integer division when dividing two integers unless you explicitly tell it to do otherwise. Two integers in --> one integer out.
Python 3 onwards will return, to quote PEP 238 http://www.python.org/dev/peps/pep-0238/ a reasonable approximation of the result of the division approximation, i.e. it will perform a floating point division and return the result without rounding.
To enable this behaviour in earlier version of Python you can use:
from __future__ import division
At the very top of the module, this should get you the consistent results you want.
You should use the decimal module. Each number knows how many significant digits it has.
If you're trying to preserve significant digits, the decimal module is has everything you need. Example:
>>> from decimal import Decimal
>>> num = Decimal('10.00')
>>> num
Decimal('10.00')
>>> num / 10
Decimal('1.00')

Incremented floats do not equal each other [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why can't decimal numbers be represented exactly in binary?
Program not entering if statement
So I'm trying to run a program that has two variables, when one variable is equal to another, it performs a function. In this case, printing spam. However, for some reason, when I run this program, I'm not getting any output even though I know they are equal.
g=0.0
b=3.0
while g < 30.0:
if g==b:
print "Hi"
g+=.1
print g, b
You are assuming that adding .1 enough times to 0.0 will produce 3.0. These are floating point numbers, they are inaccurate. Rounding errors make it so that the value is never exactly equal to 3.0. You should almost never use == to test floating point numbers.
A good way to do this is to count with integer values (e.g., loop with i from 0 to 300 by 1) and scale the counter only when the float value is used (e.g., set f = i * .1). When you do this, the loop counter is always exact, so you get exactly the iterations you want, and there is only one floating-point rounding, which does not accumulate from iteration to iteration.
The loop counter is most commonly an integer type, so that addition is easily seen to be exact (until overflow is reached). However, the loop counter may also be a floating-point type, provided you are sure that the values and operations for it are exact. (The common 32-bit floating-point format represents integers exactly from -224 to +224. Outside that, it does not have the precision to represent integers exactly. It does not represent .1 exactly, so you cannot count with increments of .1. But you could count with increments of .5, .25, .375, or other small multiples of moderate powers of two, which are represented exactly.)
To expand on Karoly Horvath's comment, what you can do to test near-equality is choose some value (let's call it epsilon) that is very, very small relative to the minimum increment. Let's say epsilon is 1.0 * 10^-6, five orders of magnitude smaller than your increment. (It should probably be based on the average rounding error of your floating point representation, but that varies, and this is simply an example).
What you then do is check if g and b are less than epsilon different - if they are close enough that they are practically equal, the difference between practically and actually being the rounding error, which you're approximating with epsilon.
Check for
abs(g - b) < epsilon
and you'll have your almost-but-not-quite equality check, which should be good enough for most purposes.

Categories