Skip to main content

lvfxp

Encode and decode LabVIEW fixed-point (FXP) numbers and their flattened bytes, exactly, in pure Python.

Install

pip install lvfxp

What is a LabVIEW fixed-point number

A LabVIEW fixed-point number stores a real value as an integer word plus a layout that says how to read it: <signed, word length, integer word length>. The word holds word length bits (1 to 64, two's complement when signed), and the integer word length fixes where the binary point sits. The real value is the word read as an integer times the resolution 2 ** (integer word length - word length). The integer word length may exceed the word length or go negative; lvfxp accepts -4096 to 4096 (its own bound, not LabVIEW's, which keeps exact values a sane size). A general fixed-point layout may instead carry an explicit delta (resolution) and offset, giving offset + word * delta.

Usage

from decimal import Decimal

from lvfxp import FxpSpec, from_fxp, to_fxp

# Signed layout with word length 8 and integer word length 5: resolution 0.125.
word = to_fxp(Decimal("15.875"), 8, 5)  # 127 (0x7F)
value = from_fxp(word, 8, 5)  # Decimal("15.875")

# A spec exposes the resolution and the representable range.
spec = FxpSpec(word_length=8, integer_word_length=5, signed=True)
spec.resolution  # Decimal("0.125")
spec.min_value, spec.max_value  # (Decimal("-16.000"), Decimal("15.875"))

# General fixed-point with an explicit delta and offset.
FxpSpec(8, 8, signed=False, delta=Decimal("0.1"), offset=Decimal("1")).decode(5)  # Decimal("1.5")

Decoding returns a Decimal that is exact at every width, including the 2 ** -64 resolution of a <64, 0> word. Floats are read through their shortest repr, so 0.1 means Decimal("0.1"). Bad input (text that is not a number, NaN, infinity, a decimal exponent beyond +/-10000, a word that does not fit, data that is not bytes) raises FxpError, a ValueError.

Rounding and overflow

Encoding rounds exactly, over rationals, so no mode can double-round near a tie. LabVIEW's three rounding modes are provided as constants:

Constant LabVIEW mode Ties
LV_ROUND_HALF_EVEN (default) Round-Half-Even to the even code
LV_ROUND_HALF_UP Round-Half-Up (asymmetric) toward positive infinity
LV_TRUNCATE Truncate always rounds down (drops low bits)

Any decimal rounding mode is accepted too. Note that decimal.ROUND_HALF_UP rounds ties away from zero, which differs from LabVIEW's Round-Half-Up for negative values.

from decimal import Decimal

from lvfxp import LV_ROUND_HALF_UP, to_fxp

to_fxp(Decimal("-0.0625"), 8, 5, rounding=LV_ROUND_HALF_UP)  # 0, the tie goes up
to_fxp(Decimal("16"), 8, 5, overflow="saturate")  # 127, the largest code
to_fxp(Decimal("16"), 8, 5, overflow="wrap")  # 128 (0x80), the low 8 bits

overflow is "raise" (the default, FxpOverflowError), "saturate" or "wrap"; clamp=True is shorthand for "saturate". LabVIEW's To Fixed-Point saturates by default; lvfxp raises by default so an out-of-range value never passes silently.

Flattened bytes

LabVIEW holds every fixed-point number in a 64-bit format and sign extends it to 64 bits, and it flattens numbers big-endian. pack and unpack read and write that form: 8 bytes, the word in the low bits, sign extended when signed and zero extended when unsigned.

from decimal import Decimal

from lvfxp import FxpSpec, pack_fxp, unpack_fxp

pack_fxp(Decimal("-16"), 8, 5).hex()  # "ffffffffffffff80"
unpack_fxp(bytes.fromhex("00000000000003e5"), 10, 4, signed=False)  # Decimal("15.578125")

spec = FxpSpec(word_length=8, integer_word_length=5)
spec.byte_length  # 8
spec.pack_array([Decimal("1"), Decimal("-1")]).hex()
# "00000002" "0000000000000008" "fffffffffffffff8": a 32-bit count, then the elements

unpack checks that the high bits are the word's extension, so bytes written for a different word length or signedness raise instead of decoding to a wrong value.

