-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
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
gh-102471: convert decimal module to use PyLong_Export API (PEP 757) #128267
base: main
Are you sure you want to change the base?
gh-102471: convert decimal module to use PyLong_Export API (PEP 757) #128267
Conversation
const uint32_t base = (uint32_t)1 << layout->bits_per_digit; | ||
const uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS; | ||
const Py_ssize_t len = export_long.ndigits; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove redundant const
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not we check if layout->bits_per_digit >= 32
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove redundant consts.
Which one looks redundant for you?
Should not we check if layout->bits_per_digit >= 32?
I was thinking about an assert. Do you think it's ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which one looks redundant for you?
All const
s applied to POD types are redundant. 3 lines here and 1 line for export_long.value
.
Some style guides require to add const
for all variables and function parameters, but this in not in CPython style guide. It makes the code too verbose and more difficult to read.
I was thinking about an assert. Do you think it's ok?
It is okay for now. But if we want to make it an exemplary code, we should remove dependence on CPython implementation details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All consts applied to POD types are redundant.
Why? Doing something like value += 1
currently will produce an error at compilation time, while without const
- not.
It makes the code too verbose and more difficult to read.
It emphasize programmer intentions not just for compiler, but for human being as well.
It is okay for now.
Ok, then I'll add assertions for all assumptions, that are silent not (endianness, digit order, etc). I think it's fine for a stdlib module. Caching in the module state seems overkill for this.
Modules/_decimal/_decimal.c
Outdated
#else | ||
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30" | ||
#endif | ||
if (base > UINT16_MAX) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use digit_size
to chose between mpd_qimport_u32
and mpd_qimport_u16
. Values other than 2 or 4 are errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are assuming that CHAR_BIT == 8
. I'm not sure we should do this.
With added assert(layout->bits_per_digit <= 32)
above (or RuntimeError) - I think nothing should be changed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are assuming that CHAR_BIT == 8
everywhere in the code.
The new C API is used to untie the decimal
code from the CPython implementation details. Other implementations can have bits_per_digit == 8
and digit_size == 1
or bits_per_digit == 16
and digit_size == 2
. While it is not particularly reasonable, but it is even possible to have bits_per_digit < 16
and digit_size > 2
. It is better to fail with a relevant error message than produce incorrect result or crash.
Doing the runtime check for every conversion can be too costly, so we can do this only at the module import (and cache the base value).
We should also check digits_order
and digit_endianness
. mpd_qimport_u16
and mpd_qimport_u32
only work if digits_order
is -1 and digit_endianness
is native. In other cases we should create a temporary digit array with expected order and endianness. Or at least raise a runtime error.
if (-(int64_t)UINT32_MAX <= value && value <= (int64_t)UINT32_MAX) { | ||
_dec_settriple(dec, value < 0 ? MPD_NEG : MPD_POS, | ||
(uint32_t)Py_ABS(value), 0); | ||
mpd_qfinalize(MPD(dec), ctx, status); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth it to have this fast-path? (Why not always calling mpd_qset_i64()?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's up to reviewers. In old pr I did benchmarks:
#127925 (comment)
For reviewers: in the history included version, where PyLong_Export always set
digits
.Benchmark hidden because not significant (1): Decimal(1<<3000)
benchmark code