lvext
Exact pure-Python codec for x87 80-bit extended-precision floats.
Install
pip install lvext
Usage
from decimal import Decimal
from fractions import Fraction
from lvext import Ext80, ExtKind, Rounding
# Every float fits exactly, and the bytes come out big-endian unless you ask.
one = Ext80.from_float(1.0)
assert one.hex() == "3fff8000000000000000"
assert one.to_bytes("little") == bytes.fromhex("0000000000000080ff3f")
# Decode to the exact value: a Fraction, or a Decimal with every digit.
value = Ext80.from_hex("4000 c90f daa2 2168 c235")
assert value.kind is ExtKind.NORMAL
assert str(value.to_decimal()).startswith("3.14159265358979323851280895940618620443")
# Narrow to a float; the result says whether anything was lost.
assert value.narrow() == (3.141592653589793, False)
# Build from exact numbers, rounding once, in any of the four x87 modes.
third = Ext80.from_fraction(Fraction(1, 3), Rounding.TOWARD_ZERO)
assert third.hex() == "3ffdaaaaaaaaaaaaaaaa"
assert Ext80.from_decimal(Decimal("0.1")).hex() == "3ffbcccccccccccccccd"
The float-in, float-out helpers from the first release are still there:
from lvext import pack_ext, unpack_ext
assert pack_ext(-2.5) == bytes.fromhex("c000a000000000000000")
assert unpack_ext(pack_ext(-2.5)) == -2.5
What it handles
- The format. 1 sign bit, a 15-bit exponent biased by 16383, an explicit integer
bit and a 63-bit fraction (Intel SDM Vol. 1, 4.2 and 8.2.2).
Ext80holds the three fields bit for bit, so all 2**80 encodings round-trip through bytes, hex and integers in either byte order. x87 memory order is little-endian. - Exact values.
to_fraction()andto_decimal()never round; the Decimal holds every digit (thousands of them near the ends of the range) whatever the context precision. - Rounding.
from_fraction,from_decimalandfrom_numberround once to 64 mantissa bits, andnarrow/to_floatround once to a double, withRounding.NEAREST_EVEN(the default),FLOOR,CEILINGorTOWARD_ZERO. Underflow is gradual; overflow gives infinity or the largest finite value as IEEE 754 prescribes for the mode. - Special values. Signed zero, infinities, quiet and signaling NaN. A float NaN
keeps its sign and payload on the way in; on the way out a float keeps the top 51
payload bits, and a signaling NaN comes out quiet, as the FPU does. A Decimal NaN
maps its payload to the low 62 bits of the mantissa (a float payload
ptherefore reads back asp << 11), andDecimal("sNaN")becomessNaN1, because a signaling NaN needs a nonzero fraction. - Legacy encodings.
ExtKindalso names the encodings the 80387 and later reject: unnormal, pseudo-infinity and pseudo-NaN. They have no value, soto_fraction()raisesInvalidEncodingErrorandto_float()returns the x87 indefinite NaN, as the FPU does. A pseudo-denormal is accepted with the value of exponent 1;canonical()rewrites it. - Errors. Bad input (wrong type, length, byte order, hex or range) raises
LvextError, which is aValueError.NotFiniteErroris raised when an infinity or NaN is asked for a Fraction. - Limits.
EXT80_MAX,EXT80_MIN_NORMAL,EXT80_MIN_SUBNORMAL,EXT80_EPSILONandEXT80_INDEFINITEareExt80constants.
Command line
$ lvext encode 1
3fff8000000000000000
$ lvext encode 1/3 --rounding toward-zero
3ffdaaaaaaaaaaaaaaaa
$ lvext encode -inf
ffff8000000000000000
$ lvext decode 3ffd8000000000000000
kind: normal
sign: +
exponent: 0x3ffd
mantissa: 0x8000000000000000
exact: 0.25
float: 0.25
float_hex: 3fd0000000000000
float_exact: yes
encode takes a decimal, a fraction such as 1/3, inf, nan, snan or -0,
with --byteorder, --rounding and --width 16 (zero-padded container).
decode takes 20 hex digits, or 32 for a 16-byte container, and --json prints one
JSON object. Errors go to stderr with exit status 1. lvext --help lists examples.
How it is verified
Each golden vector in the contract tests is an exact rational written with
fractions.Fraction. On Windows x86-64 the test suite also runs the FPU's own load
and store instructions (FLD, FSTP, FILD, FDIVP) through a small test-only
ctypes helper and compares lvext with the hardware bit for bit, under all four
rounding modes, over the edge cases and thousands of random encodings. That check
found and now guards three decode bugs in 0.1.0 (see the changelog).
LabVIEW
LabVIEW's EXT type is extended precision, but its flattened form is 16 bytes and NI
does not publish that layout in detail. lvext implements the x87 80-bit format only.
It does not decode the 16-byte flattened EXT, and none of its output has been
verified against LabVIEW. pack_ext128 and unpack_ext128 are a zero-padded
16-byte container (with "little", the layout of a 16-byte long double on x86-64
compilers such as GCC and Clang), not a documented LabVIEW format.
Why
The Python standard library cannot read this format: struct has no 80-bit code.
numpy.longdouble exposes it only where the platform's long double is the x87
type (for example Linux with GCC); on Windows with MSVC it is a 64-bit double. lvext
does the bit math in pure Python with no dependencies, so the same bytes decode the
same way on every platform, and exact conversions to Fraction and Decimal come with
it.
License
MIT. See LICENSE.
Links
- Website: https://lianes8studio.com
- GitHub: https://github.com/JaviLianes8
- LinkedIn: https://www.linkedin.com/in/jlianes/
Metadata
Release files for lvext 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lvext-0.2.0.tar.gz | 28.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lvext-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.3 kB
Release files / lvext-0.2.0.tar.gz
| Download URL | lvext-0.2.0.tar.gz |
|---|---|
| Size | 28.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
594591ab3c108170ef193555910aaa5be6d419c09c42685193e7f4ef3831dd47
|
|
BLAKE2b-256 checksum How to use checksums |
33bf834e7832c55b38b5e8b1cf1037b055304732c2f381491faf4af8c65a59d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.2
|
Release files / lvext-0.2.0-py3-none-any.whl
| Download URL | lvext-0.2.0-py3-none-any.whl |
|---|---|
| Size | 18.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a46c78f91dce8a8cd4f1fcaa7cf3e908c01c293575cc34d3bebf8eb5814ff5e7
|
|
BLAKE2b-256 checksum How to use checksums |
8095d31fb1bb792a752ca429f5203eb702f65d246dc8d90ecb1af8f83111d988
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.2
|