Format python decimal as [-.0-9]*, no trailing zeros, value-preserving - python

I'm looking for a method which takes a decimal.Decimal object x and returns a string s, such that:
x == Decimal(s)
re.match("^-?(0|[1-9][0-9]*)(.[0-9]*[1-9])?$", s) is not None
s != '-0'
In other words, the method doesn't change the value of the Decimal object; it returns a string representation which is never in scientific notation (e.g. 1e80), and never has any trailing zeros.
I would assume there is a standard library function which lets me do that, but I haven't found any. Do you know of any?

n.normalize() truncates decimals with more than 28 digits of precision, but you can use string formatting and a manual check for negative zero:
'{:f}'.format(abs(n) if n.is_zero() else n)

Related

Convert a decimal number into a byte size of 8 bit array

x = 64
var_in_bin_format = bin(64)
print(var_in_bin_format)
#Output
#0b1000000
#Desired Output -- > should always be in 8 bit format
#0b01000000
def call_another_api(var_in_bin_format):
pass
In Python, I need to call an API that expects its parameter to be always in 8 bit format regardless of the value of the decimal number?
I am not that good in bit manipulation so I am thinking if there is something I can do here?
How can I do this? I cannot use the format() function as it will convert the value into a string representation and the API that I am calling will alert me that it is not in the correct format.
Even though you say that you can't use format() because it returns a string, I'm going to post this because that's also what bin() does. bin(x) is equivalent to format(x, '#b'). I'd guess that you haven't added the '#', which means you won't have '0b' leading the value.
The Python 3 documentation for bin() actually gives a pretty strong hint about how you might do this, using format instead.
If you know that the value passed will not be negative, you can use the format string '#010b':
format(x, '#010b')
Breaking this down:
'b' means that the number will be a string binary representation.
'10' means that the entire string will be 10 characters long, two for '0b' and 8 for the value.
'0' makes it pad with '0' instead of ' '.
'#' will add the '0b' prefix, as done by bin().
Note that this assumes that the number is an integer in the range [0, 255]. Integers outside this range will generate valid representations, but will not match the format expected, and may have a leading '-'. Objects of type float can not be converted with the 'b' format code. I assume that these are not problems, given what your intended output is, but it might be a good idea to add an explicit check to throw a ValueError if the value is less than 0 or greater than 255.
If you're in Python 3.6+, you could also use f-strings:
f'{x:#010b}'
Is is not possible to convert all decimal numbers to 8 bit. You can only convert numbers from 0 to 255 in 8 bits.

Python : Conversion of float to string keeping all digits of decimal place

I have a task to count the digits after decimal place.
For which I am using the below code :
str(string_name)[::-1].find('.')
But while converting float to string, it is removing the trailing zeroes :
my_string = 16.520440
print(str(my_string ))
output - '16.52044'
expected output - '16.520440'
Since that trailing zero is not a significant digit, there is no way you can do this within the given paradigm. Floats are stored by value, not by the visual representation of the original input. For instance:
my_string_1 = 16.52044
my_string_2 = 16.520440
my_string_3 = 16.5204400
my_string_4 = 016.5204400
All assign exactly the same value to the LHS variable: they have the same binary representation. Leading and trailing zeros do not, by definition, affect this value. There is no way to differentiate the original input from the stored value.
If you want some such effect, then you need to use some other form of original value that incorporates that information. For instance, you could add a second variable that denotes the quantity of significant decimal places -- which is the opposite of what you're trying to achieve -- or perhaps make the original input a string, from which you could derive that information.

Number to string conversion with f-string without leading or trailing zeros?

