classification
Title: Minimum denormal or ** bug
Type: behavior Stage: resolved
Components: Interpreter Core, Windows Versions:
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: Kevin Braun, SilentGhost, paul.moore, steve.dower, tim.golden, tim.peters, zach.ware
Priority: normal Keywords:

Created on 2019-08-07 15:32 by Kevin Braun, last changed 2019-08-07 17:45 by steve.dower. This issue is now closed.

Messages (6)
msg349168 - (view) Author: Kevin Braun (Kevin Braun) Date: 2019-08-07 15:32
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]

I believe 2**-1074 is the smallest denormalized number for Python on my system (Windows), so I would expect 2**-1075 to yield 0.0, but it does not.  Instead:
>>> 2**-1074==2**-1075
True
>>> (2**-1074).hex()
'0x0.0000000000001p-1022'
>>> (2**-1075).hex()
'0x0.0000000000001p-1022'

And, the above is not consistent with the following:
>>> (2**-1074)/2
0.0
>>> (2**-1074)/2 == 2**-1075
False
>>> 1/2**1075
0.0
>>> 1/2**1075 == 2**-1075
False

Given the above observations, I suspect there is a bug in **.
msg349171 - (view) Author: SilentGhost (SilentGhost) * (Python triager) Date: 2019-08-07 15:42
This seems like a Windows-specific issues. Using the same version of python built with gcc 8.3, I get the results inline with your expectations and all the comparisons yield exactly opposite values.
msg349172 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2019-08-07 15:57
Python delegates exponentiation with a Python float result to the platform C's double precision `pow()` function.  So this is just what the Windows C pow(2.0, -1075.0) returns.  All native floating point operations are subject various kinds of error, and this is one.

>>> import math
>>> math.pow(2.0, -1075.0)
5e-324
>>> math.pow(2.0, -1074.0) # same thing
5e-324

To avoid intermediate libm rounding errors, use ldexp instead:

>>> math.ldexp(1, -1074) # 2.0 ** -1074
5e-324
>>> math.ldexp(1, -1075) # 2.0 ** -1075
0.0
msg349177 - (view) Author: Steve Dower (steve.dower) * (Python committer) Date: 2019-08-07 17:14
I have nothing to contribute here. Tim's right.

(I would love Python to have infinite precision float by default...)
msg349180 - (view) Author: Tim Peters (tim.peters) * (Python committer) Date: 2019-08-07 17:40
Since this depends on the platform libm implementation of pow(), I'm closing this as "won't fix".

Steve, on the chance you're serious ;-) , there are implementations of the "constructive reals", which indeed act like infinite-precision floats.  But they tend to be very slow, and, e.g., testing two of them for equality is, in general, undecidable even in theory.  Here's one such:

https://www.chiark.greenend.org.uk/~sgtatham/spigot/
msg349181 - (view) Author: Steve Dower (steve.dower) * (Python committer) Date: 2019-08-07 17:45
Only half serious ;)

I'd settle for "reliable, consistent and easy to explain rounding", which is unfortunately not what IEEE 754 provides (unless you assume significant amounts of CS background that most Python users do not have). But then, nothing else really provides it either, so it's more of a dream than a plan :)
History
Date User Action Args
2019-08-07 17:45:42steve.dowersetmessages: + msg349181
2019-08-07 17:40:20tim.peterssetstatus: open -> closed
versions: - Python 3.7
messages: + msg349180

resolution: wont fix
stage: resolved
2019-08-07 17:14:04steve.dowersetmessages: + msg349177
2019-08-07 15:57:43tim.peterssetnosy: + tim.peters
messages: + msg349172
2019-08-07 15:42:07SilentGhostsetnosy: + paul.moore, tim.golden, SilentGhost, zach.ware, steve.dower
messages: + msg349171
components: + Windows
2019-08-07 15:32:45Kevin Brauncreate