Irregular Number Pattern
ID |
csharp.irregular_number_pattern |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
CSharp |
Tags |
code_smell, literals, readability |
Description
Reports a numeric literal whose digit separators 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 have 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 myriad grouping (1_0000_0000) are accepted
alongside the conventional groups of three. A literal with no separator is never reported, and for
a real literal only the integral part is grouped — the digits after the point group from the left,
so the same test does not apply to them.
Rationale
The digit separator exists so a reader can take in the magnitude of a long literal at a glance,
the way a thousands separator does in print. That works because the eye reads the group width once
and then counts groups. Irregular groups break the mechanism and actively mislead:
1_000_00_000 looks thousand-grouped and is off by a factor of ten from a quick reading, while
100_0 reads as a hundred with a stray digit after it.
A literal grouped this way is usually the residue of an edit — a digit added or removed without moving the separators. That is precisely the change formatting should make visible to the next reviewer instead of concealing.
public class Limits
{
public const int Wrong = 100_0; // FLAW - reads as 100, is 1000
public const int Shifted = 1_000_00_000; // FLAW - groups of 3, 2 and 3
public const long Mixed = 1_0000_000L; // FLAW - a group of 4 then a group of 3
public const int Thousands = 10_000; // OK - leading group holds the remainder
public const int Pairs = 1_00_00; // OK - consistent pair grouping
public const int Millions = 1_000_000; // OK
public const int Plain = 1000000; // OK - no separators to judge
public const int Mask = 0xFF_FF_FF; // OK - consistent byte grouping
}
Remediation
Re-group the digits so that every group after the first has the same width, then check the value itself: an irregular literal often means a digit was gained or lost during an edit, and moving the separators would silently bless the wrong number. Pick the grouping that matches the domain — threes for a decimal quantity, pairs or fours for a bit mask — and use it consistently across the literals it will be compared against.