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

Merge C version of decimal into py3k. #51901

Closed
mdickinson opened this issue Jan 7, 2010 · 83 comments
Closed

Merge C version of decimal into py3k. #51901

mdickinson opened this issue Jan 7, 2010 · 83 comments
Assignees
Labels
extension-modules C modules in the Modules dir performance Performance or resource usage stdlib Python modules in the Lib dir

Comments

@mdickinson
Copy link
Member

BPO 7652
Nosy @malemburg, @rhettinger, @amauryfa, @mdickinson, @pitrou, @vstinner, @ericvsmith, @benjaminp, @cedk, @asvetlov, @skrah, @ericsnowcurrently, @JimJJewett
Files
  • 49433f35a5f8.diff
  • bba956250186.diff
  • be8a59fcba49.diff: fixed all comments except the ones in ISSUES.txt
  • ppro-mulmod.txt: Proof for x87 FPU modular multiplication
  • 40917e4b51aa.diff
  • 9b3b1f5c4072.diff
  • api-demo.c
  • 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 = 'https://github.com/skrah'
    closed_at = <Date 2012-09-02.12:18:25.358>
    created_at = <Date 2010-01-07.10:42:50.694>
    labels = ['extension-modules', 'library', 'performance']
    title = 'Merge C version of decimal into py3k.'
    updated_at = <Date 2012-09-02.12:18:25.358>
    user = 'https://github.com/mdickinson'

    bugs.python.org fields:

    activity = <Date 2012-09-02.12:18:25.358>
    actor = 'skrah'
    assignee = 'skrah'
    closed = True
    closed_date = <Date 2012-09-02.12:18:25.358>
    closer = 'skrah'
    components = ['Extension Modules', 'Library (Lib)']
    creation = <Date 2010-01-07.10:42:50.694>
    creator = 'mark.dickinson'
    dependencies = []
    files = ['22404', '23822', '23959', '23966', '24615', '24776', '25049']
    hgrepos = ['25']
    issue_num = 7652
    keywords = ['patch']
    message_count = 83.0
    messages = ['97347', '97348', '97355', '97372', '97373', '97377', '97382', '97411', '97412', '97719', '98161', '120305', '120674', '121452', '138029', '138583', '148650', '148652', '148669', '148677', '148678', '148680', '148682', '148687', '148688', '148689', '148690', '148691', '148721', '149556', '149557', '149559', '149567', '149600', '153447', '153506', '153507', '154074', '154988', '154992', '155031', '155034', '155036', '155045', '155046', '155047', '155050', '155059', '155070', '155071', '155079', '155082', '155083', '155085', '155086', '155087', '155088', '155093', '155095', '155107', '155135', '155331', '155359', '155381', '155419', '155644', '155649', '155743', '155744', '156402', '156480', '156500', '156671', '156890', '156952', '164470', '165329', '165330', '165339', '165341', '168034', '168035', '169692']
    nosy_count = 19.0
    nosy_names = ['lemburg', 'rhettinger', 'amaury.forgeotdarc', 'mark.dickinson', 'pitrou', 'vstinner', 'casevh', 'eric.smith', 'benjamin.peterson', 'jjconti', 'Arfrever', 'ced', 'asvetlov', 'skrah', "Amaury.Forgeot.d'Arc", 'python-dev', 'eric.snow', 'Ramchandra Apte', 'Jim.Jewett']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'performance'
    url = 'https://bugs.python.org/issue7652'
    versions = ['Python 3.3']

    @mdickinson
    Copy link
    Member Author

    I've created this issue to keep track of progress in merging Stefan Krah's work on decimal in c into py3k.

    We've created a branch py3k-cdecimal (with merge tracking from py3k) for this work. When the branch is fully working and tested we'll consult python-dev about next steps.

    @mdickinson mdickinson added extension-modules C modules in the Modules dir stdlib Python modules in the Lib dir performance Performance or resource usage labels Jan 7, 2010
    @ericvsmith
    Copy link
    Member

    Is the intention to write Decimal.__format__ in C, too? That would be quite a bit of work, and I'm not sure I could recommend it. But I'm not sure if your plan is to get rid of all Python code or not.

    If your plan is to rewrite absolutely everything in C, I could help out by exposing the methods that parse format specifiers and do some of the low level formatting. They're used internally by the int, float, and str formatting code. Let me know.

    @mdickinson
    Copy link
    Member Author

    Just to clarify, no decision has yet been made on *whether* the cdecimal work should be integrated into py3k; we'll consult python-dev on this once we've got a working branch and performance information.

    @mdickinson
    Copy link
    Member Author

    To answer Eric's question: Decimal.__format__ is already implemented in Stefan's work---it looks like most of the code is in

    http://svn.python.org/projects/python/branches/py3k-cdecimal/Modules/cdecimal/io.c

    (Stefan, is this right?)

    @mdickinson
    Copy link
    Member Author

    So the new branch looks great---thanks, Stefan! I'm only just beginning to look at the code properly, though.

    A couple of things:

    (1) Could we unify test_decimal and test_cdecimal somehow? This would avoid them getting out of sync when new tests are added, and would make it clear what the differences between them are. It looks like there's currently a lot of duplicate code.

    (2) At some point we'll need some documentation. Even if all it says is: the cdecimal module operates identically to the decimal module, with the following exceptions... (notes on threading differences, exponent limits, correct rounding of pow, etc.)

    @briancurtin
    Copy link
    Member

    mark> (1) Could we unify test_decimal and test_cdecimal somehow?
    mark> This would avoid them getting out of sync when new tests are
    mark> added, and would make it clear what the differences between
    mark> them are. It looks like there's currently a lot of duplicate code.

    An approach similar to the one taken in test_warnings.py might work: write common test code as a base class, then subclass it to be run against both the C and Py versions of the module.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jan 7, 2010

    Yes, formatting is completely implemented in io.c, together with quite a comprehensive test suite. I like the new Python format strings, so I wanted them in the C library, too.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jan 8, 2010

    Unify test_decimal and test_cdecimal:

    Yes, quite possible. The diff is currently 400 lines, but it should be easy to get that down to below 100 without any loss of functionality.

    I'll look into that when I'm done with the 64 bit ANSI path.

    Documentation:

    Anything is welcome, even a patch that just creates a stub so I don't have to figure out where to put it.

    The differences are listed at the bottom of:

    http://www.bytereef.org/cdecimal.html

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jan 8, 2010

    Just an update. Rev.77358 should compile and run stable on the buildbot platforms except Alpha and ia64. I'm working on a default ANSI path for
    64-bit.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jan 13, 2010

    As a first step in unifying test_decimal.py and test_cdecimal.py I
    would like to patch test_decimal.py in trunk and py3k. This is to
    minimize differences between py3k and py3k-cdecimal.

    (1) Remove test that Decimal(x) generates a copy.

    (2) Add test case to formatting test.

    (3) Extend threading test.

    (4) Use Emax of 425000000 instead of 999999999 where possible.
    (The 32-bit version of cdecimal has the official limit of
    425000000, even though 999999999 works in almost all cases.)

    If I get an OK for the two patches, I can commit them to py3k and
    trunk. If you don't want to apply (1), I'll make new patches.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jan 22, 2010

    All outstanding issues mentioned here have been solved in Rev. 77696:

    (1) New ANSI path for unknown 64bit platforms (ia64 and Alpha build
    without problems now).

    (2) Unified tests for decimal and cdecimal.

    (3) Documentation for cdecimal.

    Other improvements:

    (4) Added comprehensive test suite for testing the library directly.

    (5) Fixed warnings in Visual Studio.

    (6) Code formatting.

    @casevh
    Copy link
    Mannequin

    casevh mannequin commented Nov 3, 2010

    Has the cdecimal branch kept up with the hash value changes in 3.2?

    Is there a still a chance that cdecimal could be merged into 3.2?

    @mdickinson
    Copy link
    Member Author

    On Wed, Nov 3, 2010 at 3:28 AM, Case Van Horsen <report@bugs.python.org> wrote:

    Has the cdecimal branch kept up with the hash value changes in 3.2?

    Not sure; that's a question for Stefan.

    Is there a still a chance that cdecimal could be merged into 3.2?

    A chance, yes; the major need is for someone with time to do a full
    review. (I'm afraid that's not me, right now.)

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Nov 18, 2010

    An update on the progress:

    All development currently happens in my private mpdecimal repository. The
    next version of mpdecimal (2.0) is finished, stable and will be released
    once all tests have completed successfully. Running the whole test suite
    can take several weeks, since Valgrind is involved and all Python versions
    from 2.5 to 3.2 are tested.

    In py3k-cdecimal, r86497 is an exact copy of mpdecimal-2.0. All buildbots
    pass the short tests.

    Major improvements:

    o Full compatibility with decimal.py 3.2, including the new hash
    functions and float operations.

    o With the new FloatOperation signal, accidental float operations
    can be detected.

    o The underlying library - libmpdec - now has 100% code coverage
    together with Makefile targets for creating coverage reports.
    In particular, every possible allocation failure during an
    operation can be tested in brute force style.

    o The module has 85% code coverage. All lines except failures
    of Python C-API functions are tested.

    o Several minor bug fixes, most of them deal with allocation
    failures under extreme bignum conditions.

    Potential reviewers:

    I'll be happy to answer questions here or privately. IMO the best way to
    get acquainted with the module is to do the regular build and tests first,
    then explore Lib/test/mpdecimal, reading LIBTEST.txt and PYTEST.txt.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jun 9, 2011

    Just a couple of remarks about the diff I created:

    The changes to decimal.py are exploratory (i.e. done quite hastily)
    and serve the purpose to fulfill PEP-399.

    library/cdecimal.rst is completely out of date.

    The rest should be very stable.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jun 18, 2011

    The latest patch is based on a relatively stable revision of 3.3. To my
    knowledge, _decimal.c and decimal.py are now fully compatible in the
    sense of PEP-399.

    libmpdec
    ========

    o New test suite with comprehensive tests against decNumber.
    
    o Full support for 32-bit compilers (tested with CompCert).
    

    PEP-399
    =======

    o cdecimal.c is now called _decimal.c. Instead of importing cdecimal,
      _decimal is transparently imported as decimal (if the C version is
      available).
    
    o Unified unit tests with 100% code coverage for both decimal.py and
      _decimal.c. For _decimal.c, the tests include all failures of
      Python API functions (requires special patch for testing).
    
    o deccheck.py now also tests arbitrary input and makes sure that both
      modules raise the same exceptions.
    
    o Both modules produce the same pickle output for Decimal and Context.
    

    _decimal.c
    ==========

    o Speed up int/Decimal conversion for integers that fit into a
      single word of a PyLongObject (performance gain is around 15%).
    
    o real(), imag(), conjugate(), __complex__() support.
    
    o Fraction and complex comparison support.
    
    o Decimal constructor now accepts lists as well as tuples.
    
    o DecimalTuple support.
    
    o General cleanup and refactoring. The functions for conversions 
      between Decimal and other numeric types are much cleaner now 
      and could be used for a PyDec_* API.
    

    @vstinner
    Copy link
    Member

    Just to clarify, no decision has yet been made on *whether*
    the cdecimal work should be integrated into py3k;
    we'll consult python-dev on this once we've got a working branch
    and performance information.

    So, what is the status today?

    _decimal looks to be huge. Does Python really need yet another multiprecision library? There is already gmpy and bigfloat, based on the heavily optimized GMP library, for example. Is it a license issue? Can't we reuse GMP/MPFR to offer a Decimal API?

    _decimal should maybe first be distributed as a third party library until it is really well tested and its API is really stable, until we can decide to integrate it. The patch adds __setattr__ to the Decimal class.

    @mdickinson
    Copy link
    Member Author

    Does Python really need yet another multiprecision library?

    It's not really another library: it's a reimplementation of the existing decimal library in C. The decimal library is *hugely* valuable to the financial world, but its slowness is a major concern. _decimal would help to address that concern.

    Can't we reuse GMP/MPFR to offer a Decimal API?

    Nope: those are for binary floating-point. Shoehorning decimal semantics on top of a binary floating-point library is a really bad idea. (Actually, that's a part of why decimal.py is slow---it's using Python's *binary* integers to store *decimal* coefficients, so that even simple addition is now a quadratic operation, thanks to the binary <-> decimal conversions involved.)

    _decimal should maybe first be distributed as a third party library until it is really well tested and its API is > really stable.

    My take is that this has already happened.

    The only problem from my perspective is getting someone to find time to review such a massive patch. I've been wondering whether we could get away with some kind of 'statistical' review: do a large-scale review, and then instead of having someone go through every line of C code, pick a few representative sections at random and review those. If those code portions make it through the review unscathed, declare the code good and merge it in.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Nov 30, 2011

    Binary versus decimal
    ---------------------

    There is already gmpy and bigfloat, based on the heavily optimized GMP library,
    for example. Is it a license issue? Can't we reuse GMP/MPFR to offer a Decimal API?

    _decimal is a PEP-399 compliant C implementation of decimal.py. The underlying
    standard is Cowlishaw/IBM's "General Decimal Arithmetic Specification".

    decimal.py is used for standard-conforming financial calculations. There is
    no way to implement this in a reasonable manner using a binary floating
    point library.

    Additionally, _decimal is also heavily optimized. In fact, for small
    precisions the module has the same speed as gmpy!

    Soundness and code size
    -----------------------

    _decimal should maybe first be distributed as a third party library until
    it is really well tested and its API is really stable, until we can decide
    to integrate it.

    Except for a different directory structure, the cdecimal module is
    identical to this patch. cdecimal has been distributed for almost
    two years now and has been on pypi for a year.

    There have been many downloads from financial institutions, stock
    exchanges and also research institutes. I know for a fact from
    a private email correspondence that libmpdec is used in a billing
    application of a large national NIC.

    cdecimal *appears* to be huge because it has a test suite that
    actually provides 100% code coverage. Indeed this means that even
    every possible malloc failure is simulated together with an assertion
    that the result of the function is (NaN, Malloc_error).

    The test suite now tests against both decimal.py and decNumber.
    It has found several small issues in decimal.py, a bug in
    netlib's dtoa.c, a bug in gmp and a bug in CompCert.

    The latest tests against decNumber have found 18 issues in decNumber
    (that I haven't reported yet).

    In the past 8 months, regression tests for cdecimal-2.3 have been
    running trillions of test cases both with and without Valgrind.

    Review
    ------
    The patch could be audited by focusing on basearith.c, cdecimal.c
    and mpdecimal.c. cdecimal.c is a long but simple wrapper around
    libmpdec. mpdecimal.c contains all functions of the specification.
    I contend that for a C programmer mpdecimal.c is not significantly
    harder to read than decimal.py.

    The tricky algorithms (newtondiv, invroot, sqrt-via-invroot
    and ln) have mechanical proofs in ACL2.

    An initial audit could certainly disregard convolute.c, crt.c,
    difradix2.c, fnt.c, numbertheory.c, transpose.c and umodarith.h.

    These are only needed for the number theoretic transform that kicks
    in at around 22000 digits.

    Context type safety
    -------------------

    The patch adds __setattr__ to the Decimal class.

    Making the context more strictly typed has instantly found a bug
    in one of decimal.py's docstring tests:

    # This doctest has always passed:
    >>> c = Context(ExtendedContext)
    
    # But the operation is meaningless:
    >>> c
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib/python2.7/decimal.py", line 3708, in __repr__
        % vars(self))
    TypeError: %d format: a number is required, not Context
    >>>

    What is the concern about __setattr__? For *setting* contexts, speed
    is not so important (for reading contexts it is).

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Nov 30, 2011

    Mark Dickinson <report@bugs.python.org> wrote:

    The only problem from my perspective is getting someone to find time to review such a massive patch. I've been wondering whether we could get away with some kind of 'statistical' review: do a large-scale review, and then instead of having someone go through every line of C code, pick a few representative sections at random and review those. If those code portions make it through the review unscathed, declare the code good and merge it in.

    The regex module is in a somewhat similar situation. If I'm interpreting this

    http://mail.python.org/pipermail/python-dev/2011-August/113240.html

    dialogue correctly, a complete audit down to the last line isn't
    always necessary.

    @pitrou
    Copy link
    Member

    pitrou commented Nov 30, 2011

    If I'm interpreting this
    http://mail.python.org/pipermail/python-dev/2011-August/113240.html
    dialogue correctly, a complete audit down to the last line isn't
    always necessary.

    It is also helped by the fact you are a core developer and we trust you to be here to do maintenance :)
    I'd add that decimal is a fairly specialized module and the implications of a new engine are not as wide-reaching as a new regex engine. Especially if the pure Python version is still there.

    I think it's still probably a good idea to probe python-dev, if that hasn't already happened.

    @rhettinger
    Copy link
    Contributor

    We've been wanting this for a long time.

    Strong +1 from me.

    @amauryfa
    Copy link
    Member

    I can help with the review. Is http://bugs.python.org/review/7652/show a good starting point? I already have some comments.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Nov 30, 2011

    Antoine Pitrou <report@bugs.python.org> wrote:

    It is also helped by the fact you are a core developer and we trust you to
    be here to do maintenance :)

    Sure. The specification doesn't really change, so the work will hopefully
    be limited. :)

    I think it's still probably a good idea to probe python-dev, if that
    hasn't already happened.

    Yes, I'm planning to do that.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Nov 30, 2011

    Raymond Hettinger <report@bugs.python.org> wrote:

    We've been wanting this for a long time.

    Strong +1 from me.

    Thank you, Raymond!

    @vstinner
    Copy link
    Member

    (Actually, that's a part of why decimal.py is slow---it's
    using Python's *binary* integers to store *decimal* coefficients,
    so that even simple addition is now a quadratic operation,
    thanks to the binary <-> decimal conversions involved.)

    Oh, I forgot this minor detail (base 2 vs base 10).

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Nov 30, 2011

    Amaury Forgeot d'Arc <report@bugs.python.org> wrote:

    I can help with the review. Is http://bugs.python.org/review/7652/show a
    good starting point? I already have some comments.

    Yes, that would be great. Apart from two or three changes that I still
    need to push patch set 4 is the latest version.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 7, 2012

    Jim Jewett <report@bugs.python.org> wrote:

    implementation details. Are there are clear distinctions (type
    info/python bindings/basic arithmetic/advanced
    algorithms/internal-use-only/???)

    I failed to mention that libmpdec also has complete documentation. Perhaps
    that can answer some questions:

    http://www.bytereef.org/mpdecimal/doc/libmpdec/index.html
    http://www.bytereef.org/mpdecimal/doc/libmpdec/functions.html#quiet-functions

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 7, 2012

    Marc-Andre Lemburg <report@bugs.python.org> wrote:

    Does the C version have a C API importable as capsule ?

    Not yet. I'll try to make a list with proposed function names and post it here.

    If not, could you add one and a decimal.h to go with it ?

    Yes, sure.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 7, 2012

    This issue was raised by Jim on Rietveld:

    Currently, the order of arguments in Context.__init__() differs
    from repr(Context):

    >>> Context()
    Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[DivisionByZero, Overflow, InvalidOperation])
    >>> 
    >>> Context(28, ROUND_HALF_EVEN, -999999999, 999999999, 1, [], [DivisionByZero, Overflow, InvalidOperation])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib/python3.2/decimal.py", line 3774, in __init__
        flags = dict([(s, int(s in flags)) for s in _signals])
      File "/usr/lib/python3.2/decimal.py", line 3774, in <listcomp>
        flags = dict([(s, int(s in flags)) for s in _signals])
    TypeError: argument of type 'int' is not iterable
    >>> 

    I find this quite ugly. The repr() order is the good one, since
    it moves the flag dictionaries to the end. I wanted to change
    the order in Context.__init__() to match repr(), but I'm not
    sure if such a change is possible.

    I don't think Python code would initialize a context without keywords,
    but C extensions might.

    @jimjjewett
    Copy link
    Mannequin

    jimjjewett mannequin commented Mar 7, 2012

    On Wed, Mar 7, 2012 at 5:28 AM, Stefan Krah
    <stefan-usenet@bytereef.org> added the comment:

    OK, as a basis for discussion I've added:
    http://hg.python.org/features/cdecimal/file/8b75c2825508/Modules/_decimal/FILEMAP.txt

    That is indeed very helpful. So helpful that now I understand well
    enough to have additional gripes. :D

    Starting from that URL, I don't actually find setup.py.

    I am assuming (but would prefer to have the file explicitly state)
    that _decimal.c and docstrings.h are the only source files, and that
    setup.py would be the only build infrastructure needed if you already
    had libmpdec.a.

    I'm not sure what sort of failures building in the normal order led
    to, but that is certainly something worth documenting, and (ideally)
    fixing.

    I didn't see any mention of the literature subdirectory, which makes
    me wonder if there were other files not listed in the map. (Not yet
    curious enough to verify for myself, though.) (*.txt files?)

    I suppose a subdirectory name like "python" makes sense when you look
    at the library as a C/C++ project that happens to provide python
    bindings; as part of the python core, it is misleading. It sounds
    like it should be named "extended_tests" or some such. (Note that
    this assumes it is strictly for tests; if you are also using it to
    provide extra functionality, or to generate some of the source code,
    then I agree with Benjamin that it should move to tools, and the
    generated code should have clear comments right at the top warning
    that it is generated.)

    Within the library, does io.[ch] really limit itself to ASCII? If so,
    then I don't know why you're worried about the Turkish i. If you mean
    generic text, then please don't specify ASCII.

    Within memory.[ch], how much of this configurability is useful (or
    even available) to a python user, as opposed to an extension writer?
    Or is it really just for testing? As in, would it be reasonable to
    just have a single header with a half-dozen #defines, but to replace
    that header when doing a memory-test build? Or at least to hide the
    alternative function definitions inside an obvious #ifdef, so that
    they don't take up memory and attention when they aren't applicable?

    Under the Bignum section, it mentions that functions from these files
    are ultimately used in _mpd_fntmul, but doesn't mention where that is
    (in the main _cdecimal.c)

    Also, when it talks about "large arrays", could you clarify that it
    isn't talking about arrays of values or even matrixes, it is just
    talking about numbers large enough that even representing them takes
    at least N bytes?

    > Would it at least be OK to wrap them in stubs for exporting, so that
    > the test logic could be places with the others tests?  (I worry that
    > some tests may stop getting run if someone else modifies the build
    > process and doesn't notice the unusual location.)

    tests/runtest.c won't compile then. I'll look into the stub and also
    the _testhelp suggestions.

    OK, let me rephrase. Is newton division exported to users, or used
    internally, or is it just for testing purposes?

    _mpd_qtest_newtondiv is documented as a testcase; I would rather see
    it move to a test file. Why can't it? If it is because of
    _mpd_qdiv_inf, _settriple, _mpd_qbarrett_divmod, _mpd_real_size (the
    only apparently internal things it uses), then why are these internal?
    Could they be exposed at least to test functions? If not, could they
    at least be wrapped in something that could exposed, such as
    _testhelp_mpd_qdiv_inf?

    > > "Infinity", "InFinItY", "iNF" are all allowed by the specification.

    > OK; so is io.c part of the library, or part of the python binding?

    I see a potential source of confusion: io.c is firmly part of the
    library.

    [And should therefore be available when used without python?]

    All PEP-3101 formatting is part of libmpdec, because I like
    the mini language. io.c only understands ASCII and UTF-8 fill characters.

    It is the *library* tests that would fail under the Turkish locale
    (if not for _mpd_strneq).

    Are those tests relevant to a _cdecimal built in to python itself? If
    not, then the comment is at least misleading. Would it make sense to
    have a directory for files that are used only for the standalone C
    library, but not when built as part of python?

    > Good enough, though I would rather see that as a comment near the assembly.

    Comments how to enforce an ANSI build (much slower!) are in LIBTEST.txt
    and now also in FILEMAP.txt.

    I would not have made the leap from that to "What to do if the
    assembly code needs to be replaced or even just changed."

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 8, 2012

    Jim Jewett <report@bugs.python.org> wrote:

    > OK, as a basis for discussion I've added:
    > http://hg.python.org/features/cdecimal/file/8b75c2825508/Modules/_decimal/FILEMAP.txt

    Starting from that URL, I don't actually find setup.py.

    It's the setup.py from the Python top level directory.

    I'm not sure what sort of failures building in the normal order led
    to, but that is certainly something worth documenting, and (ideally)
    fixing.

    I do not have access to AIX. On Windows the failures were locale related
    when mixing the dynamic and static runtimes.

    I didn't see any mention of the literature subdirectory, which makes
    me wonder if there were other files not listed in the map. (Not yet
    curious enough to verify for myself, though.) (*.txt files?)

    FILEMAP.txt was intended to get people started, not to be a polished work.

    I suppose a subdirectory name like "python" makes sense when you look
    at the library as a C/C++ project that happens to provide python
    bindings; as part of the python core, it is misleading.

    OK.

    provide extra functionality, or to generate some of the source code,

    There is no source code generation. I'm not sure where this idea comes
    from. genlocale.py e.g. has this comment:

    Usage: ../../../python genlocale.py | ../tests/runtest -

    Within the library, does io.[ch] really limit itself to ASCII? If so,
    then I don't know why you're worried about the Turkish i. If you mean
    generic text, then please don't specify ASCII.

    I do mean ASCII. Please run this gdb session:

    diff --git a/Modules/_decimal/io.c b/Modules/_decimal/io.c
    --- a/Modules/_decimal/io.c
    +++ b/Modules/_decimal/io.c
    @@ -245,7 +245,7 @@
                    if (digits > (size_t)(ctx->prec-ctx->clamp))
                            goto conversion_error;
            }
    -       else if (_mpd_strneq(s, "inf", "INF", 3)) {
    +       else if (strncasecmp(s, "inf", 3) == 0) {
                    s += 3;
                    if (*s == '\0' || _mpd_strneq(s, "inity", "INITY", 6)) {
                            /* numeric-value: infinity */
    gdb ../../python
    b mpd_qset_string
    r
    >>> locale.setlocale(locale.LC_ALL, 'tr_TR.utf8')
    'tr_TR.utf8'
    >>> from decimal import *
    >>> Decimal("Infinity")

    (gdb)
    248 else if (strncasecmp(s, "inf", 3) == 0) {
    (gdb) p s
    $1 = 0x7ffff7191280 "Infinity"
    (gdb) p s[0]
    $2 = 73 'I'
    (gdb) n
    259 if ((coeff = scan_dpoint_exp(s, &dpoint, &exp, &end)) == NULL)

    Clearly 'I' is ASCII and strncasecmp fails, exactly as written in the
    comment above _mpd_strneq().

    Within memory.[ch], how much of this configurability is useful (or
    even available) to a python user, as opposed to an extension writer?

    Since it is in the libmpdec section, "User" refers to the extension writer.
    I can simply drop the "User".

    Under the Bignum section, it mentions that functions from these files
    are ultimately used in _mpd_fntmul, but doesn't mention where that is
    (in the main _cdecimal.c)

    OK.

    Also, when it talks about "large arrays", could you clarify that it
    isn't talking about arrays of values or even matrixes, it is just
    talking about numbers large enough that even representing them takes
    at least N bytes?

    But it is referring to abstract sequences or arrays of values less
    than a certain prime. These values happen to be the coefficient of
    an mpd_t, but you could perform the transform on any sequence.

    I thought 'Bignum' would already imply an array of machine words
    representing a number.

    >> Would it at least be OK to wrap them in stubs for exporting, so that
    >> the test logic could be places with the others tests? ??(I worry that
    >> some tests may stop getting run if someone else modifies the build
    >> process and doesn't notice the unusual location.)

    > tests/runtest.c won't compile then. I'll look into the stub and also
    > the _testhelp suggestions.

    OK, let me rephrase. Is newton division exported to users, or used
    internally, or is it just for testing purposes?

    It's used internally as _mpd_qbarrett_divmod(). When the coefficient has
    more than MPD_NEWTONDIV_CUTOFF words, the division functions dispatch
    to _mpd_qbarrett_divmod().

    _mpd_qtest_newtondiv is documented as a testcase; I would rather see
    it move to a test file. Why can't it?

    I said in my last mail that I'll look into it.

    >> > "Infinity", "InFinItY", "iNF" are all allowed by the specification.

    >> OK; so is io.c part of the library, or part of the python binding?

    > I see a potential source of confusion: io.c is firmly part of the
    > library.

    [And should therefore be available when used without python?]

    I meant: Despite the fact that io.c might /appear/ to be specifically
    written for the module because of the presence of PEP-3101 references,
    it is part of the standalone (moduleless) library. However, _decimal.c
    uses all parts of io.c except for a couple of functions at the bottom
    of the file that are useful for debugging.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 10, 2012

    Here's a new patch that addresses several remarks:

    o _decimal now has __floor__ and __ceil__methods.

    o libmpdec is now in a separate directory.

    o I removed the big libmpdec test suite. This is why:

     - It reduces the number of files that has been criticized multiple times.
    
     - Probably I'll be the only one who will be running the test suite.
    
     - The test suite is still on PyPI.
    
     - The test suite is so large that it's a magnet for toolchain bugs.
       For example, it found a bug in suncc (acknowledged and fixed by Sun)
       and a bug in CompCert (acknowledged and fixed by Xavier Leroy).
    
       I'm really worried that *if* someone runs the tests and finds
       an obscure bug, I'll have to spend hours debugging something
       that might ultimately be an external bug.
    

    o Renamed "python" directory into "tests".

    o Test functions in mpdecimal.c are removed.

    o All files in libmpdec that aren't required for _decimal are removed.

    o Extra functionality in _decimal.c is commented out. This does not
    yet cover the FloatOperation signal, which I would like to add
    to decimal.py as well (I'll ask later on python-dev).

    @Arfrever
    Copy link
    Mannequin

    Arfrever mannequin commented Mar 10, 2012

    Please add --with-system-libmpdec (or --with-system-mpdecimal) option in configure, similarly to --with-system-expat and --with-system-ffi options.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 11, 2012

    Benjamin Peterson <report@bugs.python.org> wrote:

    Speaking of inline, the "inline" keyword will have to go because it's not C89.

    Actually the trickier instances of "inline" in the .c files are already
    suppressed when LEGACY_COMPILER (i.e. C89) is defined. I've now listed the
    machine options here:

    http://hg.python.org/features/cdecimal/file/0f032cda94aa/Modules/_decimal/README.txt

    As I now remember, that was in fact necessary for CompCert. The "static inline"
    instances in header files might not be a problem even for embedded compilers,
    see e.g.:

    http://embeddedgurus.com/barr-code/2011/03/do-inline-function-bodies-belong-in-c-header-files/

    IIRC also the Linux kernel uses "static inline" in header files.

    @rhettinger
    Copy link
    Contributor

    FWIW, I think we would be better off if this patch were merged in soon. Waiting until later in the release cycle risks introducing bugs that we won't have time to notice or fix. An early merge lets more people exercise the code.

    @benjaminp
    Copy link
    Contributor

    Stefan, please feel free to commit the latest patch.

    @mdickinson
    Copy link
    Member Author

    FWIW, I think we would be better off if this patch were merged in soon.

    +1

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 14, 2012

    Mark Dickinson <report@bugs.python.org> wrote:

    > FWIW, I think we would be better off if this patch were merged in soon.

    +1

    OK, I'm counting three +1 for merging soon. Thanks everyone for the
    encouragement!

    I'll then proceed with the merge this weekend or shortly after,
    with one caveat:

    My *second* self-review of mpdecimal.c is currently at 17%, but I can
    as well continue with that if the whole thing is merged. I'll probably
    replace the caching of ln(10) (not thread-safe, protected by the GIL)
    with a real thread-safe version.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 14, 2012

    Arfrever Frehtes Taifersar Arahesis report@bugs.python.org wrote:

    Please add --with-system-libmpdec (or --with-system-mpdecimal) option in
    configure, similarly to --with-system-expat and --with-system-ffi options.

    I've added that to the list of issues. However, if there is any change in
    the behavior of decimal.py that also applies to the library, the libmpdec
    shipped with Python will be silently updated.

    I can probably release a matching external libmpdec with every major Python
    release, but I can't promise to track all minor releases.

    This would mean that for Python-3.3 the yet unreleased mpdecimal-2.4,
    hopefully with thread-safe mpd_pow(), mpd_ln() and mpd_log10() should
    be used.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 20, 2012

    We need to decide what to do with the different limits of the 64-bit and
    32-bit versions:

                   MAX_EMAX    
    

    default context 10**9-1
    64-bit 10**18-1
    32-bit 425000000

    I think it would be annoying to have the values in DefaultContext,
    ExtendedContext and BasicContext depend on the machine.

    The best thing might be to use Emax=10**8-1 and Emin=-(10**8-1) throughout.
    I don't think many applications depend on having Emax=10**9-1. If they do,
    they'll have to use the 64-bit version.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 21, 2012

    The best thing might be to use Emax=10**8-1 and Emin=-(10**8-1) throughout.
    I don't think many applications depend on having Emax=10**9-1. If they do,
    they'll have to use the 64-bit version.

    10**6-1 would be another option. The advantage is that it's visually
    obvious that the default exponents have changed.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Mar 21, 2012

    New changeset 7355550d5357 by Stefan Krah in branch 'default':
    Issue bpo-7652: Integrate the decimal floating point libmpdec library to speed
    http://hg.python.org/cpython/rev/7355550d5357

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Mar 23, 2012

    New changeset f6d646e30028 by Stefan Krah in branch 'default':
    Issue bpo-7652: Enable linking of _decimal.so against an installed libmpdec.
    http://hg.python.org/cpython/rev/f6d646e30028

    @vstinner
    Copy link
    Member

    You may now close the issue.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Mar 27, 2012

    I have a couple of questions about the proposed capsule C API. In order to
    be useful, it should be possible to set temporary contexts and pass them
    to functions. For example, PyDec_Add could look like:

       PyDec_Add(PyObject *a, PyObject *b, PyObject *context);

    To stay sane, either access macros or access functions to the context would
    need to be provided:

      CTX(workcontext)->prec = 100;
      PyDec_SetPrec(workcontext, 100);
      PyDecContext_SetPrec(workcontext, 100);

    Probably flags would need to be checked at some point:

    if (CTX(workcontext)->status & MPD_Underflow)
    if (PyDec_GetStatus(workcontext) & MPD_Underflow)
    if (PyDecContext_GetStatus(workcontext) & MPD_Underflow)

    I wonder if users would not be better off if libmpdec functions and
    access macros were exposed instead. The big advantage is that errors
    (even malloc errors) propagate in the form of NaNs, so it is not
    necessary to do repeated error checking.

    Also, I find things like (status & MPD_Underflow) more readable than
    the alternatives above.

    I've attached a short (fake) example to demonstrate what I mean.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jul 1, 2012

    I've opened bpo-15237 for the capsule API. I didn't add everyone to the
    nosy list, since I suspect it's not of general interest.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Jul 12, 2012

    New changeset afdb0e1a9dac by Stefan Krah in branch 'default':
    Issue bpo-7652: Clean up _mpd_qinvroot() and mark it LIBMPDEC_ONLY. Use the
    http://hg.python.org/cpython/rev/afdb0e1a9dac

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jul 12, 2012

    I switched the algorithm in mpd_qsqrt() to the one from decimal.py.
    Previously the square root was calculated in terms of 1/invsqrt(x).

    Curiously enough this scheme _always_ seems to produce exact results
    when expected, but I don't have a proof. I remember I left this in because the specification gives some leeway with respect to exact
    results:

    "Square-root can also be calculated by using the power operation (with a second operand of 0.5). The result in that case will not be exact in most cases, and may not be correctly rounded."

    Anyway, the algorithm from decimal.py gives the desired guarantees
    and is also faster.

    Since we're almost in beta-2 stage, would someone be able to do a
    post commit review of mpd_qsqrt()? It should be a direct translation
    from the function in decimal.py.

    @amauryfa
    Copy link
    Member

    I compared both implementations, and they are the same.

    I noticed that on line 7537, the call to mpd_qshiftl() may "goto malloc_error;". I think there is a memory leak in this case, "mpd_del(&c)" and 2 others lines are skipped.

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Jul 12, 2012

    Thanks, Amaury! -- "goto malloc_error" should not leak, because there's always
    a jump to the "out" label in line 7563.

    I use this idiom a lot ever since I saw it in several places in the Linux
    kernel. Of course it's a matter of taste.

    @vstinner
    Copy link
    Member

    Is there some remaining work on this issue? Or can it be closed?

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Aug 12, 2012

    I'm almost done with my (second) self-review of mpdecimal.c. The only
    functions missing are all Karatsuba functions and mpd_qpowmod().

    If there are any takers, I would be very happy. For the Karatsuba functions
    you'll probably need Roman Maeder's paper, which I could send to anyone
    interested. All I can promise is that it's a beautiful formulation of the
    algorithm. :)

    @skrah
    Copy link
    Mannequin

    skrah mannequin commented Sep 2, 2012

    My review is done. The Karatsuba function is basically a small stack machine
    and very hard to prove formally as far as I can see. The algorithm is cited
    in TAOCP and the subdivision is brute force tested for all combinations of
    coefficient lengths of the two input operands that are used in libmpdec
    (currently 256 < nwords <= 1024).

    @skrah skrah mannequin self-assigned this Sep 2, 2012
    @skrah skrah mannequin closed this as completed Sep 2, 2012
    @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
    extension-modules C modules in the Modules dir performance Performance or resource usage stdlib Python modules in the Lib dir
    Projects
    None yet
    Development

    No branches or pull requests

    9 participants