[Tutor] nice()

Alan Gauld alan.gauld at freenet.co.uk
Mon Feb 13 00:24:45 CET 2006


I have no particularly strong view on the concept (except that I usually
see the "problem" as a valuable opportunity to introduce a concept
that has far wider reaching consequences than floating point
numbers!).

However I do dislike the name nice() - there is already a nice() in the
os module with a fairly well understood function. But I'm sure some
time with a thesaurus can overcome that single mild objection. :-)

Alan G
Author of the learn to program web tutor
http://www.freenetpages.co.uk/hp/alan.gauld



----- Original Message ----- 
From: "Smith" <smiles at worksmail.net>
To: <tutor at python.org>
Cc: <edu-sig at python.org>; <python-dev at python.org>
Sent: Sunday, February 12, 2006 6:44 PM
Subject: [Tutor] nice()


I've been thinking about a function that was recently proposed at python-dev 
named 'areclose'. It is a function that is meant to tell whether two (or 
possible more) numbers are close to each other. It is a function similar to 
one that exists in Numeric. One such implementation is

def areclose(x,y,abs_tol=1e-8,rel_tol=1e-5):
    diff = abs(x-y)
    return diff <= ans_tol or diff <= rel_tol*max(abs(x),abs(y))

(This is the form given by Scott Daniels on python-dev.)

Anyway, one of the rationales for including such a function was:

  When teaching some programming to total newbies, a common frustration
  is how to explain why a==b is False when a and b are floats computed
  by different routes which ``should'' give the same results (if
  arithmetic had infinite precision).  Decimals can help, but another
  approach I've found useful is embodied in Numeric.allclose(a,b) --
  which returns True if all items of the arrays are ``close'' (equal to
  within certain absolute and relative tolerances)
The problem with the above function, however, is that it *itself* has a 
comparison between floats and it will give undesired result for something 
like the following test:

###
>>> print areclose(2, 2.1, .1, 0) #see if 2 and 2.1 are within 0.1 of each 
>>> other
False
>>>
###

Here is an alternative that might be a nice companion to the repr() and 
round() functions: nice(). It is a combination of Tim Peter's delightful 
'case closed' presentation in the thread, "Rounding to n significant 
digits?" [1] and the hidden magic of "prints" simplification of floating 
point numbers when being asked to show them.

It's default behavior is to return a number in the form that the number 
would have when being printed. An optional argument, however, allows the 
user to specify the number of digits to round the number to as counted from 
the most significant digit. (An alternative name, then, could be 'lround' 
but I think there is less baggage for the new user to think about if the 
name is something like nice()--a function that makes the floating point 
numbers "play nice." And I also think the name...sounds nice.)

Here it is in action:

###
>>> 3*1.1==3.3
False
>>> nice(3*1.1)==nice(3.3)
True
>>> x=3.21/0.65; print x
4.93846153846
>>> print nice(x,2)
4.9
>>> x=x*1e5; print nice(x,2)
490000.0
###

Here's the function:
###
def nice(x,leadingDigits=0):
 """Return x either as 'print' would show it (the default) or rounded to the
 specified digit as counted from the leftmost non-zero digit of the number,

 e.g. nice(0.00326,2) --> 0.0033"""
 assert leadingDigits>=0
 if leadingDigits==0:
  return float(str(x)) #just give it back like 'print' would give it
 leadingDigits=int(leadingDigits)
 return float('%.*e' % (leadingDigits,x)) #give it back as rounded by the %e 
format
###

Might something like this be useful? For new users, no arguments are needed 
other than x and floating points suddenly seem to behave in tests made using 
nice() values. It's also useful for those computing who want to show a 
physically meaningful value that has been rounded to the appropriate digit 
as counted from the most significant digit rather than from the decimal 
point.

Some time back I had worked on the significant digit problem and had several 
math calls to figure out what the exponent was. The beauty of Tim's solution 
is that you just use built in string formatting to do the work. Nice.

/c

[1] http://mail.python.org/pipermail/tutor/2004-July/030324.html 



More information about the Tutor mailing list