Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-111545: Add Py_HashDouble() function#113115

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
vstinner wants to merge2 commits intopython:mainfromvstinner:hash_double4

Conversation

vstinner
Copy link
Member

@vstinnervstinner commentedDec 14, 2023
edited by github-actionsbot
Loading

Add tests: Modules/_testcapi/hash.c and
Lib/test/test_capi/test_hash.py.


📚 Documentation preview 📚:https://cpython-previews--113115.org.readthedocs.build/

Add tests: Modules/_testcapi/hash.c andLib/test/test_capi/test_hash.py.
@@ -84,17 +84,20 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0};
*/

Py_hash_t
_Py_HashDouble(PyObject *inst, double v)
Py_HashDouble(double v)
{
int e, sign;
double m;
Py_uhash_t x, y;

if (!Py_IS_FINITE(v)) {

Choose a reason for hiding this comment

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

What if remove this and keep onlyPy_IS_INFINITY(v) check?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

If prefer to have a deterministic behavior and always return the same hash value (0) ifvalue is NaN. There are legit use cases to treat NaN as hash value 0.

With the following change, only check for,Py_HashDouble() hangs (fail to exit the loop) ifvalue is NaN.

diff --git a/Python/pyhash.c b/Python/pyhash.cindex f64edde4043..23aa2dac7cc 100644--- a/Python/pyhash.c+++ b/Python/pyhash.c@@ -90,14 +90,8 @@ Py_HashDouble(double v)     double m;     Py_uhash_t x, y;-    if (!Py_IS_FINITE(v)) {-        if (Py_IS_INFINITY(v)) {-            return (v > 0 ? _PyHASH_INF : -_PyHASH_INF);-        }-        else {-            assert(Py_IS_NAN(v));-            return 0;-        }+    if (Py_IS_INFINITY(v)) {+        return (v > 0 ? _PyHASH_INF : -_PyHASH_INF);     }      m = frexp(v, &e);

With the following change, Py_HashDouble() returns-_PyHASH_INF ifvalue is NaN, sinceNaN > 0 is false:

diff --git a/Python/pyhash.c b/Python/pyhash.cindex f64edde4043..a853d6dad99 100644--- a/Python/pyhash.c+++ b/Python/pyhash.c@@ -91,13 +91,8 @@ Py_HashDouble(double v)     Py_uhash_t x, y;      if (!Py_IS_FINITE(v)) {-        if (Py_IS_INFINITY(v)) {-            return (v > 0 ? _PyHASH_INF : -_PyHASH_INF);-        }-        else {-            assert(Py_IS_NAN(v));-            return 0;-        }+        // v can be NaN+        return (v > 0 ? _PyHASH_INF : -_PyHASH_INF);     }      m = frexp(v, &e);

Choose a reason for hiding this comment

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

What if usePy_IS_INFINITY() instead of!Py_IS_FINITE()?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

My first attempt (first patch in my comment) leads to a hang if you pass NaN.

Why do you want to avoid!Py_IS_FINITE +Py_IS_INFINITY check? Are you worried about performance?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Recipe of What's New in Python 3.13:

Py_hash_thash_double(PyObject*obj,doublevalue)     {if (!Py_IS_NAN(value)) {returnPy_HashDouble(value);         }else {returnPy_HashPointer(obj);         }     }

Using this recipe and the current implementation, there are 3 code paths:

  • NaN: 1 test (Py_IS_NAN()),hash_double() callsPy_HashPointer().
  • infinity: 3 tests (!Py_IS_NAN(),!Py_IS_FINITE(),Py_IS_INFINITY()),return (v > 0 ? _PyHASH_INF : -_PyHASH_INF).
  • finite: 2 tests (!Py_IS_NAN(),Py_IS_FINITE()), the loop.

I don't think that it's a big deal to add 1 or 2 tests per float point number. I care more about the API, having a deterministic behavior for the 3 cases.

Choose a reason for hiding this comment

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

I want to avoid any promises about NaN. It should be recommended to not use this function for NaN.

Comment on lines +55 to +66
* If *value* is positive infinity, return :data:`sys.hash_info.inf
<sys.hash_info>`.
* If *value* is negative infinity, return :data:`-sys.hash_info.inf
<sys.hash_info>`.
* If *value* is not-a-number (NaN), return :data:`sys.hash_info.nan
<sys.hash_info>` (``0``).
* Otherwise, return the hash value of the finite *value* number.

.. note::
Return the hash value ``0`` for the floating point numbers ``-0.0`` and
``+0.0``, and for not-a-number (NaN). ``Py_IS_NAN(value)`` can be used to
check if *value* is not-a-number.

Choose a reason for hiding this comment

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

It exposes too much implementation details why already exposed in different place. Why not simply say that it is equivalent to hash() of Python float object if it is not a NaN? And if it is a NaN, you should use other value to avoid collisions.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Are you talking about the note, or describing the 3 cases and return values? I can just remove the note. My idea is to suggest using Py_IS_NAN() to treate NaN differently. But I'm not sure which implementation to suggest.

@zooba says that if you have a Python object, just callPyObject_Hash(obj) on it 😁

Choose a reason for hiding this comment

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

About describing all 3 cases. It should already be described in other place (documentation forsys.hash_info orfloat orhash()), and if it is not described in details, than it is not necessary for users. You should only document that for non-NaN values it returns the same result as for hash() for Python float object.

@vstinner
Copy link
MemberAuthor

I created PR#112095 more than 1 month ago. I spent time to run benchmark, implement different APIs, try to collect feedback on each API, and discuss in length advantages and disadvantages of each API. Sadly, we failed to reach a consensus on the API. Nowanother API is being discussed. The API looks simple to me, I didn't expect to spend more than one month on a single function.

I need to take a break from that topic. I don't have the energy to dig into these discussions. I prefer to close the PR for now.

@vstinnervstinner deleted the hash_double4 branchDecember 20, 2023 11:02
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@serhiy-storchakaserhiy-storchakaserhiy-storchaka approved these changes

@tirantiranAwaiting requested review from tirantiran is a code owner

Assignees
No one assigned
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

2 participants
@vstinner@serhiy-storchaka

[8]ページ先頭

©2009-2025 Movatter.jp