millify 1.1.0

dotnet add package millify --version 1.1.0
                    
NuGet\Install-Package millify -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="millify" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="millify" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="millify" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add millify --version 1.1.0
                    
#r "nuget: millify, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package millify@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=millify&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=millify&version=1.1.0
                    
Install as a Cake Tool

Millify for .NET

Build, Test & Coverage codecov License: MIT NuGet

Millify is a small .NET library that turns large counts and measurements into short, readable labels (2.5K, 1.44M, 1Mi, …). Use it anywhere you would otherwise hand-roll suffix logic: analytics tiles, tables, logs, charts, and mobile or web API payloads.

The NuGet package is named millify. This repository is millify-dotnet; the API lives in the MillifyDotnet namespace.

Conceptually similar to the millify package for JavaScript.

What you get

  • One-call formatting via Millify.Shorten for long, double, decimal, and BigInteger.
  • Decimal (1000) or binary (1024) scaling with sensible default suffixes, including IEC-style units for byte-style values.
  • Split “scale then format” via Millify.Decompose and Millify.FormatScaled (or MillifiedNumber.ToFormattedString) when you need the numeric magnitude for tooltips, charts, tests, or accessibility text.
  • Culture-aware decimal separators (separator only; digit grouping stays off so labels stay compact).
  • Optional Span<char> output via Millify.TryFormat for fixed buffers and reduced allocations.
  • Tunable presentation: precision, “smart” precision by magnitude, trimming of insignificant fractional zeros, custom unit lists, spacing before the suffix, and casing.

Install

Package ID: millify

dotnet add package millify

Package Manager Console:

Install-Package millify

Or add a PackageReference in your project file (pin a version in production apps):

<PackageReference Include="millify" Version="1.1.0" />

Quick start

using MillifyDotnet;

Millify.Shorten(2500);              // 2.5K
Millify.Shorten(1_500_000);         // 1.5M
Millify.Shorten(-5_300_000);        // -5.3M
Millify.Shorten(1234.5m);           // 1.2K (default precision is 1)

Default options trim trailing fractional zeros (for example 1.0M becomes 1M). Set TrimInsignificantZeros = false on MillifyOptions if you want a fixed-width fractional part.

Common uses

Custom suffixes and layout

var options = new MillifyOptions(
    precision: 2,
    lowercase: true,
    spaceBeforeUnit: true,
    units: new[] { "", "k", "m", "b", "t" });

Millify.Shorten(1_440_000, options); // "1.44 m"

Byte-style values (1024 steps, IEC-style defaults)

When ScaleBase is Binary and you do not pass units, suffixes default to IEC-style (Ki, Mi, Gi, …) with appropriate casing.

var bytes = new MillifyOptions(precision: 2, scaleBase: MillifyScaleBase.Binary);
Millify.Shorten(1_048_576, bytes); // 1Mi

Localized decimal separator

Culture affects the decimal separator only (for example 1,2K with fr-FR). Thousands separators are not inserted.

using System.Globalization;
using MillifyDotnet;

var fr = new MillifyOptions(precision: 1, culture: CultureInfo.GetCultureInfo("fr-FR"));
Millify.Shorten(1234.5m, fr); // "1,2K" — comma as decimal separator

Decompose for UI logic

using System.Globalization;
using MillifyDotnet;

var options = new MillifyOptions(precision: 2, culture: CultureInfo.GetCultureInfo("fr-FR"));
var parts = Millify.Decompose(9_990, options);

var label = parts.ToFormattedString(options);   // same formatting rules as Shorten
var same = Millify.FormatScaled(parts, options);
// parts.ScaledValue — signed magnitude after scaling
// parts.UnitIndex   — index into options.Units for the active suffix

TryFormat into a buffer

Span<char> buffer = stackalloc char[32];
if (Millify.TryFormat(1234.5m, buffer, out var written))
{
    var text = buffer[..written].ToString();
}

Overloads exist for decimal, long, double, and BigInteger. On netstandard1.6 and netstandard2.0, the library references System.Memory so Span<char> is available.

Smarter fractions on large magnitudes

Enable SmartPrecision to reduce fractional digits when the scaled value is large (for denser dashboards), while keeping finer detail when the scaled magnitude is small. Behavior is still capped by Precision.

var dense = new MillifyOptions(precision: 2, smartPrecision: true);
Millify.Shorten(9_990, dense);       // 9.99K — full precision while scaled value < 10
Millify.Shorten(125_000_000, dense); // 125M — no fractional digits once scaled value ≥ 100

API at a glance

Member Role
Millify.Shorten(...) Format a number to a single string with suffix.
Millify.Decompose(...) Return MillifiedNumber (scaled value + unit index) without final string formatting.
Millify.FormatScaled(MillifiedNumber, ...) Format a decomposed value with the same rules as Shorten.
MillifiedNumber.ToFormattedString(...) Instance helper equivalent to FormatScaled.
Millify.TryFormat(..., Span<char>, ...) Write the same string as Shorten into a buffer; returns false if the buffer is too small.
MillifyOptions Precision, casing, spacing, custom Units, ScaleBase, trimming, smart precision, Culture.
MillifyScaleBase Decimal (1000) or Binary (1024) steps between tiers.

MillifyOptions reference

Option Type Default Description
Precision int 1 Maximum fractional digits (F precision). Must be >= 1.
Lowercase bool false When true, suffixes are lowercased. When false, decimal single-letter units are uppercased; binary ki-style units become Ki, Mi, etc.
SpaceBeforeUnit bool false Inserts a space between the number and the suffix.
Units string[] See below Suffix per magnitude, index 0 for the unscaled tier. Must be non-empty and must not contain null entries.
ScaleBase MillifyScaleBase Decimal (1000) Binary (1024) for IEC-style scaling.
TrimInsignificantZeros bool true Removes trailing fractional zeros (for example 2.50K2.5K).
SmartPrecision bool false When true, fractional digits are reduced for larger scaled magnitudes: scaled absolute value >= 1000 decimals; [10, 100) → up to 1 (still capped by Precision); below 10 (including values < 1) → full Precision.
Culture CultureInfo? null Supplies the decimal separator only (invariant when null). Digit grouping is disabled so only the separator changes (for example 1,2K in fr-FR).

Constructor parameter order (all have defaults except where noted):
precision, lowercase, spaceBeforeUnit, units, scaleBase, trimInsignificantZeros, smartPrecision, culture.

Default Units

When you do not pass units:

  • ScaleBase.Decimal: { "", "k", "m", "g", "t", "p", "e", "z", "y" } (shown as K, M, G, … when Lowercase is false).
  • ScaleBase.Binary: { "", "ki", "mi", "gi", "ti", "pi", "ei", "zi", "yi" } (shown as Ki, Mi, Gi, … when Lowercase is false).

BigInteger behavior

Shorten and Decompose compute the scaled value as (decimal)abs(n) / (decimal)pow(scaleBase, unitIndex) when that fits in decimal. If the cast overflows, a double ratio is used; if the value is still not representable, an OverflowException is thrown with guidance to add more Units tiers so the value can be scaled further.

Validation

Units is validated when options are constructed and whenever the Units property is assigned: the array must contain at least one entry, and no element may be null.

Target frameworks

The library targets:

  • netstandard1.6
  • netstandard2.0
  • netstandard2.1

netstandard1.6 and netstandard2.0 builds reference System.Memory for Span<char> support in TryFormat.

Tests in this repository target a current .NET SDK (see CI); your app only needs a TFM compatible with the standards above.


Contributing

Issues and pull requests are welcome. The millify package ships this README from the repository root.

Build and test (from the repository root):

cd src
dotnet restore
dotnet build --configuration Release
dotnet test --configuration Release

CI runs the same steps under Ubuntu with the .NET 10 SDK. The solution file is src/millify.slnx.

Layout

  • src/millify — library source (MillifyDotnet namespace)
  • src/millify.Tests — xUnit tests

License

MIT. See LICENSE.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp1.0 was computed.  netcoreapp1.1 was computed.  netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard1.6 is compatible.  netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen30 was computed.  tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 122 4/19/2026
1.0.3 119 2/28/2026
1.0.2 290 6/13/2024
1.0.1 8,952 6/4/2023
1.0.0 311 6/4/2023

- Add optional trimming of insignificant fractional zeros (default on; set TrimInsignificantZeros=false to preserve fixed-width fractions).
- Add SmartPrecision to reduce fractional digits for larger scaled magnitudes while keeping finer detail near 1–10.
- Add MillifyScaleBase (decimal 1000 vs binary 1024) with IEC-style default suffixes for binary mode.
- Add BigInteger Shorten/Decompose with exact decimal scaling when representable, plus a controlled overflow path.
- Add CultureInfo support for the decimal separator only (digit grouping stays disabled).
- Add MillifiedNumber plus Decompose/FormatScaled for split formatting workflows (tooltips, charts, a11y).
- Add TryFormat overloads writing into Span (System.Memory referenced on netstandard1.6 and netstandard2.0).
- Validate Units (non-empty, no null entries) on construction and when the Units property is set.
- Fix incorrect casing logic that previously called ToUpperInvariant on the numeric text when Lowercase was true.