diff --git a/src/Humanizer.Tests.Shared/MetricNumeralTests.cs b/src/Humanizer.Tests.Shared/MetricNumeralTests.cs index bd4021077..c0410eebd 100644 --- a/src/Humanizer.Tests.Shared/MetricNumeralTests.cs +++ b/src/Humanizer.Tests.Shared/MetricNumeralTests.cs @@ -88,18 +88,25 @@ public void TestAllSymbolsAsInt(int exponent) } [Theory] - [InlineData("0", 0d, false, true)] - [InlineData("123", 123d, false, true)] - [InlineData("-123", (-123d), false, true)] - [InlineData("1.23k", 1230d, false, true)] - [InlineData("1 k", 1000d, true, true)] - [InlineData("1 kilo", 1000d, true, false)] - [InlineData("1milli", 1E-3, false, false)] - public void ToMetric(string expected, double input, bool hasSpace, bool useSymbol) + [InlineData("0", 0d, false, true, null)] + [InlineData("123", 123d, false, true, null)] + [InlineData("-123", (-123d), false, true, null)] + [InlineData("1.23k", 1230d, false, true, null)] + [InlineData("1 k", 1000d, true, true, null)] + [InlineData("1 kilo", 1000d, true, false, null)] + [InlineData("1milli", 1E-3, false, false, null)] + [InlineData("1milli", 1E-3, false, false, null)] + [InlineData("1.23milli", 1.234E-3, false, false, 2)] + [InlineData("12.34k", 12345, false, true, 2)] + [InlineData("12k", 12345, false, true, 0)] + [InlineData("-3.9m", -3.91e-3, false, true, 1)] + public void ToMetric(string expected, double input, bool hasSpace, bool useSymbol, int? decimals) { - Assert.Equal(expected, input.ToMetric(hasSpace, useSymbol)); + Assert.Equal(expected, input.ToMetric(hasSpace, useSymbol, decimals)); } + + [Theory] [InlineData(1E+27)] [InlineData(1E-27)] diff --git a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt index e2427e5c4..dc1b8f379 100644 --- a/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt +++ b/src/Humanizer.Tests/ApiApprover/PublicApiApprovalTest.approve_public_api.approved.txt @@ -388,8 +388,8 @@ namespace Humanizer public class static MetricNumeralExtensions { public static double FromMetric(this string input) { } - public static string ToMetric(this int input, bool hasSpace = False, bool useSymbol = True) { } - public static string ToMetric(this double input, bool hasSpace = False, bool useSymbol = True) { } + public static string ToMetric(this int input, bool hasSpace = False, bool useSymbol = True, System.Nullable decimals = null) { } + public static string ToMetric(this double input, bool hasSpace = False, bool useSymbol = True, System.Nullable decimals = null) { } } public class NoMatchFoundException : System.Exception { diff --git a/src/Humanizer/MetricNumeralExtensions.cs b/src/Humanizer/MetricNumeralExtensions.cs index ecfb1012f..e973165c3 100644 --- a/src/Humanizer/MetricNumeralExtensions.cs +++ b/src/Humanizer/MetricNumeralExtensions.cs @@ -90,27 +90,28 @@ public static double FromMetric(this string input) return BuildNumber(input, input[input.Length - 1]); } - /// - /// Converts a number into a valid and Human-readable Metric representation. - /// - /// - /// Inspired by a snippet from Thom Smith. - /// See this link for more. - /// - /// Number to convert to a Metric representation. - /// True will split the number and the symbol with a whitespace. - /// True will use symbol instead of name - /// - /// - /// 1000.ToMetric() => "1k" - /// 123.ToMetric() => "123" - /// 1E-1.ToMetric() => "100m" - /// - /// - /// A valid Metric representation - public static string ToMetric(this int input, bool hasSpace = false, bool useSymbol = true) + /// + /// Converts a number into a valid and Human-readable Metric representation. + /// + /// + /// Inspired by a snippet from Thom Smith. + /// See this link for more. + /// + /// Number to convert to a Metric representation. + /// True will split the number and the symbol with a whitespace. + /// True will use symbol instead of name + /// If not null it is the numbers of decimals to round the number to + /// + /// + /// 1000.ToMetric() => "1k" + /// 123.ToMetric() => "123" + /// 1E-1.ToMetric() => "100m" + /// + /// + /// A valid Metric representation + public static string ToMetric(this int input, bool hasSpace = false, bool useSymbol = true, int? decimals = null) { - return ((double)input).ToMetric(hasSpace, useSymbol); + return ((double)input).ToMetric(hasSpace, useSymbol, decimals); } /// @@ -123,6 +124,7 @@ public static string ToMetric(this int input, bool hasSpace = false, bool useSym /// Number to convert to a Metric representation. /// True will split the number and the symbol with a whitespace. /// True will use symbol instead of name + /// If not null it is the numbers of decimals to round the number to /// /// /// 1000d.ToMetric() => "1k" @@ -131,14 +133,14 @@ public static string ToMetric(this int input, bool hasSpace = false, bool useSym /// /// /// A valid Metric representation - public static string ToMetric(this double input, bool hasSpace = false, bool useSymbol = true) + public static string ToMetric(this double input, bool hasSpace = false, bool useSymbol = true, int? decimals = null) { if (input.Equals(0)) return input.ToString(); if (input.IsOutOfRange()) throw new ArgumentOutOfRangeException(nameof(input)); - return BuildRepresentation(input, hasSpace, useSymbol); + return BuildRepresentation(input, hasSpace, useSymbol, decimals); } /// @@ -199,19 +201,20 @@ private static string ReplaceNameBySymbol(string input) current.Replace(name.Value, name.Key.ToString())); } - /// - /// Build a Metric representation of the number. - /// - /// Number to convert to a Metric representation. - /// True will split the number and the symbol with a whitespace. - /// True will use symbol instead of name - /// A number in a Metric representation - private static string BuildRepresentation(double input, bool hasSpace, bool useSymbol) + /// + /// Build a Metric representation of the number. + /// + /// Number to convert to a Metric representation. + /// True will split the number and the symbol with a whitespace. + /// True will use symbol instead of name + /// If not null it is the numbers of decimals to round the number to + /// A number in a Metric representation + private static string BuildRepresentation(double input, bool hasSpace, bool useSymbol, int? decimals) { var exponent = (int)Math.Floor(Math.Log10(Math.Abs(input)) / 3); - return exponent.Equals(0) + return exponent.Equals(0) ? input.ToString() - : BuildMetricRepresentation(input, exponent, hasSpace, useSymbol); + : BuildMetricRepresentation(input, exponent, hasSpace, useSymbol, decimals); } /// @@ -221,10 +224,13 @@ private static string BuildRepresentation(double input, bool hasSpace, bool useS /// Exponent of the number in a scientific notation /// True will split the number and the symbol with a whitespace. /// True will use symbol instead of name + /// If not null it is the numbers of decimals to round the number to /// A number in a Metric representation - private static string BuildMetricRepresentation(double input, int exponent, bool hasSpace, bool useSymbol) + private static string BuildMetricRepresentation(double input, int exponent, bool hasSpace, bool useSymbol, int? decimals) { var number = input * Math.Pow(1000, -exponent); + if (decimals.HasValue) + number = Math.Round(number, decimals.Value); var symbol = Math.Sign(exponent) == 1 ? Symbols[0][exponent - 1] : Symbols[1][-exponent - 1];