What is verified, and what is not

  • Value math: verified. Word length, integer word length, signedness, the scaling 2 ** (iwl - wl), rounding and overflow follow NI's documentation, and a contract test checks every word of each word length up to 8 bits, at several integer word lengths, against independent rational arithmetic.
  • 8-byte layout: verified. NI documents the 64-bit, sign-extended format and big-endian flattening, and its numeric table gives fixed-point 64 bits of storage on disk. A real LabVIEW 2026 capture agrees byte for byte: front-panel datalog records written by the shipped Fixed-Point Cast.vi example store each fixed-point value as 8 big-endian bytes, signed words sign extended and an unsigned <+, 10, 4> word with its top bit set zero extended (tests/fixtures/labview2026). The output of the Flatten To String function itself was not captured; NI Community reports of 8-byte flattened fixed-point strings (see Sources) match the same form.
  • 1-D arrays: derived, not captured. pack_array/unpack_array combine NI's documented array rule (a 32-bit count, then the elements) with the verified 8-byte element.
  • Overflow status: not supported. NI gives 72 bits when a fixed-point value includes an overflow status, but does not document where the status byte sits or how it is encoded, and no capture was possible, so lvfxp refuses 9-byte input instead of guessing.

Compact form (not LabVIEW)

pack_compact/unpack_compact (and pack_fxp_compact/unpack_fxp_compact) store the word in ceil(word_length / 8) big-endian bytes. This is a dense form for your own files or links; LabVIEW does not read or write it. Unpacking it ignores bits above the word.

Command line

Installing the package adds an lvfxp command.

$ lvfxp encode 15.875 --wl 8 --iwl 5
000000000000007f
$ lvfxp encode 16 --wl 8 --iwl 5 --overflow saturate
000000000000007f
$ lvfxp decode ffffffffffffff80 --wl 8 --iwl 5
-16
$ lvfxp decode 7f --wl 8 --iwl 5 --compact
15.875
$ lvfxp decode --word 128 --wl 8 --iwl 5 --unsigned
16
$ lvfxp info --wl 8 --iwl 5
layout: <+/-, 8, 5>
resolution: 0.125
minimum: -16
maximum: 15.875
flattened bytes: 8
compact bytes: 1

encode prints the flattened hex, the integer word with --word, or the compact hex with --compact; it takes --overflow, --clamp and --rounding half-even|half-up|truncate. decode reads flattened hex, compact hex with --compact, or an integer with --word, and prints the exact value in plain notation. Every command takes --wl, --iwl, --unsigned, --delta and --offset; lvfxp --help lists runnable examples. Data errors print to stderr with exit code 1; usage errors exit 2. Put -- before a negative value in scientific notation, as in lvfxp encode --wl 8 --iwl 5 -- -1e0.

Why

Fixed-point is a core LabVIEW datatype, handled natively by LabVIEW's own flatten and type-descriptor machinery (there is no separate OpenG FXP package); that machinery runs inside LabVIEW only. LabVIEW exchanges fixed-point data through TDMS files, flattened strings and network streams, but the Python tools that read those sources do not decode the FXP type: autolv states fixed-point is not supported, and npTDMS lists it among its unsupported types. General fixed-point maths packages such as fxpmath and fixedpoint implement their own arithmetic, not LabVIEW's wire layout. struct and numpy have no type for an arbitrary word length with a shifting or negative integer word length. lvfxp fills that gap with a small, zero-dependency, exact codec.

Sources

License

MIT. See LICENSE.

Metadata

Release files for lvfxp 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lvfxp 0.3.0
File Size Uploaded
lvfxp-0.3.0.tar.gz 26.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lvfxp 0.3.0
File Interpreter ABI Platform
lvfxp-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 43.8 kB

Release files / lvfxp-0.3.0.tar.gz

Download URL lvfxp-0.3.0.tar.gz
Size 26.7 kB
Tags Source
SHA-256 checksum
How to use checksums
055e65a8a42cbae4dba2fe73374024348d41d37b5ca6c1dffe7e83a4cc4721ee
BLAKE2b-256 checksum
How to use checksums
af1f104fd9fbf773f7807c32cbb9cc94fc0c24717a0ba34304526f5e3f41ddfb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Release files / lvfxp-0.3.0-py3-none-any.whl

Download URL lvfxp-0.3.0-py3-none-any.whl
Size 17.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c9522352b2c402a5e9cd6209e63190902000958d251d31d8a3b9703c72f64aa8
BLAKE2b-256 checksum
How to use checksums
1f6ed83246c4795f6b08c25d0cb40ccab622f5629e33a15fef6f14f919709e6c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page