Unfortunately it seems that not even the new-style formatting with float.__format__ supports this. The default formatting of floats is the same as with repr; and with f flag there are 6 fractional digits by default:
>>> format(0.0000000005, 'f')
'0.000000'
However there is a hack to get the desired result - not the fastest one, but relatively simple:
- first the float is converted to a string using
str()orrepr() - then a new
Decimalinstance is created from that string. Decimal.__format__supportsfflag which gives the desired result, and, unlikefloats it prints the actual precision instead of default precision.
Thus we can make a simple utility function float_to_str:
import decimal
# create a new context for this task
ctx = decimal.Context()
# 20 digits should be enough for everyone :D
ctx.prec = 20
def float_to_str(f):
"""
Convert the given float to a string,
without resorting to scientific notation
"""
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
Care must be taken to not use the global decimal context, so a new context is constructed for this function. This is the fastest way; another way would be to use decimal.local_context but it would be slower, creating a new thread-local context and a context manager for each conversion.
This function now returns the string with all possible digits from mantissa, rounded to the shortest equivalent representation:
>>> float_to_str(0.1)
'0.1'
>>> float_to_str(0.00000005)
'0.00000005'
>>> float_to_str(420000000000000000.0)
'420000000000000000'
>>> float_to_str(0.000000000123123123123123123123)
'0.00000000012312312312312313'
The last result is rounded at the last digit
As @Karin noted, float_to_str(420000000000000000.0) does not strictly match the format expected; it returns 420000000000000000 without trailing .0.
Unfortunately it seems that not even the new-style formatting with float.__format__ supports this. The default formatting of floats is the same as with repr; and with f flag there are 6 fractional digits by default:
>>> format(0.0000000005, 'f')
'0.000000'
However there is a hack to get the desired result - not the fastest one, but relatively simple:
- first the float is converted to a string using
str()orrepr() - then a new
Decimalinstance is created from that string. Decimal.__format__supportsfflag which gives the desired result, and, unlikefloats it prints the actual precision instead of default precision.
Thus we can make a simple utility function float_to_str:
import decimal
# create a new context for this task
ctx = decimal.Context()
# 20 digits should be enough for everyone :D
ctx.prec = 20
def float_to_str(f):
"""
Convert the given float to a string,
without resorting to scientific notation
"""
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
Care must be taken to not use the global decimal context, so a new context is constructed for this function. This is the fastest way; another way would be to use decimal.local_context but it would be slower, creating a new thread-local context and a context manager for each conversion.
This function now returns the string with all possible digits from mantissa, rounded to the shortest equivalent representation:
>>> float_to_str(0.1)
'0.1'
>>> float_to_str(0.00000005)
'0.00000005'
>>> float_to_str(420000000000000000.0)
'420000000000000000'
>>> float_to_str(0.000000000123123123123123123123)
'0.00000000012312312312312313'
The last result is rounded at the last digit
As @Karin noted, float_to_str(420000000000000000.0) does not strictly match the format expected; it returns 420000000000000000 without trailing .0.
If you are satisfied with the precision in scientific notation, then could we just take a simple string manipulation approach? Maybe it's not terribly clever, but it seems to work (passes all of the use cases you've presented), and I think it's fairly understandable:
def float_to_str(f):
float_string = repr(f)
if 'e' in float_string: # detect scientific notation
digits, exp = float_string.split('e')
digits = digits.replace('.', '').replace('-', '')
exp = int(exp)
zero_padding = '0' * (abs(int(exp)) - 1) # minus 1 for decimal point in the sci notation
sign = '-' if f < 0 else ''
if exp > 0:
float_string = '{}{}{}.0'.format(sign, digits, zero_padding)
else:
float_string = '{}0.{}{}'.format(sign, zero_padding, digits)
return float_string
n = 0.000000054321654321
assert(float_to_str(n) == '0.000000054321654321')
n = 0.00000005
assert(float_to_str(n) == '0.00000005')
n = 420000000000000000.0
assert(float_to_str(n) == '420000000000000000.0')
n = 4.5678e-5
assert(float_to_str(n) == '0.000045678')
n = 1.1
assert(float_to_str(n) == '1.1')
n = -4.5678e-5
assert(float_to_str(n) == '-0.000045678')
Performance:
I was worried this approach may be too slow, so I ran timeit and compared with the OP's solution of decimal contexts. It appears the string manipulation is actually quite a bit faster. Edit: It appears to only be much faster in Python 2. In Python 3, the results were similar, but with the decimal approach slightly faster.
Result:
Python 2: using
ctx.create_decimal():2.43655490875Python 2: using string manipulation:
0.305557966232Python 3: using
ctx.create_decimal():0.19519368198234588Python 3: using string manipulation:
0.2661344590014778
Here is the timing code:
from timeit import timeit
CODE_TO_TIME = '''
float_to_str(0.000000054321654321)
float_to_str(0.00000005)
float_to_str(420000000000000000.0)
float_to_str(4.5678e-5)
float_to_str(1.1)
float_to_str(-0.000045678)
'''
SETUP_1 = '''
import decimal
# create a new context for this task
ctx = decimal.Context()
# 20 digits should be enough for everyone :D
ctx.prec = 20
def float_to_str(f):
"""
Convert the given float to a string,
without resorting to scientific notation
"""
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
'''
SETUP_2 = '''
def float_to_str(f):
float_string = repr(f)
if 'e' in float_string: # detect scientific notation
digits, exp = float_string.split('e')
digits = digits.replace('.', '').replace('-', '')
exp = int(exp)
zero_padding = '0' * (abs(int(exp)) - 1) # minus 1 for decimal point in the sci notation
sign = '-' if f < 0 else ''
if exp > 0:
float_string = '{}{}{}.0'.format(sign, digits, zero_padding)
else:
float_string = '{}0.{}{}'.format(sign, zero_padding, digits)
return float_string
'''
print(timeit(CODE_TO_TIME, setup=SETUP_1, number=10000))
print(timeit(CODE_TO_TIME, setup=SETUP_2, number=10000))
python - Casting float to string without scientific notation - Stack Overflow
How do I convert scientific notation number to float?
python - How to suppress scientific notation when printing float values? - Stack Overflow
python - Converting float to string without scientific notation - Stack Overflow
Videos
You can just use the standard string formatting option stating the precision you want
>>> fl = 0.000005
>>> print '%.6f' % fl
0.000005
Use
fl = 0.00005
s = '%8.5f' % fl
print s, type(s)
Gives
0.00005 <type 'str'>
In case you want no extra digits, use %g (although it uses exponential notation for e.g. 0.000005). See for example:
fl = 0.0005
s = '%g' % fl
print s, type(s)
fl = 0.005
s = '%g' % fl
print s, type(s)
Gives
0.0005 <type 'str'>
0.005 <type 'str'>
num = 4.04e-10
Tried searching online but none of the methods actually worked:/
Using the newer version ''.format (also remember to specify how many digit after the . you wish to display, this depends on how small is the floating number). See this example:
>>> a = -7.1855143557448603e-17
>>> '{:f}'.format(a)
'-0.000000'
as shown above, default is 6 digits! This is not helpful for our case example, so instead we could use something like this:
>>> '{:.20f}'.format(a)
'-0.00000000000000007186'
Update
Starting in Python 3.6, this can be simplified with the new formatted string literal, as follows:
>>> f'{a:.20f}'
'-0.00000000000000007186'
'%f' % (x/y)
but you need to manage precision yourself. e.g.,
'%f' % (1/10**8)
will display zeros only.
details are in the docs
Or for Python 3 the equivalent old formatting or the newer style formatting
You need to apply a string formatter to it, instead of just wrapping it in a string. Specifically you can:
df["id"].apply("{:.0f}".format)
You can simply change "{:.0f}" to "{:.1f}" or "{:.2f}" ... "{:.nf}" to be able to keep 0, 1, 2, ... n decimal places respectively.
Or if you want to see all numbers to the right of the decimal, then this should work:
df["id"].apply("{:f}".format)
Maybe you should try using the str() method? (not tested)
df['id'] = str(df['id'])
You can use map or apply, as mentioned in this comment:
print (df.userid.map(lambda x: '{:.0f}'.format(x)))
0 nan
1 109117800000
2 113785600000
Name: userid, dtype: object
df.userid = df.userid.map(lambda x: '{:.0f}'.format(x))
print (df)
userid
0 nan
1 109117800000
2 113785600000
I wondered whether map would be faster, but it is the same:
#[300000 rows x 1 columns]
df = pd.concat([df]*100000).reset_index(drop=True)
#print (df)
In [40]: %timeit (df.userid.map(lambda x: '{:.0f}'.format(x)))
1 loop, best of 3: 211 ms per loop
In [41]: %timeit (df.userid.apply(lambda x: '{:.0f}'.format(x)))
1 loop, best of 3: 210 ms per loop
Another solution is to_string, but it is slow:
print(df.userid.to_string(float_format='{:.0f}'.format))
0 nan
1 109117800000
2 113785600000
In [41]: (df.userid.to_string(float_format='{:.0f}'.format))
1 loop, best of 3: 2.52 s per loop
I just stumbled upon this problem after reading a dataframe from a json file using the read_json method and unfortunately it does not have a keep_default_na parameter.
The solution was to convert the long floats to np.int64 before converting them to str.
In [53]: tweet_id_sample = tweets.iloc[0]['id']
tweet_id_sample
Out[53]: 8.924206435553362e+17
In [54]: tweet_id_sample.astype(str)
Out[54]: '8.924206435553362e+17'
In [55]: tweet_id_sample.astype(np.int64).astype(str)
Out[55]: '892420643555336192'
In [56]: # This overflows
tweet_id_sample.astype(int)
Out[56]: -2147483648
As Josh Friedlander mentions you are comparing an int to a float which will always be invalid.
In order to represent a float without scientific notation you can use Decimal but this is also not going to make your comparison to an int valid...
>>> from decimal import Decimal
>>> n = float('9400108205499941468492')
>>> format(Decimal.from_float(n))
'9400108205499941388288'
>>> y = format(Decimal.from_float(n))
>>> type(y)
<class 'str'>
you can use int() instead of float and when you add 0.5 or any other float then if you want the approximate value then you use int