Float
eryk sun
eryksun at gmail.com
Sat Jul 30 14:19:19 EDT 2016
On Sat, Jul 30, 2016 at 4:03 PM, Dennis Lee Bieber
<wlfraed at ix.netcom.com> wrote:
> And in a rather convoluted route, one can get to the underlying
> representation...
>
>>>> import struct
>>>> f = struct.pack(">f", 3.0)
>>>> i = struct.pack(">i", 3)
>>>> fi = struct.unpack(">i", f)
>>>> ii = struct.unpack(">i", i) #really a waste of time
>>>> "0x%8.8X 0x%8.8X" % (fi[0], ii[0])
> '0x40400000 0x00000003'
A CPython float is a C double-precision float ('d'), which is an IEEE
754 binary64 on all of CPython's supported platforms. This format has
a precision of 15 decimal digits (rounded down from 15.95). Uniquely
representing a C double requires 17 decimal digits. See the following
Wikipedia article for more information:
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
The underlying representation of 3.0 is as follows:
>>> n = 3.0
>>> hex(struct.unpack('Q', struct.pack('d', n))[0])
'0x4008000000000000'
You can confirm this by attaching a native debugger (such as gdb on
Linux or cdb on Windows) and pointer casting the float object's
ob_fval field as a uint64_t integer:
Linux (python3-dbg):
>>> id(n)
140737353618480
(gdb) p/x *(uint64_t *)&((PyFloatObject *)140737353618480)->ob_fval
$1 = 0x4008000000000000
Windows (python_d.exe):
>>> id(n)
2398135104848
0:000> ?? *(ULONG64 *)&((PyFloatObject *)2398135104848)->ob_fval
unsigned int64 0x40080000`00000000
We can break this down as follows:
sign = 0x400 >> 11
= 0
exponent = (0x400 & (1 << 11 - 1)) - 1023
= 1
significand = 1 + 0x8000000000000 / 2 ** 52
= 1.5
n = (-1) ** sign * significand * 2 ** exponent
= 3.0
Python's float type has a hex() method that represents the value in
hexadecimal as follows:
>>> n.hex()
'0x1.8000000000000p+1'
This format shows the implicit integer bit of the normalized
significand and decodes the sign and exponent values for you.
More information about the Python-list
mailing list