Reputation: 1585
I want to access the source code the built-in function round()
, to allow me to create a very similar function. How can i access this source code and how easily will this be to edit / use?
The reason I am interested in doing this is that the built-in function round()
converts an integer into float even when the number of digits is negative.
For Example:
round(1234.5678,-2)
Returns
1200.0
I want to create a function that returns an integer. I am sure there other methods of achieving the same result, but I want to see how the built in function achieves this task as I would expect this to be reasonably efficient.
Upvotes: 0
Views: 5808
Reputation: 16164
just expanding the comment from Mark Dickinson and to make sure I understand it myself, the CPython round
function is spread over several parts of the code base.
round(number, ndigits)
starts by looking up and invoking the __round__
method on the object. this is implemented by the C function builtin_round_impl
in bltinmodule.c
for float
s this invokes the float.__round__
method, which is implemented in float___round___impl
in floatobject.c:1045 but there's a stub entry point in floatobject.c.h that I think is mostly maintained by Python's argument clinic tool. this header is also where its PyMethodDef
is defined as FLOAT___ROUND___METHODDEF
the C function float___round___impl
starts by checking if ndigits
was not specified (i.e. nothing passed, or passed as None
), in this case then it calls round
from the C standard library (or the version from pymath.c as a fallback).
if ndigits
is specified then it probably calls the version of double_round
in floatobject.c:927. this works in 53bit precision, so adjusts floating point rounding modes and is generally pretty fiddly code, but basically it converts the double to a string with a given precision, and then converts back to a double
for a small number of platforms
there's another version of double_round
at floatobject.c:985 that does the obvious thing of basically round(x * 10**ndigits) / 10**ndigits
, but these extra operations can reduce precision of the result
note that the higher precision version will give different answers to the version in NumPy and equivalent version in R, as commented on here. for example, round(0.075, 2)
results in 0.07 with the builtin round, while numpy and R give 0.08. the easiest way I've found of seeing what's going on is by using the decimal
module to see the full decimal expansion of the float:
from decimal import Decimal
print(Decimal(0.075))
gives: 0.0749999999999999972…
, i.e. 0.075 can't be accurately represented by a (binary) floating point number and the closest number happens to be slightly smaller, and hence it rounds down to 0.07. while the implementation in numpy gives 0.08 because it effectively does round(0.075 * 100) / 100
and the intermediate value happens to round up, i.e:
print(Decimal(0.075 * 100))
giving exactly 7.5
, which rounds exactly to 8
.
Upvotes: 5
Reputation: 987
The source seems to be: https://github.com/python/cpython/blob/master/Python/pymath.c
double
round(double x)
{
double absx, y;
absx = fabs(x);
y = floor(absx);
if (absx - y >= 0.5)
y += 1.0;
return copysign(y, x);
}
where copysign is:
double
copysign(double x, double y)
{
/* use atan2 to distinguish -0. from 0. */
if (y > 0. || (y == 0. && atan2(y, -1.) > 0.)) {
return fabs(x);
} else {
return -fabs(x);
}
}
Upvotes: 1