diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 98c5b334fc3..8254d3feb3b 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -184,6 +184,13 @@ Power and logarithmic functions result is calculated in a way which is accurate for *x* near zero. +.. function:: log2(x) + + Return the base-2 logarithm of *x*. + + .. versionadded:: 3.3 + + .. function:: log10(x) Return the base-10 logarithm of *x*. This is usually more accurate diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 37f96ce948d..e657af24720 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -76,6 +76,33 @@ New module: :mod:`faulthandler`. * :envvar:`PYTHONFAULTHANDLER` * :option:`-X` ``faulthandler`` + +math +---- + +The :mod:`math` module has a new function: + + * :func:`~math.log2`: return the base-2 logarithm of *x* + (Written by Mark Dickinson in :issue:`11888`). + + +nntplib +------- + +The :class:`nntplib.NNTP` class now supports the context manager protocol to +unconditionally consume :exc:`socket.error` exceptions and to close the NNTP +connection when done:: + + >>> from nntplib import NNTP + >>> with nntplib.NNTP('news.gmane.org') as n: + ... n.group('gmane.comp.python.committers') + ... + ('211 1454 1 1454 gmane.comp.python.committers', '1454', '1', '1454', 'gmane.comp.python.committers') + >>> + +(Contributed by Giampaolo Rodolà in :issue:`9795`) + + os -- @@ -96,21 +123,6 @@ os (Patch submitted by Giampaolo Rodolà in :issue:`10784`.) -nntplib -------- - -The :class:`nntplib.NNTP` class now supports the context manager protocol to -unconditionally consume :exc:`socket.error` exceptions and to close the NNTP -connection when done:: - - >>> from nntplib import NNTP - >>> with nntplib.NNTP('news.gmane.org') as n: - ... n.group('gmane.comp.python.committers') - ... - ('211 1454 1 1454 gmane.comp.python.committers', '1454', '1', '1454', 'gmane.comp.python.committers') - >>> - -(Contributed by Giampaolo Rodolà in :issue:`9795`) sys --- @@ -120,10 +132,11 @@ sys (:issue:`11223`) + signal ------ -* The :mod:`signal` module has a new functions: +* The :mod:`signal` module has new functions: * :func:`~signal.pthread_sigmask`: fetch and/or change the signal mask of the calling thread (Contributed by Jean-Paul Calderone in :issue:`8407`) ; diff --git a/Lib/test/math_testcases.txt b/Lib/test/math_testcases.txt index 5e24335a879..7d1c0852712 100644 --- a/Lib/test/math_testcases.txt +++ b/Lib/test/math_testcases.txt @@ -517,3 +517,117 @@ expm10306 expm1 1.79e308 -> inf overflow -- weaker version of expm10302 expm10307 expm1 709.5 -> 1.3549863193146328e+308 + +------------------------- +-- log2: log to base 2 -- +------------------------- + +-- special values +log20000 log2 0.0 -> -inf divide-by-zero +log20001 log2 -0.0 -> -inf divide-by-zero +log20002 log2 inf -> inf +log20003 log2 -inf -> nan invalid +log20004 log2 nan -> nan + +-- exact value at 1.0 +log20010 log2 1.0 -> 0.0 + +-- negatives +log20020 log2 -5e-324 -> nan invalid +log20021 log2 -1.0 -> nan invalid +log20022 log2 -1.7e-308 -> nan invalid + +-- exact values at powers of 2 +log20100 log2 2.0 -> 1.0 +log20101 log2 4.0 -> 2.0 +log20102 log2 8.0 -> 3.0 +log20103 log2 16.0 -> 4.0 +log20104 log2 32.0 -> 5.0 +log20105 log2 64.0 -> 6.0 +log20106 log2 128.0 -> 7.0 +log20107 log2 256.0 -> 8.0 +log20108 log2 512.0 -> 9.0 +log20109 log2 1024.0 -> 10.0 +log20110 log2 2048.0 -> 11.0 + +log20200 log2 0.5 -> -1.0 +log20200 log2 0.25 -> -2.0 +log20200 log2 0.125 -> -3.0 +log20200 log2 0.0625 -> -4.0 + +-- values close to 1.0 +log20300 log2 1.0000000000000002 -> 3.2034265038149171e-16 +log20301 log2 1.0000000001 -> 1.4426951601859516e-10 +log20302 log2 1.00001 -> 1.4426878274712997e-5 + +log20310 log2 0.9999999999999999 -> -1.6017132519074588e-16 +log20311 log2 0.9999999999 -> -1.4426951603302210e-10 +log20312 log2 0.99999 -> -1.4427022544056922e-5 + +-- tiny values +log20400 log2 5e-324 -> -1074.0 +log20401 log2 1e-323 -> -1073.0 +log20402 log2 1.5e-323 -> -1072.4150374992789 +log20403 log2 2e-323 -> -1072.0 + +log20410 log2 1e-308 -> -1023.1538532253076 +log20411 log2 2.2250738585072014e-308 -> -1022.0 +log20412 log2 4.4501477170144028e-308 -> -1021.0 +log20412 log2 1e-307 -> -1019.8319251304202 + +-- huge values +log20500 log2 1.7976931348623157e+308 -> 1024.0 +log20501 log2 1.7e+308 -> 1023.9193879716706 +log20502 log2 8.9884656743115795e+307 -> 1023.0 + +-- selection of random values +log20600 log2 -7.2174324841039838e+289 -> nan invalid +log20601 log2 -2.861319734089617e+265 -> nan invalid +log20602 log2 -4.3507646894008962e+257 -> nan invalid +log20603 log2 -6.6717265307520224e+234 -> nan invalid +log20604 log2 -3.9118023786619294e+229 -> nan invalid +log20605 log2 -1.5478221302505161e+206 -> nan invalid +log20606 log2 -1.4380485131364602e+200 -> nan invalid +log20607 log2 -3.7235198730382645e+185 -> nan invalid +log20608 log2 -1.0472242235095724e+184 -> nan invalid +log20609 log2 -5.0141781956163884e+160 -> nan invalid +log20610 log2 -2.1157958031160324e+124 -> nan invalid +log20611 log2 -7.9677558612567718e+90 -> nan invalid +log20612 log2 -5.5553906194063732e+45 -> nan invalid +log20613 log2 -16573900952607.953 -> nan invalid +log20614 log2 -37198371019.888618 -> nan invalid +log20615 log2 -6.0727115121422674e-32 -> nan invalid +log20616 log2 -2.5406841656526057e-38 -> nan invalid +log20617 log2 -4.9056766703267657e-43 -> nan invalid +log20618 log2 -2.1646786075228305e-71 -> nan invalid +log20619 log2 -2.470826790488573e-78 -> nan invalid +log20620 log2 -3.8661709303489064e-165 -> nan invalid +log20621 log2 -1.0516496976649986e-182 -> nan invalid +log20622 log2 -1.5935458614317996e-255 -> nan invalid +log20623 log2 -2.8750977267336654e-293 -> nan invalid +log20624 log2 -7.6079466794732585e-296 -> nan invalid +log20625 log2 3.2073253539988545e-307 -> -1018.1505544209213 +log20626 log2 1.674937885472249e-244 -> -809.80634755783126 +log20627 log2 1.0911259044931283e-214 -> -710.76679472274213 +log20628 log2 2.0275372624809709e-154 -> -510.55719818383272 +log20629 log2 7.3926087369631841e-115 -> -379.13564735312292 +log20630 log2 1.3480198206342423e-86 -> -285.25497445094436 +log20631 log2 8.9927384655719947e-83 -> -272.55127136401637 +log20632 log2 3.1452398713597487e-60 -> -197.66251564496875 +log20633 log2 7.0706573215457351e-55 -> -179.88420087782217 +log20634 log2 3.1258285390731669e-49 -> -161.13023800505653 +log20635 log2 8.2253046627829942e-41 -> -133.15898277355879 +log20636 log2 7.8691367397519897e+49 -> 165.75068202732419 +log20637 log2 2.9920561983925013e+64 -> 214.18453534573757 +log20638 log2 4.7827254553946841e+77 -> 258.04629628445673 +log20639 log2 3.1903566496481868e+105 -> 350.47616767491166 +log20640 log2 5.6195082449502419e+113 -> 377.86831861008250 +log20641 log2 9.9625658250651047e+125 -> 418.55752921228753 +log20642 log2 2.7358945220961532e+145 -> 483.13158636923413 +log20643 log2 2.785842387926931e+174 -> 579.49360214860280 +log20644 log2 2.4169172507252751e+193 -> 642.40529039289652 +log20645 log2 3.1689091206395632e+205 -> 682.65924573798395 +log20646 log2 2.535995592365391e+208 -> 692.30359597460460 +log20647 log2 6.2011236566089916e+233 -> 776.64177576730913 +log20648 log2 2.1843274820677632e+253 -> 841.57499717289647 +log20649 log2 8.7493931063474791e+297 -> 989.74182713073981 diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 1bbc22d0960..3b80bb6c6f7 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -649,6 +649,28 @@ class MathTests(unittest.TestCase): n= 2**90 self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) + @requires_IEEE_754 + def testLog2(self): + self.assertRaises(TypeError, math.log2) + # Check that we get exact equality for log2 of powers of 2. + actual = [math.log2(2.0**n) for n in range(-324, 1024)] + expected = [float(n) for n in range(-324, 1024)] + self.assertEqual(actual, expected) + + # Check some integer values + self.assertEqual(math.log2(1), 0.0) + self.assertEqual(math.log2(2), 1.0) + self.assertEqual(math.log2(4), 2.0) + + # Large integer values + self.assertEqual(math.log2(2**1023), 1023.0) + self.assertEqual(math.log2(2**1024), 1024.0) + self.assertEqual(math.log2(2**2000), 2000.0) + + self.assertRaises(ValueError, math.log2, -1.5) + self.assertRaises(ValueError, math.log2, NINF) + self.assertTrue(math.isnan(math.log2(NAN))) + def testLog10(self): self.assertRaises(TypeError, math.log10) self.ftest('log10(0.1)', math.log10(0.1), -1) diff --git a/Misc/NEWS b/Misc/NEWS index 3b9d27ac34c..a9f6e36604e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -140,6 +140,9 @@ Core and Builtins Library ------- +- Issue #11888: Add log2 function to math module. Patch written by Mark + Dickinson. + - Issue #12012: ssl.PROTOCOL_SSLv2 becomes optional. - Issue #8407: The signal handler writes the signal number as a single byte diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index d173bff5c61..8815dd53b9b 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -577,6 +577,55 @@ m_log(double x) } } +/* + log2: log to base 2. + + Uses an algorithm that should: + (a) produce exact results for powers of 2, and + (b) be monotonic, assuming that the system log is monotonic. +*/ + +static double +m_log2(double x) +{ + if (!Py_IS_FINITE(x)) { + if (Py_IS_NAN(x)) + return x; /* log2(nan) = nan */ + else if (x > 0.0) + return x; /* log2(+inf) = +inf */ + else { + errno = EDOM; + return Py_NAN; /* log2(-inf) = nan, invalid-operation */ + } + } + + if (x > 0.0) { + double m; + int e; + m = frexp(x, &e); + /* We want log2(m * 2**e) == log(m) / log(2) + e. Care is needed when + * x is just greater than 1.0: in that case e is 1, log(m) is negative, + * and we get significant cancellation error from the addition of + * log(m) / log(2) to e. The slight rewrite of the expression below + * avoids this problem. + */ + if (x >= 1.0) { + return log(2.0 * m) / log(2.0) + (e - 1); + } + else { + return log(m) / log(2.0) + e; + } + } + else if (x == 0.0) { + errno = EDOM; + return -Py_HUGE_VAL; /* log2(0) = -inf, divide-by-zero */ + } + else { + errno = EDOM; + return Py_NAN; /* log10(-inf) = nan, invalid-operation */ + } +} + static double m_log10(double x) { @@ -1622,6 +1671,15 @@ PyDoc_STRVAR(math_log_doc, Return the logarithm of x to the given base.\n\ If the base not specified, returns the natural logarithm (base e) of x."); +static PyObject * +math_log2(PyObject *self, PyObject *arg) +{ + return loghelper(arg, m_log2, "log2"); +} + +PyDoc_STRVAR(math_log2_doc, +"log2(x)\n\nReturn the base 2 logarithm of x."); + static PyObject * math_log10(PyObject *self, PyObject *arg) { @@ -1894,6 +1952,7 @@ static PyMethodDef math_methods[] = { {"log", math_log, METH_VARARGS, math_log_doc}, {"log1p", math_log1p, METH_O, math_log1p_doc}, {"log10", math_log10, METH_O, math_log10_doc}, + {"log2", math_log2, METH_O, math_log2_doc}, {"modf", math_modf, METH_O, math_modf_doc}, {"pow", math_pow, METH_VARARGS, math_pow_doc}, {"radians", math_radians, METH_O, math_radians_doc},