[feat] Expose fractionalBits in PackOptions#80
Open
RiyaanB wants to merge 1 commit intonianticlabs:mainfrom
Open
[feat] Expose fractionalBits in PackOptions#80RiyaanB wants to merge 1 commit intonianticlabs:mainfrom
RiyaanB wants to merge 1 commit intonianticlabs:mainfrom
Conversation
lukeyreyno
reviewed
May 4, 2026
| // Higher values would either silently truncate the encoded fixed-point value | ||
| // (it would no longer fit in int24) or invoke undefined behavior in the | ||
| // (1 << fractionalBits) computation used by both encode and decode. | ||
| constexpr int MAX_FRACTIONAL_BITS = 23; |
Collaborator
There was a problem hiding this comment.
There should probably be a reasonable MIN_FRACTIONAL_BITS to enforce avoiding unrealistically small cases.
Contributor
Author
There was a problem hiding this comment.
Good call! How about MIN_FRACTIONAL_BITS = 4? gives ±524km range / 6cm resolution as the floor — covers any aerial / city-scale use case, rules out N=0...3 which seem nonsensical (sub-half-meter precision over hundreds of km)
e849096 to
792c46e
Compare
Exposes the position fixed-point precision as a configurable PackOption. The format already encodes fractionalBits in NgspFileHeader byte 13 and the decoder reads it; this change is purely encoder-side. Any value the encoder accepts round-trips through every existing decoder. Per-axis representable range is +/- 2^(23 - fractionalBits) and resolution is 2^-fractionalBits: N=4: +/- 524288 m, ~6 cm (MIN_FRACTIONAL_BITS) N=8: +/- 32768 m, ~4 mm N=12: +/- 2048 m, 0.24 mm (default, unchanged) N=16: +/- 128 m, 15 um N=20: +/- 8 m, 0.95 um N=23: +/- 1 m, 0.12 um (MAX_FRACTIONAL_BITS) Default is unchanged (12), so existing callers behave identically. Verified by SHA256: save_spz with default PackOptions on both sample files produces output byte-identical to upstream/main. Out-of-range fractionalBits is rejected at save time with a logged error, matching the existing sh1Bits validation style. The lower bound MIN_FRACTIONAL_BITS=4 was added per review feedback to rule out values that have no plausible Gaussian-splat use case (N=3 has 12.5 cm resolution over a 1048 km range; lower N gets progressively worse). Note: this PR does not protect against picking a fractionalBits value that's too high for the cloud's extent -- positions outside the representable range silently wrap with the existing encoder. That bug predates this PR (any cloud with extent > 2 km already wraps at the hardcoded N=12) and is fixed in a separate PR (nianticlabs#81), which composes cleanly: when both land, encoder rejects out-of-range fractionalBits *and* out-of-range positions for the chosen fractionalBits. Tests (tests/python/test_pack_options_v4.py, 10 cases): - Defaults match historical values; all 48 pre-existing tests pass unchanged with byte-identical output. - For fractional_bits in {4, 8, 12, 16, 20, 23}: byte 13 of the file (the fractionalBits field of NgspFileHeader) echoes the set value, and position round-trip max error is bounded by the quantization step 2^-N. Per-N input extent is sized to fit strictly within the representable range. - fractional_bits below MIN_FRACTIONAL_BITS (3, 0) and above MAX_FRACTIONAL_BITS (24) are all rejected.
792c46e to
a3cea38
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Exposes the position fixed-point precision (
fractionalBits) as a configurablePackOption. The format already encodesfractionalBitsinNgspFileHeaderbyte 13, and the decoder reads it on every load — so this change is purely encoder-side. Any value the encoder accepts round-trips through every existing decoder; no version bump or format change.Per-axis representable range is
±2^(23 − fractionalBits); resolution is2^−fractionalBits:MIN_FRACTIONAL_BITSMAX_FRACTIONAL_BITSDefault is unchanged, so all existing callers behave identically.
Changes
PackOptions.fractionalBits(default12, range[MIN_FRACTIONAL_BITS = 4, MAX_FRACTIONAL_BITS = 23]).sh1Bitsvalidation style. The lower bound rules outN ∈ {0, 1, 2, 3}— values that give decimeter-or-coarser resolution over thousand-kilometre ranges and have no plausible Gaussian-splat use case.packed.fractionalBits = 12inpackGaussianswitho.fractionalBits.PackOptions.fractional_bitsplus module constantsspz.MIN_FRACTIONAL_BITS,spz.MAX_FRACTIONAL_BITS,spz.DEFAULT_FRACTIONAL_BITS.PackOptionssection updated.Test plan
tests/python/test_pack_options_v4.py(10 cases, all passing locally; 58 / 58 in the full Python suite):save_spzoutput with defaultPackOptions()is byte-identical toupstream/mainon both samples (hornedlizard.spz→c5dcc897…2d286,racoonfamily.spz→1a5d4676…767df).fractional_bitsround-trip. Parametrized overn ∈ {4, 8, 12, 16, 20, 23}. Each case asserts:fractionalBitsfield ofNgspFileHeader(byte 13:magic[0..3],version[4..7],numPoints[8..11],shDegree[12],fractionalBits[13]) echoes the set value;≤ 2^−n, with inputextent = min(8, 0.5 × 2^(23−n))so values fit strictly within the representable range — includingn = 23, where extent collapses to 0.5 and range to ±1.fractional_bitsrejection. Parametrized over{MIN_FRACTIONAL_BITS − 1, 0, MAX_FRACTIONAL_BITS + 1}— every out-of-range value causessave_spzto returnfalse.Compatibility & relationship to PR #81
This PR does not protect against picking a
fractionalBitsvalue that's too high for the cloud's extent — positions outside±2^(23−N)silently wrap with the existing encoder. That bug predates this PR (any cloud with extent > 2 km already wraps today at the hardcodedN = 12) and is fixed in a separate PR: #81 — Reject positions that overflow the int24 fixed-point range.The two compose cleanly: with both landed, the encoder rejects out-of-range
fractionalBitsand out-of-range positions for whateverfractionalBitsis in effect. This PR doesn't strictly depend on #81 — it's safe to merge in either order, since #81 also protects existing N=12 callers regardless of whetherfractionalBitsis configurable.The original draft of this PR also exposed
compressionLevel. That part has been dropped — without an official format spec the conservative default is to keep encoder output variance small.fractionalBitsis the part the format already pins via the header field.