2

This question must be an easy one but I couldn't find anything about it here.

Is there a way in python to define or use 1 command line so that every float number across the script will be rounded by X digists.

For example if I define:

max_float_digits = 4

and the program wants to outputs or print the following number 5.1355895682, all I will see is 5.1356

This necessity rises when some module return this value to my script:

[('user_ctr', 2253.459088429824), ('user_clicks', 2042.3175666666664), ('t_avg_sim', 176.83513843049306), ('item_ctr', 137.6051319164618), ('item_age', 128.52122456437388), ('t_max_sim', 126.23752629310347), ('item_read_time', 46.94452635514022), ('clicks', 26.368489035532996), ('likes', 15.43922933884298), ('c_max_sim', 14.761540559999993), ('no_clicks', 13.020515220883516), ('c_avg_sim', 10.969965476190472), ('user_read_time', 10.21293115646259), ('user_age', 9.776781678832117)]

Instead of trying to access each value and round them I'd like a 1 command line that will round them all for me.

Eran Moshe
  • 3,062
  • 2
  • 22
  • 41
  • Rounding floats is [an illusion](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). – Ignacio Vazquez-Abrams Dec 04 '17 at 07:11
  • 2
    How hard is it to just use `str.format`? – cs95 Dec 04 '17 at 07:15
  • Hard when you get outputs and returned lists and lists of tuples to start accessing those around and round them all over your program :> – Eran Moshe Dec 04 '17 at 07:17
  • There's a cute hack [here](https://stackoverflow.com/a/12442955/4014959) that will print floats at a given precision, but it only works on individual float objects, not floats inside a collection. I guess you could modify it to make it recursively search the built-in collection types for floats, but that will slow down _all_ printing. – PM 2Ring Dec 04 '17 at 08:11

3 Answers3

2

Sorry, Python doesn't provide global settings to control formatting. If you don't want to display floats at their full precision then you need to format them explicitly. I suppose that's not so convenient when you print a collection containing floats. However, the __repr__ of Python's collection objects (tuple, list, dict, set) is primarily intended for displaying these objects to programmers, not to users. It is expected that you will format such objects if you intend to display them to users, not just dump them to the terminal.

If you want to use the same formatting in multiple places it may be worthwhile to define a function. Eg,

max_float_digits = 4
def rounded(val):
    return '{:.{}f}'.format(val, max_float_digits)

data = [
    ('user_ctr', 2253.459088429824), ('user_clicks', 2042.3175666666664), 
    ('t_avg_sim', 176.83513843049306), ('item_ctr', 137.6051319164618), 
    ('item_age', 128.52122456437388), ('t_max_sim', 126.23752629310347), 
    ('item_read_time', 46.94452635514022), ('clicks', 26.368489035532996), 
    ('likes', 15.43922933884298), ('c_max_sim', 14.761540559999993), 
    ('no_clicks', 13.020515220883516), ('c_avg_sim', 10.969965476190472), 
    ('user_read_time', 10.21293115646259), ('user_age', 9.776781678832117),
]

out_data = [(key, rounded(val)) for key, val in data]
for row in out_data:
    print(row)

output

('user_ctr', '2253.4591')
('user_clicks', '2042.3176')
('t_avg_sim', '176.8351')
('item_ctr', '137.6051')
('item_age', '128.5212')
('t_max_sim', '126.2375')
('item_read_time', '46.9445')
('clicks', '26.3685')
('likes', '15.4392')
('c_max_sim', '14.7615')
('no_clicks', '13.0205')
('c_avg_sim', '10.9700')
('user_read_time', '10.2129')
('user_age', '9.7768')

If you actually want the string representation of the list, and you want it to look as shown in the question, you can use the built-in round function.

output = [(key, round(val, max_float_digits)) for key, val in data]
print(output)    

output

[('user_ctr', 2253.4591), ('user_clicks', 2042.3176), ('t_avg_sim', 176.8351), ('item_ctr', 137.6051), ('item_age', 128.5212), ('t_max_sim', 126.2375), ('item_read_time', 46.9445), ('clicks', 26.3685), ('likes', 15.4392), ('c_max_sim', 14.7615), ('no_clicks', 13.0205), ('c_avg_sim', 10.97), ('user_read_time', 10.2129), ('user_age', 9.7768)]

To turn any object into its string representation, pass it to repr or str, eg

s = repr(output)
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • I guess that's the closest thing I can do.. I know that in R I can do it in 1 line in the start of the program and it'll affect all the outputs that comes later on... – Eran Moshe Dec 04 '17 at 08:59
  • @EranMoshe You can do similar things in 3rd-party numeric processing frameworks like Numpy, which has [`numpy.set_printoptions`](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.set_printoptions.html). – PM 2Ring Dec 04 '17 at 09:03
0

No, there isn't such a parameter, but you can probably achieve a similar thing with the decimal module if you wan't to use decimal numbers instead of IEEE754 floating-point types.

See the example (from the URL https://docs.python.org/2/library/decimal.html):

>>> from decimal import *
>>> getcontext().prec = 6
>>> Decimal(1) / Decimal(7)
Decimal('0.142857')
>>> getcontext().prec = 28
>>> Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')

Of course if you need both speed and high precision you can also use modules like mpmath or gmpy but I think that decimal is worth a try in your own case (it is a module from the standard library).

Thomas Baruchel
  • 7,236
  • 2
  • 27
  • 46
  • Alas.. I have an already working program that uses other modules where I cant control their returned values. I just wanted to restrict them to 4 digits after the . – Eran Moshe Dec 04 '17 at 07:15
  • @EranMoshe Why do you want to introduce rounding errors like that? Normally, rounding should only be done to display floats, not while you're still doing arithmetic with them. – PM 2Ring Dec 04 '17 at 07:16
  • Well then, round them only when I output them – Eran Moshe Dec 04 '17 at 07:18
0

How about having you own method defined and put it as a decorator for each of the methods you need output in the format you want :

Try out this sample :

import math
max_float_digits = 4
v = 8.8333333333333339
def hyper(v,max_no_float):
    rounding = pow(10,max_no_float)
    return (math.ceil(v * rounding) / rounding)
print hyper(v,max_float_digits)
print hyper(v,3)

Output :

>>>8.8334
>>>8.834

Give it a try .

coder3521
  • 2,608
  • 1
  • 28
  • 50