Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Round default argument for "ndigits" #64132

Closed
jbvsmo mannequin opened this issue Dec 9, 2013 · 17 comments
Closed

Round default argument for "ndigits" #64132

jbvsmo mannequin opened this issue Dec 9, 2013 · 17 comments
Labels
docs Documentation in the Doc dir interpreter-core (Objects, Python, Grammar, and Parser dirs)

Comments

@jbvsmo
Copy link
Mannequin

jbvsmo mannequin commented Dec 9, 2013

BPO 19933
Nosy @terryjreedy, @mdickinson, @bitdancer, @jbvsmo, @vajrasky
Files
  • fix_round_with_zero_ndigits.patch
  • fix_doc_round_ndigits.patch
  • fix_doc_round_ndigits_v2.patch
  • fix_doc_ndigits_round_and_add_None_ndigits.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2015-04-15.20:16:44.830>
    created_at = <Date 2013-12-09.04:24:20.461>
    labels = ['interpreter-core', 'docs']
    title = 'Round default argument for "ndigits"'
    updated_at = <Date 2015-04-15.20:16:44.828>
    user = 'https://github.com/jbvsmo'

    bugs.python.org fields:

    activity = <Date 2015-04-15.20:16:44.828>
    actor = 'steve.dower'
    assignee = 'docs@python'
    closed = True
    closed_date = <Date 2015-04-15.20:16:44.830>
    closer = 'steve.dower'
    components = ['Documentation', 'Interpreter Core']
    creation = <Date 2013-12-09.04:24:20.461>
    creator = 'JBernardo'
    dependencies = []
    files = ['33051', '33054', '33060', '33074']
    hgrepos = []
    issue_num = 19933
    keywords = ['patch']
    message_count = 17.0
    messages = ['205647', '205650', '205651', '205652', '205653', '205682', '205683', '205695', '205699', '205703', '205704', '205706', '205708', '205729', '205770', '206162', '241152']
    nosy_count = 7.0
    nosy_names = ['terry.reedy', 'mark.dickinson', 'r.david.murray', 'docs@python', 'python-dev', 'JBernardo', 'vajrasky']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = None
    status = 'closed'
    superseder = None
    type = None
    url = 'https://bugs.python.org/issue19933'
    versions = ['Python 3.5']

    @jbvsmo
    Copy link
    Mannequin Author

    jbvsmo mannequin commented Dec 9, 2013

    From the docs for built-in function "round":
    "If ndigits is omitted, it defaults to zero"
    (http://docs.python.org/3/library/functions.html#round)

    But, the only way to get an integer from round is by not having the second argument (ndigits):

        >>> round(3.5)
        4
        >>> round(3.5, 1)
        3.5
        >>> round(3.5, 0)
        4.0
        >>> round(3.5, -1)
        0.0
        >>> round(3.5, None)
        Traceback (most recent call last):
          File "<pyshell#6>", line 1, in <module>
            round(3.5, None)
        TypeError: 'NoneType' object cannot be interpreted as an integer

    Either the docs are wrong or the behavior is wrong. I think it's easier to fix the former...

    But also there should be a way to make round return an integer (e.g. passing None as 2nd argument)

    @jbvsmo jbvsmo mannequin assigned docspython Dec 9, 2013
    @jbvsmo jbvsmo mannequin added docs Documentation in the Doc dir interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Dec 9, 2013
    @vajrasky
    Copy link
    Mannequin

    vajrasky mannequin commented Dec 9, 2013

    Here is the preliminary patch.

    After patch:

    round(1.23, 0) => 1 not 1.0

    round(4.67, 0) => 5 not 5.0

    @mdickinson
    Copy link
    Member

    After patch:
    round(1.23, 0) => 1 not 1.0
    round(4.67, 0) => 5 not 5.0

    Please no! Two-argument round should continue to return a float in all cases.

    The docs should be fixed.

    @mdickinson
    Copy link
    Member

    But also there should be a way to make round return an integer

    I don't understand. There's already a way to make round return an integer: don't pass a second argument.

    @vajrasky
    Copy link
    Mannequin

    vajrasky mannequin commented Dec 9, 2013

    Okay, here is the patch to fix the doc.

    @mdickinson
    Copy link
    Member

    Thanks. It's inaccurate to say that a float is returned in general, though: for most builtin numeric types, what's returned has the same type as its input. So rounding a Decimal to two places gives a Decimal on output, etc. (That's already explained in the next paragraph.)

    @mdickinson
    Copy link
    Member

    How about just removing the mention of 'defaults to zero', and say something like: "if ndigits is omitted, returns the nearest int to its input"

    @jbvsmo
    Copy link
    Mannequin Author

    jbvsmo mannequin commented Dec 9, 2013

    I don't understand. There's already a way to make round return an integer: don't pass a second argument.

    If this function were to be written in Python, it would be something like:

        def round(number, ndigits=0):
            ...

    or

        def round(number, ndigits=None):
            ...

    But in C you can forge the signature to whatever you want and parse the arguments accordingly. In Python there's always a way to get the default behavior by passing the default argument, but in C it may not exist (in this case PyObject *o_ndigits = NULL;)

    So, I propose the default value being None, so this behavior can be achieved using a second argument.

    @vajrasky
    Copy link
    Mannequin

    vajrasky mannequin commented Dec 9, 2013

    Here is the updated doc fix.

    Anyway, why not round(1.2) -> 1.0 in the first place? Just curious.

    @jbvsmo
    Copy link
    Mannequin Author

    jbvsmo mannequin commented Dec 9, 2013

    Anyway, why not round(1.2) -> 1.0 in the first place? Just curious.

    It was the behavior on Python 2.x, but somehow when they changed the rounding method to nearest even number this happened... I think it's too late to change back the return type.

    @bitdancer
    Copy link
    Member

    Do you have any real-world motivating use case for None? Not just theoretical consistency with what a Python version of the function would look like. (I'm not saying we shouldn't consider supporting None as a low priority change, I'm just trying to figure out where you'd ever need it in the real world.)

    @jbvsmo
    Copy link
    Mannequin Author

    jbvsmo mannequin commented Dec 9, 2013

    Not really. Just consistency:

    For the same reason

    ' foo '.strip(None)
    

    works... To avoid special casing the function call when you already have a variable to hold the argument.

    @bitdancer
    Copy link
    Member

    Right, but None in that case has real world utility, since you might have the the value in a variable. But you are hardly going to hold int-or-not in a variable, especially a variable that is really about the number of places in the float result...

    @mdickinson
    Copy link
    Member

    Anyway, why not round(1.2) -> 1.0 in the first place? Just curious.

    All this changed as part of PEP-3141. I wasn't watching Python 3 development closely back then, but I think at least part of the motivation was to provide a way to get away from the use of int to truncate a float to its integer part: the argument goes that a simple type conversion shouldn't throw away information, and that if you want a transformation from float to int that throws away information you should ask for it explicitly. So math.trunc was born as the preferred way to truncate a float to an int, and math.floor, math.ceil and round became alternative float -> int conversion methods. That entailed those functions returning ints.

    In the case of math.floor and math.ceil at least, I think this is regrettable. There are plenty of places where you just want a float -> float floor or ceiling, and Python no longer has a cheap operation for that available: floor as a float-to-float operation is cheap; floor as a float-to-long-integer operation is significantly more costly.

    In the case of round, we still have round(x, 0) available as a cheap float->float conversion, so it's less of a problem. And I hardly ever use trunc, so I don't care about that case.

    @vajrasky
    Copy link
    Mannequin

    vajrasky mannequin commented Dec 10, 2013

    In case we want to add consistency with None ndigits, here is the patch adding support for None value for ndigits parameter.

    This one looks like a low-risk addition but since Python 3.4 is in beta phase....

    @terryjreedy
    Copy link
    Member

    The docstring is better than the current doc as it says that the default precision is 0, without calling that the default for ndigits.

    ''' round(number[, ndigits]) -> number

    Round a number to a given precision in decimal digits (default 0 digits).
    This returns an int when called with one argument, otherwise the
    same type as the number. ndigits may be negative.'''
    

    ---
    Sidenote: To write round in Python, one could easily write

    _sentinel = object
    def round(number, ndigits=_sentinel):
      if ndigits is _sentinel: ...

    which makes ndigits positional-or-keyword, and almost optional-with-no-default, as _sentinel is close enough to being a default that cannot be passed in. This is a standard idiom. One who was really picky about having no default could use
    def round(number, *args, **kwds): ...
    and look for len(args) == 1 xor kwds.keys() == {'ndigits'}.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Apr 15, 2015

    New changeset e3cc75b1000b by Steve Dower in branch 'default':
    bpo-19933: Provide default argument for ndigits in round. Patch by Vajrasky Kok.
    https://hg.python.org/cpython/rev/e3cc75b1000b

    @zooba zooba closed this as completed Apr 15, 2015
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    docs Documentation in the Doc dir interpreter-core (Objects, Python, Grammar, and Parser dirs)
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants