Humanizr/Humanizer · error · ArgumentOutOfRangeException

Fixed metric precision must be between 0 and 15.

Error message

Fixed metric precision must be between 0 and 15.

What it means

Thrown by ToMetric when the KeepTrailingZeros flag is set but the decimals argument falls outside 0-15. The trailing-zeros feature appends exactly 'decimals' fractional zeros to the output, so the value must fit a fixed-precision string format. Humanizer caps this at 15 to match the maximum practical precision of a double's decimal representation.

Source

Thrown at src/Humanizer/MetricNumeralExtensions.cs:43

/// <summary>
/// Contains extension methods for changing a number to Metric representation (ToMetric)
/// and from Metric representation back to the number (FromMetric)
/// </summary>
public static class MetricNumeralExtensions
{
    const int Limit = 27;

    static readonly double BigLimit = Math.Pow(10, Limit);
    static readonly double SmallLimit = Math.Pow(10, -Limit);

    static bool ShouldKeepTrailingZeros(MetricNumeralFormats? formats, int? decimals)
    {
        if (!decimals.HasValue || !formats.HasValue || !formats.Value.HasFlag(MetricNumeralFormats.KeepTrailingZeros))
            return false;

        if (decimals is < 0 or > 15)
            throw new ArgumentOutOfRangeException(nameof(decimals), decimals, "Fixed metric precision must be between 0 and 15.");

        return true;
    }

    static string FormatLongWithTrailingZeros(long input, int decimals, NumberFormatInfo nfi) =>
        decimals > 0
            ? input.ToString(nfi) + nfi.NumberDecimalSeparator + new string('0', decimals)
            : input.ToString(nfi);

    /// <summary>
    /// Symbols is a list of every symbols for the Metric system.
    /// </summary>
    static readonly List<char>[] Symbols =
    [
        ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
        ['m', 'μ', 'n', 'p', 'f', 'a', 'z', 'y']
    ];

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Clamp the decimals argument to the range 0-15 before calling ToMetric: var safeDecimals = Math.Clamp(decimals, 0, 15);
  2. If you don't need fixed trailing zeros, omit the KeepTrailingZeros flag or pass decimals: null.
  3. Validate the value with an explicit range check and surface a clear error to your caller before reaching Humanizer.

Example fix

// before
var s = value.ToMetric(MetricNumeralFormats.KeepTrailingZeros, decimals);

// after
var safeDecimals = decimals is >= 0 and <= 15 ? decimals : 0;
var s = value.ToMetric(MetricNumeralFormats.KeepTrailingZeros, safeDecimals);
Defensive patterns

Strategy: validation

Validate before calling

static bool IsValidMetricDecimals(int? decimals, MetricNumeralFormats? formats) =>
    !decimals.HasValue ||
    !formats.GetValueOrDefault().HasFlag(MetricNumeralFormats.KeepTrailingZeros) ||
    decimals is >= 0 and <= 15;

Prevention

When it happens

Trigger: Calling input.ToMetric(MetricNumeralFormats.KeepTrailingZeros, 16) or any value < 0 (e.g. -1). Only fires when both the formats parameter has the KeepTrailingZeros flag AND decimals has a value.

Common situations: Developer copies a precision constant from a UI formatter (e.g. decimal places = 17) into ToMetric. Off-by-one from a variable that can reach -1 before the call. Passing an unvalidated user-supplied decimal-count directly into ToMetric.

Related errors


AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13). Data as JSON: /api/errors/0b1b216e90f1de4a. Report an issue: GitHub.