FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

bpo-37822: Add math.as_integer_ratio(). by serhiy-storchaka · Pull Request #15210 · python/cpython · GitHub

/ cpython Public
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .c  (1) .h  (1) .py  (3) .rst  (3) All 4 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
11 changes: 11 additions & 0 deletions Doc/library/math.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ noted otherwise, all return values are floats.
Number-theoretic and representation functions
---------------------------------------------

.. function:: as_integer_ratio(number)

Return integer ratio.

This function returns ``number.as_integer_ratio()`` if the argument has
the ``as_integer_ratio()`` method, otherwise it returns a pair
``(number.numerator, number.denominator)``.

.. versionadded:: 3.9


.. function:: ceil(x)

Return the ceiling of *x*, the smallest integer greater than or equal to *x*.
Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.9.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ New Modules
Improved Modules
================


math
----

Added new function :func:`math.as_integer_ratio`.
(Contributed by Serhiy Storchaka in :issue:``.)


threading
---------

Expand Down
26 changes: 7 additions & 19 deletions Lib/fractions.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,7 @@ def __new__(cls, numerator=0, denominator=None, *, _normalize=True):
self = super(Fraction, cls).__new__(cls)

if denominator is None:
if type(numerator) is int:
self._numerator = numerator
self._denominator = 1
return self

elif isinstance(numerator, numbers.Rational):
self._numerator = numerator.numerator
self._denominator = numerator.denominator
return self

elif isinstance(numerator, (float, Decimal)):
# Exact conversion
self._numerator, self._denominator = numerator.as_integer_ratio()
return self

elif isinstance(numerator, str):
if isinstance(numerator, str):
# Handle construction from strings.
m = _RATIONAL_FORMAT.match(numerator)
if m is None:
Expand All @@ -156,10 +141,13 @@ def __new__(cls, numerator=0, denominator=None, *, _normalize=True):
denominator *= 10**-exp
if m.group('sign') == '-':
numerator = -numerator

else:
raise TypeError("argument should be a string "
"or a Rational instance")
try:
self._numerator, self._denominator = math.as_integer_ratio(numerator)
return self
except TypeError:
raise TypeError("argument should be a string or a number, "
"not %s" % type(numerator).__name__) from None