Update: as up to now (2019-09), masking leading or trailing zeros in decimal numbers formatted to string seems to be unsupported in
Python. You will need to use a workaround to get something like '.01'
from the number 0.0101 (assuming 3 decimal places desired).
I would even argue that it's a good thing not to support such a format
since
I'd consider '0.01' be better in terms of readability than '.01'
'0.010' carries information (3 digits of precision...) that is lost in '0.01'
If desired anyway, one could use one of the suggestions below. Thank you all for contributing.
Q: I'm looking for a way to output floating point numbers as strings, formatted without leading/trailing zeros. Is there a way to do this with '{ }'.format() or f-string? I searched the internet but didn't find anything. Did I just miss it or is it not possible (Python 3.7)?
What I have in mind is basically
some_number = 0.3140
string = f'{some_number:x}' # giving '.314'
that gives the output string '.314'.. So is there an x that does this?
Of course one could work-around with lstrip / rstrip as described e.g. here or similar here:
In [93]: str(0.3140).lstrip('0').rstrip('0')
Out[93]: '.314'
but it would be more convenient to use only an f-string. Since I can use that for other formatting options, optionally calling strip demands additional lines of code.
just to mention it here... if you use numpy there's also the function np.format_float_positional() that gives you full control of how to display numbers as strings and trim trailing zeros...
from the doc of np.format_float_positional:
Format a floating-point scalar as a decimal string in positional notation.
Provides control over rounding, trimming and padding. Uses and assumes IEEE >unbiased rounding. Uses the "Dragon4" algorithm.
getting rid of the leading zero is then just a simple check whether we're dealing with a number that starts with 0 or not
import numpy as np
def format_float(x, trim_leading=True, **kwargs):
s = np.format_float_positional(x, **kwargs)
if trim_leading is True and int(x) == 0 and len(s) > 1:
s = s.replace("0.", ".")
return s
format_float(0.0234001232, trim_leading=True, precision=4)
>>> '.0234'
format_float(0.0234001232, trim_leading=False, precision=4)
>>> '0.0234'
format_float(0.0234001232, precision=8)
>>> '0.02340012'
format_float(12.000558, precision=2, trim="-", fractional=False)
>>> '12'
if you want to just strip 0 from floating numbers you could use this "hack"
"." + str(0.314).split("0.")[-1]
this is in no way an elegant solution, but it will get the job done
also if you want to use .format as well you don't need another line, you could just
"." +str(0.314).split("0.")[-1].format('')
If you want to use format(), then try as below.
print("Hello {0}, your balance is {1}.".format("Adam", "0.314".lstrip('0')))
Just use lstrip() within format function, you don't need to write additional line of code.
here's a helper function that I came up with since the strip workaround can't be avoided:
def dec2string_stripped(num, dec_places=3, strip='right'):
"""
Parameters
----------
num : float or list of float
scalar or list of decimal numbers.
dec_places : int, optional
number of decimal places to return. defaults to 3.
strip : string, optional
what to strip. 'right' (default), 'left' or 'both'.
Returns
-------
list of string.
numbers formatted as strings according to specification (see kwargs).
"""
if not isinstance(num, list): # might be scalar or numpy array
try:
num = list(num)
except TypeError: # input was scalar
num = [num]
if not isinstance(dec_places, int) or int(dec_places) < 1:
raise ValueError(f"kwarg dec_places must be integer > 1 (got {dec_places})")
if strip == 'right':
return [f"{n:.{str(dec_places)}f}".rstrip('0') for n in num]
if strip == 'left':
return [f"{n:.{str(dec_places)}f}".lstrip('0') for n in num]
if strip == 'both':
return [f"{n:.{str(dec_places)}f}".strip('0') for n in num]
raise ValueError(f"kwarg 'strip' must be 'right', 'left' or 'both' (got '{strip}')")

Understanding the Python %g in string formatting, achieving Java String.format behavior

