python / cpython

The Python programming language
https://www.python.org
Other
63.18k stars 30.25k forks source link

Multiplying 4.6*100 will result in 459.99999999999994 #84387

Closed 2920e11f-c1e6-4a26-bbc9-4b9c60b653b2 closed 4 years ago

2920e11f-c1e6-4a26-bbc9-4b9c60b653b2 commented 4 years ago
BPO 40206
Nosy @ericvsmith, @stevendaprano, @skrah, @remilapeyre
Files
  • Screen Shot 2020-04-06 at 6.56.48 PM.png
  • 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: ```python assignee = None closed_at = created_at = labels = ['3.7', 'invalid'] title = 'Multiplying 4.6*100 will result in 459.99999999999994' updated_at = user = 'https://bugs.python.org/ahmaddana' ``` bugs.python.org fields: ```python activity = actor = 'skrah' assignee = 'none' closed = True closed_date = closer = 'eric.smith' components = [] creation = creator = 'ahmad dana' dependencies = [] files = ['49040'] hgrepos = [] issue_num = 40206 keywords = [] message_count = 7.0 messages = ['365860', '365861', '365864', '365865', '365867', '365869', '365903'] nosy_count = 5.0 nosy_names = ['eric.smith', 'steven.daprano', 'skrah', 'remi.lapeyre', 'ahmad dana'] pr_nums = [] priority = 'normal' resolution = 'not a bug' stage = 'resolved' status = 'closed' superseder = None type = None url = 'https://bugs.python.org/issue40206' versions = ['Python 3.7'] ```

    2920e11f-c1e6-4a26-bbc9-4b9c60b653b2 commented 4 years ago

    While we using python3.7 to do some number multiplication, we faced an issue with multiplying 4.6*100 which lead to strange output, the result was 459.99999999999994, while it should be something like 460.00

    23982c60-ed6c-47d1-96c2-69d417bd81b3 commented 4 years ago

    Hi ahmad, calculation with floating points in Python uses the IEE 754 (https://fr.wikipedia.org/wiki/IEEE_754) standard and will result in such quirks.

    If you want to not loose precision you can use the decimal module:

    >>> from decimal import Decimal
    >>> Decimal('4.6')*100
    Decimal('460.0')

    Since this is not a bug if you have other questions when working with floats, try to ask on python-list or a forum.

    ericvsmith commented 4 years ago

    See https://docs.python.org/3.8/tutorial/floatingpoint.html

    stevendaprano commented 4 years ago

    Rémi, it is not true that the Decimal module won't lose precision. It will. Decimal is not exact either, it is still a floating point format similar to float.

    py> Decimal(1)/3*3 Decimal('0.9999999999999999999999999999')

    The two major advantages of Decimal are: it matches the number you type more closely, and you can choose how much precision to use. (At the cost of memory and speed.) But there are also disadvantages: rounding errors with Decimal tend to be larger on average than for binary floats.

    If you want exact calculations that will never lose precision, you have to use Fractions, but that is slower and less convenient.

    2920e11f-c1e6-4a26-bbc9-4b9c60b653b2 commented 4 years ago

    Regarding the comment about the decimal point precision , and solving the issue with the decimal library, the following attachment shows you that the decimal Library did exactly the same behaviour

    23982c60-ed6c-47d1-96c2-69d417bd81b3 commented 4 years ago

    @Steven Yes that's true, I only meant that in the context of the issue where only the multiplication is used. FWIW Fraction also would have issues with e.g. trigonometric functions right?

    @ahmad, that's because you did Decimal(4.6) which first parse 4.6 as a float then call Decimal() with the result. You need to use Decimal('4.6') to avoid the parser reading 4.6 as a float. Have a look at the tutorial Eric Smith linked, the documentation of decimal and the response from Steven D'Aprano for more information.

    5531d0d8-2a9c-46ba-8b8b-ef76132a492c commented 4 years ago

    You can also set the decimal.FloatOperation trap to avoid accidental errors:

    >>> from decimal import *
    >>> c = getcontext()
    >>> Decimal(4.6) * 100
    Decimal('459.9999999999999644728632120')
    
    >>> c.traps[FloatOperation] = True
    >>> Decimal(4.6) * 100
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
    
    >>> Decimal("4.6") * 100
    Decimal('460.0')
    >>>