74 lines
2.4 KiB
Markdown
74 lines
2.4 KiB
Markdown
The code below deals with expansion of single-precision IEEE 754 floating point values to
|
|
double-precision (and vice-versa) without losing detail of NaN bit-patterns.
|
|
|
|
```python
|
|
def to_bytes(self):
|
|
"""Converts this 32-bit single-precision floating point value to its binary32 format,
|
|
taking care to preserve the quiet/signalling bit-pattern of NaN values, unlike its
|
|
`struct.pack('>f', ...)` equivalent.
|
|
|
|
```python
|
|
>>> Float.from_bytes(b'\\x7f\\x80\\x00{')
|
|
Float(nan)
|
|
>>> Float.from_bytes(b'\\x7f\\x80\\x00{').to_bytes()
|
|
b'\\x7f\\x80\\x00{'
|
|
|
|
>>> struct.unpack('>f', b'\\x7f\\x80\\x00{')[0]
|
|
nan
|
|
>>> Float(struct.unpack('>f', b'\\x7f\\x80\\x00{')[0]).to_bytes()
|
|
b'\\x7f\\xc0\\x00{'
|
|
>>> struct.pack('>f', struct.unpack('>f', b'\\x7f\\x80\\x00{')[0])
|
|
b'\\x7f\\xc0\\x00{'
|
|
|
|
```
|
|
|
|
(Note well the difference between `7f80007b` and `7fc0007b`!)
|
|
|
|
"""
|
|
|
|
if math.isnan(self.value) or math.isinf(self.value):
|
|
dbs = struct.pack('>d', self.value)
|
|
vd = struct.unpack('>Q', dbs)[0]
|
|
sign = vd >> 63
|
|
payload = (vd >> 29) & 0x007fffff
|
|
vf = (sign << 31) | 0x7f800000 | payload
|
|
return struct.pack('>I', vf)
|
|
else:
|
|
return struct.pack('>f', self.value)
|
|
|
|
@staticmethod
|
|
def from_bytes(bs):
|
|
"""Converts a 4-byte-long byte string to a 32-bit single-precision floating point value
|
|
wrapped in a [Float][preserves.values.Float] instance. Takes care to preserve the
|
|
quiet/signalling bit-pattern of NaN values, unlike its `struct.unpack('>f', ...)`
|
|
equivalent.
|
|
|
|
```python
|
|
>>> Float.from_bytes(b'\\x7f\\x80\\x00{')
|
|
Float(nan)
|
|
>>> Float.from_bytes(b'\\x7f\\x80\\x00{').to_bytes()
|
|
b'\\x7f\\x80\\x00{'
|
|
|
|
>>> struct.unpack('>f', b'\\x7f\\x80\\x00{')[0]
|
|
nan
|
|
>>> Float(struct.unpack('>f', b'\\x7f\\x80\\x00{')[0]).to_bytes()
|
|
b'\\x7f\\xc0\\x00{'
|
|
>>> struct.pack('>f', struct.unpack('>f', b'\\x7f\\x80\\x00{')[0])
|
|
b'\\x7f\\xc0\\x00{'
|
|
|
|
```
|
|
|
|
(Note well the difference between `7f80007b` and `7fc0007b`!)
|
|
|
|
"""
|
|
vf = struct.unpack('>I', bs)[0]
|
|
if (vf & 0x7f800000) == 0x7f800000:
|
|
# NaN or inf. Preserve quiet/signalling bit by manually expanding to double-precision.
|
|
sign = vf >> 31
|
|
payload = vf & 0x007fffff
|
|
dbs = struct.pack('>Q', (sign << 63) | 0x7ff0000000000000 | (payload << 29))
|
|
return Float(struct.unpack('>d', dbs)[0])
|
|
else:
|
|
return Float(struct.unpack('>f', bs)[0])
|
|
```
|