In Java:
String test1 = String.format("%7.2g", 3e9);
System.out.println(test1);
This prints 3.0e+09
In Python 2.7, if I run this code
for num in [3e9, 3.1e9, 3.01e9, 3e2, 3.1e2, 3.01e2]:
print '%7.2g %7.2f %7.2e' % (num, num, num)
I get
3e+09 3000000000.00 3.00e+09
3.1e+09 3100000000.00 3.10e+09
3e+09 3010000000.00 3.01e+09
3e+02 300.00 3.00e+02
3.1e+02 310.00 3.10e+02
3e+02 301.00 3.01e+02
Huh? It looks like the precision (.2) argument is treated totally differently in Python's %g than in Python's %f, Python's %e, or Java's %g. Here's the doc (my emphasis):
General format. For a given precision p >= 1, this rounds the number to p significant digits and then formats the result in either fixed-point format or in scientific notation, depending on its magnitude.
The precise rules are as follows: suppose that the result formatted with presentation type 'e' and precision p-1 would have exponent exp. Then if -4 <= exp < p, the number is formatted with presentation type 'f' and precision p-1-exp. Otherwise, the number is formatted with presentation type 'e' and precision p-1. In both cases insignificant trailing zeros are removed from the significand, and the decimal point is also removed if there are no remaining digits following it.
Positive and negative infinity, positive and negative zero, and nans, are formatted as inf, -inf, 0, -0 and nan respectively, regardless of the precision.
A precision of 0 is treated as equivalent to a precision of 1. The default precision is 6.
WTF? Is there any way to prevent those trailing zeros from being removed? The whole point of string formatting is to achieve some consistency, e.g. for text alignment.
Is there any way to get the Java behavior (essentially the number of significant digits to the right of the decimal point) without having to rewrite the whole thing from scratch?
With the format method, you can do something like this:
for num in [3e9, 3.1e9, 3.01e9, 3e2, 3.1e2, 3.01e2]:
print ('{n:7.2{c}} {n:7.2f} {n:7.2e}'.format(n=num, c='e' if num > 1e4 else 'f'))
Output:
3.00e+09 3000000000.00 3.00e+09
3.10e+09 3100000000.00 3.10e+09
3.01e+09 3010000000.00 3.01e+09
300.00 300.00 3.00e+02
310.00 310.00 3.10e+02
301.00 301.00 3.01e+02
There are two parts to it that might not be so well known.
1. Parametrizing the string formatting
In addition to simple formatting:
>>> '{}'.format(3.5)
'3.5'
and formatting with more detailed specification of the result:
>>> '{:5.2f}'.format(3.5)
' 3.50'
you can use keyword arguments in formatthat you can access in the string :
>>> '{num:5.2f}'.format(num=3.5)
' 3.50'
You can use these also for the format specification itself:
>>> '{:5.{deci}f}'.format(3.5, deci=3)
'3.500'
2. The if expression
In addition to the if statement there is an if expression, a.k.a. ternary operator.
So, this expression:
a = 1
b = 2
res = 10 if a < b else 20
is equivalent to this statement:
if a < b:
res = 10
else:
res= 20
Putting both together yields something like this:
'{num:7.2{c}}'.format(num=num, c='e' if num > 1e4 else 'f')
The formatting that python does is more consistent with C's printf style formatting, which also drops trailing zeros for the g conversion. Since python's reference implementation is in C, why should it be consistent with Java in this case?
When using the % operator for string formatting, the relevant documentation is String Formatting Operations, which has some differences to the one you linked to, notably that it allows the # alternate form for g:
The alternate form causes the result to always contain a decimal point, and trailing zeroes are not removed as they would otherwise be.
The precision determines the number of significant digits before and after the decimal point and defaults to 6.
So in your case:
>>> "%#7.2g" % 3e9
3.0e+09
This is different from what is allowed by str.format(), where # is used to enable prefixes for binary, octal or hexadecimal output (at least in python2, this was changed in python3).

How do I preserve leading zeros in Python integers for string formatting

I have what I believe to be a simple question. I have the following code and the output is not what I expect:
import socket
import time
r=031508.78
h=373309.9
z=0
mes='"TSRA %s %s %s\r\n"'%(r,h,z)
print mes
My problem is that r prints as '31508.78' and I want '031508.78'. How I get preserve the leading zero?
The 0 in front of the number is not important for python, because numbers have no leading 0s. You can use:
print '%09.2f'%r
I prefer the new string format mini language. I find it more intuitive.
r = 31508.78
h = 373309.9
z = 0
print '"TSRA {0: 011.3f} {1: 011.3f} {2}"\r\n'.format(r, h, z)
print '{0: 011.3f}\n{1: 011.3f}\n{2: 011.3f}'.format(r, h, z)
{0: 011.3f} 0: indicates the first item (r). 011 tells the formatter that there will be 11 characters including the decimal point and a place holder for a negative sign. By placing a 0 in front the formatter will pad the space with leading zeros. Finally, .3f indicates the precision and a fixed number of places after the decimal point.
Yields:
"TSRA 031508.780 373309.900 0"
031508.780
373309.900
000000.000
I don't think there is any way to store a number with a leading zero in python.
This leaves you with two ways:
1) Make r a string i.e. r="031408.78"
I think this is the best method for you as you can do all the arithmetic you want to do (using the implicit typecasting of python) and see the number appended with a 0 wherever you use it.
2) If you simply want to print the number with leading zeros consider using this
print "%09.2f' % r #for this you need the length of the number
For Python, there is no difference in the following things:
31508.78
031508.78
0000000031508.78
31508.7800000000000
0000000000000031508.7800000000000
Those are all valid representations of the exact same number, which is canonically represented by 31508.78, which is essentially just a convention. Different representations are possible too, and actually, the number is represented in a way that’s described by the IEEE-754 standard.
So, when you want that leading zero, you have to tell Python explicitely to put it there because it simply doesn’t know about it. You just used one of many valid representations to specify that one number which default string representation does not include leading zeroes.
The best way to do this is using string formatting, which comes in two flavors. The old style flavor, using the % operator, and the newer str.format function:
>>> '%09.2f' % 31508.78
'031508.78'
>>> '{:09.2f}'.format(31508.78)
'031508.78'
Or, if you don’t actually want a number, you could also just specify the number as a string to begin with, which of course keeps the leading zero.

Categories