elif type(numerator) is int is type(denominator):
pass # *very* normal case
Expand Down
18 changes: 1 addition & 17 deletions Lib/statistics.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -225,27 +225,11 @@ def _exact_ratio(x):
x is expected to be an int, Fraction, Decimal or float.
"""
try:
# Optimise the common case of floats. We expect that the most often
# used numeric type will be builtin floats, so try to make this as
# fast as possible.
if type(x) is float or type(x) is Decimal:
return x.as_integer_ratio()
try:
# x may be an int, Fraction, or Integral ABC.
return (x.numerator, x.denominator)
except AttributeError:
try:
# x may be a float or Decimal subclass.
return x.as_integer_ratio()
except AttributeError:
# Just give up?
pass
return math.as_integer_ratio(x)
except (OverflowError, ValueError):
# float NAN or INF.
assert not _isfinite(x)
return (x, None)
msg = "can't convert type '{}' to numerator/denominator"
raise TypeError(msg.format(type(x).__name__))


def _convert(value, T):
Expand Down
29 changes: 29 additions & 0 deletions Lib/test/test_math.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import random
import struct
import sys
from decimal import Decimal
from fractions import Fraction


eps = 1E-05
Expand Down Expand Up @@ -293,6 +295,33 @@ def testAcosh(self):
self.assertRaises(ValueError, math.acosh, NINF)
self.assertTrue(math.isnan(math.acosh(NAN)))

def testAsIntegerRatio(self):
as_integer_ratio = math.as_integer_ratio
self.assertEqual(as_integer_ratio(0), (0, 1))
self.assertEqual(as_integer_ratio(3), (3, 1))
self.assertEqual(as_integer_ratio(-3), (-3, 1))
self.assertEqual(as_integer_ratio(False), (0, 1))
self.assertEqual(as_integer_ratio(True), (1, 1))
self.assertEqual(as_integer_ratio(0.0), (0, 1))
self.assertEqual(as_integer_ratio(-0.0), (0, 1))
self.assertEqual(as_integer_ratio(0.875), (7, 8))
self.assertEqual(as_integer_ratio(-0.875), (-7, 8))
self.assertEqual(as_integer_ratio(Decimal('0')), (0, 1))
self.assertEqual(as_integer_ratio(Decimal('0.875')), (7, 8))
self.assertEqual(as_integer_ratio(Decimal('-0.875')), (-7, 8))
self.assertEqual(as_integer_ratio(Fraction(0)), (0, 1))
self.assertEqual(as_integer_ratio(Fraction(7, 8)), (7, 8))
self.assertEqual(as_integer_ratio(Fraction(-7, 8)), (-7, 8))

self.assertRaises(OverflowError, as_integer_ratio, float('inf'))
self.assertRaises(OverflowError, as_integer_ratio, float('-inf'))
self.assertRaises(ValueError, as_integer_ratio, float('nan'))
self.assertRaises(OverflowError, as_integer_ratio, Decimal('inf'))
self.assertRaises(OverflowError, as_integer_ratio, Decimal('-inf'))
self.assertRaises(ValueError, as_integer_ratio, Decimal('nan'))

self.assertRaises(TypeError, as_integer_ratio, '0')

def testAsin(self):
self.assertRaises(TypeError, math.asin)
self.ftest('asin(-1)', math.asin(-1), -math.pi/2)
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added :func:`math.as_integer_ratio`.
11 changes: 10 additions & 1 deletion Modules/clinic/mathmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions Modules/mathmodule.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -3306,9 +3306,83 @@ math_comb_impl(PyObject *module, PyObject *n, PyObject *k)
}


/*[clinic input]
math.as_integer_ratio
x: object
/
greatest common divisor of x and y

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This looks like a copy-paste error. Maybe:

Suggested change
greatest common divisor of x and y
Return the rational representation of x as the pair (numerator, denominator).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Thank you. But this proposition was rejected.

[clinic start generated code]*/

static PyObject *
math_as_integer_ratio(PyObject *module, PyObject *x)
/*[clinic end generated code: output=1844868fd4efb2f1 input=d7f2e8ffd51c6599]*/
{
_Py_IDENTIFIER(as_integer_ratio);
_Py_IDENTIFIER(numerator);
_Py_IDENTIFIER(denominator);
PyObject *ratio, *as_integer_ratio, *numerator, *denominator;

if (PyLong_CheckExact(x)) {
return PyTuple_Pack(2, x, _PyLong_One);
}

if (_PyObject_LookupAttrId(x, &PyId_as_integer_ratio, &as_integer_ratio) < 0) {
return NULL;
}
if (as_integer_ratio) {
ratio = _PyObject_CallNoArg(as_integer_ratio);
Py_DECREF(as_integer_ratio);
if (ratio == NULL) {
return NULL;
}
if (!PyTuple_Check(ratio)) {
PyErr_Format(PyExc_TypeError,
"unexpected return type from as_integer_ratio(): "
"expected tuple, got '%.200s'",
Py_TYPE(ratio)->tp_name);
Py_DECREF(ratio);
return NULL;
}
if (PyTuple_GET_SIZE(ratio) != 2) {
PyErr_SetString(PyExc_ValueError,
"as_integer_ratio() must return a 2-tuple");
Py_DECREF(ratio);
return NULL;
}
}
else {
if (_PyObject_LookupAttrId(x, &PyId_numerator, &numerator) < 0) {
return NULL;
}
if (numerator == NULL) {
PyErr_Format(PyExc_TypeError,
"required a number, not '%.200s'",
Py_TYPE(x)->tp_name);
return NULL;
}
if (_PyObject_LookupAttrId(x, &PyId_denominator, &denominator) < 0) {
Py_DECREF(numerator);
return NULL;
}
if (denominator == NULL) {
Py_DECREF(numerator);
PyErr_Format(PyExc_TypeError,
"required a number, not '%.200s'",
Py_TYPE(x)->tp_name);
return NULL;
}
ratio = PyTuple_Pack(2, numerator, denominator);
Py_DECREF(numerator);
Py_DECREF(denominator);
}
return ratio;
}


static PyMethodDef math_methods[] = {
{"acos", math_acos, METH_O, math_acos_doc},
{"acosh", math_acosh, METH_O, math_acosh_doc},
MATH_AS_INTEGER_RATIO_METHODDEF
{"asin", math_asin, METH_O, math_asin_doc},
{"asinh", math_asinh, METH_O, math_asinh_doc},
{"atan", math_atan, METH_O, math_atan_doc},
Expand Down

Back | FazBrowse Home | New Git URL