Skip to content

log(Decimal(x)) is broken for large x #106502

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

Closed
ra1u opened this issue Jul 7, 2023 · 8 comments
Closed

log(Decimal(x)) is broken for large x #106502

ra1u opened this issue Jul 7, 2023 · 8 comments
Labels
stdlib Python modules in the Lib dir

Comments

@ra1u
Copy link

ra1u commented Jul 7, 2023

issue is that for large x math.log(Decimal(x)) returns inf

I am not sure if that is an issue with documentation or implementation

Python 3.11.4 (main, Jun  7 2023, 10:13:09) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from math import log
>>> from decimal import Decimal
>>> d = Decimal(300**300)
>>> d
Decimal('136891479058588375991326027382088315966463695625337436471480190078368997177499076593800206155688941388250484440597994042813512732765695774566001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
>>> log(d)
inf
>>> log(int(d))
1711.1347423968602
@ra1u ra1u added the type-bug An unexpected behavior, bug, or error label Jul 7, 2023
@tim-one
Copy link
Member

tim-one commented Jul 7, 2023

The math module is really for floats. decimal has its own algorithms:

>>> import decimal
>>> x = decimal.Decimal(300**300)
>>> x.ln()
Decimal('1711.134742396860317829368444')

@hugovk hugovk added the stdlib Python modules in the Lib dir label Jul 7, 2023
@sunmy2019
Copy link
Member

Actually

>>> float(decimal.Decimal(300**300))
inf

@sunmy2019
Copy link
Member

You should not rely on math.log which is designed for float.

@ra1u
Copy link
Author

ra1u commented Jul 7, 2023

I understand all of that. What I am asking is following: Does documentation/specification states this restrictions?

@ra1u
Copy link
Author

ra1u commented Jul 7, 2023

@sunmy2019

There is another issue with float() implementation .

From: https://docs.python.org/3/library/functions.html?#float

If the argument is outside the range of a Python float, an OverflowError will be raised.

According to this OverflowError should be raised from float(decimal.Decimal(300**300))

@sunmy2019
Copy link
Member

sunmy2019 commented Jul 7, 2023

If the argument is outside the range of a Python float, an OverflowError will be raised.

That's only true for int and float.

Otherwise, if the argument is an integer or a floating point number, a floating point number with the same value (within Python’s floating point precision) is returned. If the argument is outside the range of a Python float, an OverflowError will be raised.

You should look at the following paragraph for general Python objects.

For a general Python object x, float(x) delegates to x.__float__(). If __float__() is not defined then it falls back to __index__().

@rhettinger rhettinger removed the type-bug An unexpected behavior, bug, or error label Jul 8, 2023
@skirpichev
Copy link
Contributor

On one hand, it might be considered as a tiny documentation issue for the float() type.

Here is a patch.
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 3520609706..2672d243c7 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -677,7 +677,7 @@ are always available.  They are listed here in alphabetical order.
 
    Otherwise, if the argument is an integer or a floating point number, a
    floating point number with the same value (within Python's floating point
-   precision) is returned.  If the argument is outside the range of a Python
+   precision) is returned.  If an integer is outside the range of a Python
    float, an :exc:`OverflowError` will be raised.
 
    For a general Python object ``x``, ``float(x)`` delegates to

On another hand, there could be some confusion in this case:

>>> log(int(d))
1711.1347423968602
>>> float(int(d))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: int too large to convert to float

While for other functions of the module, like sin(), we could expect simple type-cast for integer arguments, i.e. sin(<big-int>)=sin(float(<big-int>)) - the log() function is a special one. It uses internally the _PyLong_Frexp() private helper to compute the log even in some cases when an integer argument is outside the range of a Python float.

@tim-one, shouldn't we document this somehow? Maybe we could expose the _PyLong_Frexp() in the math.frexp() do deal with big integers in this function like in the log()? (This will break the x == m * 2**e invariant, however.)

@mdickinson
Copy link
Member

I think this should be closed. We can open a separate issue for improving the documentation if that's needed.

@ra1u ra1u closed this as completed Jun 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir
Projects
None yet
Development

No branches or pull requests

7 participants