Irregular Number Pattern

ID

java.irregular_number_pattern

Severity

high

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

Java

Tags

code_smell, literals, readability

Description

Reports a numeric literal whose digit-separator underscores split the digits into groups of inconsistent size — 100_0, 100_00, 1_000_00_000.

Uniformity is judged on the digit groups only: every group after the first must be the same width, and the leading group may be shorter because it holds the remainder. Any consistent grouping passes, so pair grouping (1_00_00) and hex nibble grouping (0x7fff_ffff) are accepted alongside the conventional groups of three. A literal with no separator is never reported, and for a floating-point literal only the integral part is graded — the fractional digits group differently and are left alone.

Binary literals (0b/0B) are exempt outright: their bits are commonly grouped to match a field layout rather than a magnitude, so an irregular-looking grouping there is often deliberate.

Test code is out of scope. A test that writes a duration as 3600_000L, or pins a schema version as 2026_01_000, is not a literal anyone reads for its magnitude. Decided by the user-configured applicativeCode.excludedPathPatterns plus the src/it/java convention.

Rationale

The underscore separator exists so a reader can take in a long literal’s magnitude at a glance, the way a thousands separator does in print. That only works while the grouping is regular, because the eye reads the group width once and then counts groups. 1_000_00_000 reads as thousand-grouped and is a factor of ten off from what a quick glance suggests, while 100_0 reads as a hundred with a stray trailing digit.

A literal grouped this way is usually the residue of an edit — a digit added or removed without moving the separators. That is exactly the change the formatting should make visible to the next reviewer, not hide from them.

long shipmentTotal = 10_000_00_000L; // Bad -- groups of 2, 3, 2, 3
long retryBudget = 20000_000_000L;   // Bad -- leading group wider than the rest
long badOffset = 0x1234_567_89ab_cdefL; // Bad -- groups of 4, 3, 4, 4

long shipmentTotal = 10_000_000_000L; // Good -- uniform groups of three
long badOffset = 0x1234_5678_9abcL;   // Good -- uniform hex groups
int statusBits = 0b101_11001_0001;    // Good -- binary literals are exempt

Remediation

Re-group the digits so every group after the first is the same width, then check the value itself: an irregular literal often means a digit was gained or lost during an edit, and simply moving the separators would bless the wrong number without noticing it. Pick the grouping that matches the domain — threes for a decimal quantity, nibbles or bytes for a hex mask — and apply it consistently to every literal it will be compared against.

© 2026 Xygeni. All rights reserved