Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions dali/python/nvidia/dali/experimental/dynamic/_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,16 @@ def __init__(
self.type_id = type_id

if kind == DType.Kind.float:
self.exponent_bits = exponent_bits or DType._default_exponent_bits(bits, True)
self.significand_bits = significand_bits or DType._default_significand_bits(bits, True)
if exponent_bits is None:
exponent_bits = DType._default_exponent_bits(bits, True)

@mzient mzient Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
exponent_bits = DType._default_exponent_bits(bits, True)
exponent_bits = DType._default_exponent_bits(bits, True)
elif exponent_bits < 1:
raise ValueError(f"Invalid number of exponent bits: {exponent_bits}")

elif exponent_bits < 1:
raise ValueError(f"Invalid number of exponent bits: {exponent_bits}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Width errors omit the limit

[Style] The new errors for exponent_bits and significand_bits show the rejected value but do not tell users that the minimum is 1. They also lack final periods. The repository requires errors to name the parameter, show expected versus actual values, and end as complete sentences.

Use messages such as Invalid exponent_bits: got {exponent_bits}, expected at least 1. for both checks, and update the test expectations.

Rule Used: Error messages must name the affected parameter/operator, show expected vs. actual values, and end as a complete sentence. Not "is 5" but "got 5 channels, expected 1 or 3.". Use make_string(...) for C++ concatenation; f-strings in Python. (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

if significand_bits is None:
significand_bits = DType._default_significand_bits(bits, True)

@mzient mzient Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
significand_bits = DType._default_significand_bits(bits, True)
significand_bits = DType._default_significand_bits(bits, True)
elif significand_bits < 1:
raise ValueError(f"Invalid number of significand bits: {significand_bits}")

elif significand_bits < 1:
raise ValueError(f"Invalid number of significand bits: {significand_bits}")
self.exponent_bits = exponent_bits
self.significand_bits = significand_bits
else:
self.exponent_bits = None
self.significand_bits = None
Expand Down Expand Up @@ -278,7 +286,17 @@ def parse_internal(name: str) -> "DType":

t = parse_internal(name)
if t.type_id is None:
t.type_id = _type2id[t]
try:
t.type_id = _type2id[t]
except KeyError:
# __init__ caches the temporary type before this lookup. Drop that
# entry so a later parse does not return the rejected type. Leave a
# name that already pointed at a different type alone.
if _name2type.get(t.name) is t:
del _name2type[t.name]
Comment on lines +295 to +296

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Rejected names break bfloat16

[Bug] A failed dtype("f16e8m7") now breaks later dtype("bfloat16") calls. The temporary DType gets the name bfloat16 and overwrites its existing entry before this check runs. Because bfloat16 has no type_id, the lookup fails and this cleanup deletes its name. Later calls raise ValueError instead of returning ndd.bfloat16.

Save and restore any prior entry, or avoid caching temporary types until the lookup succeeds.

if name != t.name and _name2type.get(name) is t:
del _name2type[name]
raise ValueError(f"{name} is not a registered type.") from None
if t.type_id is not None:
t = _id2type[t.type_id] # use the same DType instance as the one registered with the id
_name2type[name] = t
Expand Down
19 changes: 19 additions & 0 deletions dali/test/python/experimental_mode/test_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,22 @@ def test_type_significand_bits():
assert ndd.float16.significand_bits == 10
assert ndd.float32.significand_bits == 23
assert ndd.float64.significand_bits == 52


def test_explicit_zero_float_field_is_not_replaced_with_the_default():
# A written 0 used to be treated as missing, so these came back as float32 and float16.
cases = {
"f32e8m0": "Invalid number of significand bits: 0",
"f16e0m10": "Invalid number of exponent bits: 0",
}
for name, message in cases.items():
try:
parsed = ndd.dtype(name)
except ValueError as err:
if message not in str(err):
raise AssertionError(f"{name} raised {err!r}, want {message}") from err
continue
raise AssertionError(
f"{name} was accepted as {parsed.name} "
f"(exponent {parsed.exponent_bits}, significand {parsed.significand_bits})"
)