C#中的工程符号?
是否有任何代码(或内置函数)允许以工程符号输出浮点数?
例如, 1.5e-4
将显示为150µ
,5e-3将显示为5m
。
这可能需要重构:
private static string ToEngineeringNotation(this double d) { double exponent = Math.Log10(Math.Abs(d)); if (Math.Abs(d) >= 1) { switch ((int)Math.Floor(exponent)) { case 0: case 1: case 2: return d.ToString(); case 3: case 4: case 5: return (d / 1e3).ToString() + "k"; case 6: case 7: case 8: return (d / 1e6).ToString() + "M"; case 9: case 10: case 11: return (d / 1e9).ToString() + "G"; case 12: case 13: case 14: return (d / 1e12).ToString() + "T"; case 15: case 16: case 17: return (d / 1e15).ToString() + "P"; case 18: case 19: case 20: return (d / 1e18).ToString() + "E"; case 21: case 22: case 23: return (d / 1e21).ToString() + "Z"; default: return (d / 1e24).ToString() + "Y"; } } else if (Math.Abs(d) > 0) { switch ((int)Math.Floor(exponent)) { case -1: case -2: case -3: return (d * 1e3).ToString() + "m"; case -4: case -5: case -6: return (d * 1e6).ToString() + "μ"; case -7: case -8: case -9: return (d * 1e9).ToString() + "n"; case -10: case -11: case -12: return (d * 1e12).ToString() + "p"; case -13: case -14: case -15: return (d * 1e15).ToString() + "f"; case -16: case -17: case -18: return (d * 1e15).ToString() + "a"; case -19: case -20: case -21: return (d * 1e15).ToString() + "z"; default: return (d * 1e15).ToString() + "y"; } } else { return "0"; } }
这里是一些类似Ruby代码的链接 ,虽然格式为dddem,其中m,指数,总是3的倍数。
音译到C#。 由于我不熟悉格式,我不确定这完全符合您的要求。 例如,0.0015格式为2e-3。 使用案例陈述和UTF-8或其他编码将希腊字母替换为指数是相当简单的。 练习留给读者。
public static class FormatExtensions { public static string ToEngineering( this double value ) { int exp = (int)(Math.Floor( Math.Log10( value ) / 3.0 ) * 3.0); double newValue = value * Math.Pow(10.0,-exp); if (newValue >= 1000.0) { newValue = newValue / 1000.0; exp = exp + 3; } return string.Format( "{0:##0}e{1}", newValue, exp); } }
用法:
Console.WriteLine( ((double)15000).ToEngineering() ); double val = 15000; Console.WriteLine( val.ToEngineering() );
我没有使用子类化,而是利用Double实现IFormattable并编写格式化数字的IFormatProvider这一事实。 然后我的代码看起来类似于:
double d = 123.45; Console.WriteLine(d.ToString(null, new MyCustomFormat()));
结合两个早期的答案并添加一个单位(伏特等)给出了11000伏特等11kV的漂亮整洁答案。
public static string ToEngineering(this double value, string unitName) { int exp = (int)(Math.Floor(Math.Log10(value) / 3.0) * 3.0); double newValue = value * Math.Pow(10.0, -exp); if (newValue >= 1000.0) { newValue = newValue / 1000.0; exp = exp + 3; } var symbol = String.Empty; switch (exp) { case 3: symbol = "k"; break; case 6: symbol = "M"; break; case 9: symbol = "G"; break; case 12: symbol = "T"; break; case -3: symbol = "m"; break; case -6: symbol = "μ"; break; case -9: symbol = "n"; break; case -12: symbol = "p"; break; } return string.Format("{0:##0.000} {1}{2}", newValue, symbol, unitName); }
这是一个老线程,但答案可能也是正确的。 现有代码的问题:它不处理NaN,任何无穷大,负数或非常小的数字(如double.Epsilon)。 你无法传递精确度。
我的代码是:
static string DoubleToEngineering(double value, string displayPrecision) { string Retval; if (double.IsNaN(value) || double.IsInfinity(value) || double.IsNegativeInfinity(value) || double.IsPositiveInfinity(value) || value == 0.0 ) { Retval = String.Format("{0:" + "F" + displayPrecision + "}", value); return Retval; } bool isNeg = value < 0; if (isNeg) value = -value; int exp = (int)(Math.Floor(Math.Log10(value) / 3.0) * 3.0); int powerToRaise = -exp; double newValue = value; // Problem: epsilon is something-324 // The biggest possible number is somethinge306 // You simply can't do a Math.Power (10, 324), it becomes infiniity. if (powerToRaise > 300) { powerToRaise -= 300; newValue = newValue * Math.Pow(10.0, 300); } newValue = newValue * Math.Pow(10.0, powerToRaise); // I don't know when this below is triggered. if (newValue >= 1000.0) { newValue = newValue / 1000.0; exp = exp + 3; } var fmt = "{0:F" + displayPrecision + "}"; Retval = String.Format (fmt, newValue); if (exp != 0) Retval += String.Format("e{0}", exp); if (isNeg) Retval = "-" + Retval; return Retval; }
测试用例如下。 我对测试用例的个人标准(对不起,这不遵循最新和最好的NUnit指导):public static Test()不带参数并返回错误数。 它通常调用私有静态TestOne(args,expected),它计算实际值,与预期值进行比较,并返回错误数。
private static int TestDoubleToEngineeringOne(double value, string expected) { var fakePrecision = "4"; int NError = 0; var actual = DoubleToEngineering(value, fakePrecision); if (actual != expected) { System.Diagnostics.Debug.WriteLine($"ERROR: DoubleToEngineering({value}) expected {expected} actual {actual}"); NError++; } return NError; } public static int TestDoubleToEngineering() { int NError = 0; NError += TestDoubleToEngineeringOne(0, "0.0000"); NError += TestDoubleToEngineeringOne(1, "1.0000"); NError += TestDoubleToEngineeringOne(2, "2.0000"); NError += TestDoubleToEngineeringOne(3, "3.0000"); NError += TestDoubleToEngineeringOne(10, "10.0000"); NError += TestDoubleToEngineeringOne(999, "999.0000"); NError += TestDoubleToEngineeringOne(1000, "1.0000e3"); NError += TestDoubleToEngineeringOne(1.234E21, "1.2340e21"); NError += TestDoubleToEngineeringOne(-1, "-1.0000"); NError += TestDoubleToEngineeringOne(-999, "-999.0000"); NError += TestDoubleToEngineeringOne(-1000, "-1.0000e3"); NError += TestDoubleToEngineeringOne(0.1, "100.0000e-3"); NError += TestDoubleToEngineeringOne(0.02, "20.0000e-3"); NError += TestDoubleToEngineeringOne(0.003, "3.0000e-3"); NError += TestDoubleToEngineeringOne(0.0004, "400.0000e-6"); NError += TestDoubleToEngineeringOne(0.00005, "50.0000e-6"); NError += TestDoubleToEngineeringOne(double.NaN, "NaN"); NError += TestDoubleToEngineeringOne(double.PositiveInfinity, "∞"); NError += TestDoubleToEngineeringOne(double.NegativeInfinity, "-∞"); NError += TestDoubleToEngineeringOne(double.Epsilon, "4.9407e-324"); NError += TestDoubleToEngineeringOne(double.MaxValue, "179.7693e306"); NError += TestDoubleToEngineeringOne(double.MinValue, "-179.7693e306"); return NError; }
这是另一个处理否定而没有舍入的版本
public static string ToEngineering(this double value) { var absValue = Math.Abs(value); var exp = absValue < 0.001 ? 0 : (int)(Math.Floor(Math.Log10(absValue) / 3.0) * 3.0); var newValue = value * Math.Pow(10.0, -exp); return $"{newValue}e{exp}"; }
我来到这里寻找一个可以采用任何数字表示的函数,例如signed / unsigned int,float,double,decimal,numeric string,虽然我发现了一些灵感来自Patrick McDonald的答案,但我找不到我要找的完整答案,所以我编写了自己的解决方案,它采用数字字符串,可以从任何数字类型轻松获得。 我在这个post中分享我的解决方案,供其他人使用,如果他们希望有更通用的东西:
public static string ToEngineeringNotation(string originalString, int? significantFigures = null) { var str = originalString; // remove spaces and negative sign str.Replace(" ", ""); string prefix = ""; if (str[0] == '-') { str = str.Substring(1); prefix = "-"; } // Get the exponent, remove the exponent nomenclature int exponent = 0; int exponentStrIndex = 0; if ((exponentStrIndex = str.IndexOfAny("Ee".ToArray())) >= 0) { string exponentStr = str.Substring(exponentStrIndex + 1); str = str.Substring(0, exponentStrIndex); Int32.TryParse(exponentStr, out exponent); } // remove the decimal point, and adjust the exponent so the decimal point // should go just after the first digit, and trim trailing zeros int currentDecimalPosition = str.IndexOf('.'); if (currentDecimalPosition >= 0) { exponent += currentDecimalPosition-1; str = str.Replace(".", ""); } else { exponent += str.Length - 1; } str = str.TrimEnd('0'); // At this point we should only have digits, just return the original string if we don't if (!str.All(char.IsDigit)) { return originalString; } // Trim leading zeros, the decimal point is effectively moved as it's // just after the first digit, so adjust the exponent int lengthBefore = str.Length; str = str.TrimStart('0'); exponent += str.Length-lengthBefore; // work out how much we need to shift the decimal point to get // engineering notation var decimalShiftRequired = exponent % 3; if (decimalShiftRequired < 0) decimalShiftRequired += 3; // Put the decimal point back in, but move the decimal point right // according to the shift worked out above. if (significantFigures.HasValue && significantFigures.Value < str.Length) { str = str.Substring(0, significantFigures.Value); } if (exponent == 0) { decimalShiftRequired = 0; } str = str.PadRight(1 + decimalShiftRequired, '0'); str = $"{str.Substring(0, 1 + decimalShiftRequired)}.{str.Substring(1 + decimalShiftRequired)}"; exponent -= decimalShiftRequired; // Remove the decimal point if there are no digits after it str = str.TrimEnd('.'); // Create a default suffix consisting of the exponent string suffix = exponent != 0 ? $"E{(exponent < 0 ? "" : "+")}{exponent}" : ""; // Work out which letter to put on the end, if any. If no letter is found, // then the Exponent suffix above will be added without modification switch (exponent) { case 3: suffix = "k"; break; case 6: suffix = "M"; break; case 9: suffix = "G"; break; case 12: suffix = "T"; break; case 15: suffix = "P"; break; case 18: suffix = "E"; break; case 21: suffix = "Z"; break; case 24: suffix = "Y"; break; case -3: suffix = "m"; break; case -6: suffix = "μ"; break; case -9: suffix = "n"; break; case -12: suffix = "p"; break; case -15: suffix = "f"; break; case -18: suffix = "a"; break; case -21: suffix = "z"; break; case -24: suffix = "y"; break; } return $"{prefix}{str}{suffix}"; }
这是上面函数顶部的注释块,我把它放在最后,所以人们不必滚动它来获取代码:
/// /// Converts a numeric string to Engineering Notation /// /// /// class Program /// { /// static void Main(string[] args) /// { /// foreach(var a in SampleNumbers) /// { /// var leftPad = a < 0 ? "" : " "; /// var rightPad = a < 0 ? "--> " : "-> "; /// var original = $"{leftPad}{a.ToString().PadRight(22)}{rightPad}"; /// var engineering = $"{leftPad}{a.ToEngineeringNotation(256).PadRight(22)}{rightPad}"; /// var engineering3Figures = $"{leftPad}{a.ToEngineeringNotation(3)}"; /// Console.WriteLine($"/// {original}{engineering}{engineering3Figures}"); /// } /// Console.ReadLine(); /// } /// /// private static IEnumerable SampleNumbers /// { /// get /// { /// var testValues = new[] /// { /// Double.NaN, /// Double.Epsilon, /// Double.MinValue, /// Double.MaxValue, /// Double.NegativeInfinity, /// Double.PositiveInfinity, /// -300, /// -30, /// -1.1, /// -1, /// -0.1, /// -0.01, /// -0.001, /// -0.0001, /// 0, /// 0.0001, /// 0.001, /// 0.01, /// 0.1, /// 1, /// 1.1, /// 30, /// 300 /// }; /// /// foreach (double a in testValues) /// { /// yield return a; /// } /// for (int i = 28; i >= -28; --i) /// { /// yield return Math.Pow(10, i) * -1.234567890123; /// } /// for (int i = -28; i <= 28; ++i) /// { /// yield return Math.Pow(10, i) * 1.234567890123; /// } /// } /// } /// } /// Gives the following output /// /// NaN -> NaN -> NaN /// 4.94065645841247E-324 -> 4.94065645841247E-324 -> 4.94E-324 /// -1.79769313486232E+308--> -179.769313486232E+306--> -179E+306 /// 1.79769313486232E+308 -> 179.769313486232E+306 -> 179E+306 /// -Infinity --> -Infinity --> -Infinity /// Infinity -> Infinity -> Infinity /// -30000 --> -30k --> -30k /// -3000 --> -3k --> -3k /// -300 --> -300 --> -300 /// -30 --> -30 --> -30 /// -1.1 --> -1.1 --> -1.1 /// -1 --> -1 --> -1 /// -0.1 --> -100m --> -100m /// -0.01 --> -10m --> -10m /// -0.001 --> -1m --> -1m /// -0.0001 --> -100μ --> -100μ /// 0 -> 0 -> 0 /// 0.0001 -> 100μ -> 100μ /// 0.001 -> 1m -> 1m /// 0.01 -> 10m -> 10m /// 0.1 -> 100m -> 100m /// 1 -> 1 -> 1 /// 1.1 -> 1.1 -> 1.1 /// 30 -> 30 -> 30 /// 300 -> 300 -> 300 /// 3000 -> 3k -> 3k /// 30000 -> 30k -> 30k /// -1.234567890123E+28 --> -12.34567890123E+27 --> -12.3E+27 /// -1.234567890123E+27 --> -1.234567890123E+27 --> -1.23E+27 /// -1.234567890123E+26 --> -123.4567890123Y --> -123Y /// -1.234567890123E+25 --> -12.34567890123Y --> -12.3Y /// -1.234567890123E+24 --> -1.234567890123Y --> -1.23Y /// -1.234567890123E+23 --> -123.4567890123Z --> -123Z /// -1.234567890123E+22 --> -12.34567890123Z --> -12.3Z /// -1.234567890123E+21 --> -1.234567890123Z --> -1.23Z /// -1.234567890123E+20 --> -123.4567890123E --> -123E /// -1.234567890123E+19 --> -12.34567890123E --> -12.3E /// -1.234567890123E+18 --> -1.234567890123E --> -1.23E /// -1.234567890123E+17 --> -123.4567890123P --> -123P /// -1.234567890123E+16 --> -12.34567890123P --> -12.3P /// -1.234567890123E+15 --> -1.234567890123P --> -1.23P /// -123456789012300 --> -123.4567890123T --> -123T /// -12345678901230 --> -12.34567890123T --> -12.3T /// -1234567890123 --> -1.234567890123T --> -1.23T /// -123456789012.3 --> -123.4567890123G --> -123G /// -12345678901.23 --> -12.34567890123G --> -12.3G /// -1234567890.123 --> -1.234567890123G --> -1.23G /// -123456789.0123 --> -123.4567890123M --> -123M /// -12345678.90123 --> -12.34567890123M --> -12.3M /// -1234567.890123 --> -1.234567890123M --> -1.23M /// -123456.7890123 --> -123.4567890123k --> -123k /// -12345.67890123 --> -12.34567890123k --> -12.3k /// -1234.567890123 --> -1.234567890123k --> -1.23k /// -123.4567890123 --> -123.4567890123 --> -123 /// -12.34567890123 --> -12.34567890123 --> -12.3 /// -1.234567890123 --> -1.234567890123 --> -1.23 /// -0.1234567890123 --> -123.4567890123m --> -123m /// -0.01234567890123 --> -12.34567890123m --> -12.3m /// -0.001234567890123 --> -1.234567890123m --> -1.23m /// -0.0001234567890123 --> -123.4567890123μ --> -123μ /// -1.234567890123E-05 --> -12.34567890123μ --> -12.3μ /// -1.234567890123E-06 --> -1.234567890123μ --> -1.23μ /// -1.234567890123E-07 --> -123.4567890123n --> -123n /// -1.234567890123E-08 --> -12.34567890123n --> -12.3n /// -1.234567890123E-09 --> -1.234567890123n --> -1.23n /// -1.234567890123E-10 --> -123.4567890123p --> -123p /// -1.234567890123E-11 --> -12.34567890123p --> -12.3p /// -1.234567890123E-12 --> -1.234567890123p --> -1.23p /// -1.234567890123E-13 --> -123.4567890123f --> -123f /// -1.234567890123E-14 --> -12.34567890123f --> -12.3f /// -1.234567890123E-15 --> -1.234567890123f --> -1.23f /// -1.234567890123E-16 --> -123.4567890123a --> -123a /// -1.234567890123E-17 --> -12.34567890123a --> -12.3a /// -1.234567890123E-18 --> -1.234567890123a --> -1.23a /// -1.234567890123E-19 --> -123.4567890123z --> -123z /// -1.234567890123E-20 --> -12.34567890123z --> -12.3z /// -1.234567890123E-21 --> -1.234567890123z --> -1.23z /// -1.234567890123E-22 --> -123.4567890123y --> -123y /// -1.234567890123E-23 --> -12.34567890123y --> -12.3y /// -1.234567890123E-24 --> -1.234567890123y --> -1.23y /// -1.234567890123E-25 --> -123.4567890123E-27 --> -123E-27 /// -1.234567890123E-26 --> -12.34567890123E-27 --> -12.3E-27 /// -1.234567890123E-27 --> -1.234567890123E-27 --> -1.23E-27 /// -1.234567890123E-28 --> -123.4567890123E-30 --> -123E-30 /// 1.234567890123E-28 -> 123.4567890123E-30 -> 123E-30 /// 1.234567890123E-27 -> 1.234567890123E-27 -> 1.23E-27 /// 1.234567890123E-26 -> 12.34567890123E-27 -> 12.3E-27 /// 1.234567890123E-25 -> 123.4567890123E-27 -> 123E-27 /// 1.234567890123E-24 -> 1.234567890123y -> 1.23y /// 1.234567890123E-23 -> 12.34567890123y -> 12.3y /// 1.234567890123E-22 -> 123.4567890123y -> 123y /// 1.234567890123E-21 -> 1.234567890123z -> 1.23z /// 1.234567890123E-20 -> 12.34567890123z -> 12.3z /// 1.234567890123E-19 -> 123.4567890123z -> 123z /// 1.234567890123E-18 -> 1.234567890123a -> 1.23a /// 1.234567890123E-17 -> 12.34567890123a -> 12.3a /// 1.234567890123E-16 -> 123.4567890123a -> 123a /// 1.234567890123E-15 -> 1.234567890123f -> 1.23f /// 1.234567890123E-14 -> 12.34567890123f -> 12.3f /// 1.234567890123E-13 -> 123.4567890123f -> 123f /// 1.234567890123E-12 -> 1.234567890123p -> 1.23p /// 1.234567890123E-11 -> 12.34567890123p -> 12.3p /// 1.234567890123E-10 -> 123.4567890123p -> 123p /// 1.234567890123E-09 -> 1.234567890123n -> 1.23n /// 1.234567890123E-08 -> 12.34567890123n -> 12.3n /// 1.234567890123E-07 -> 123.4567890123n -> 123n /// 1.234567890123E-06 -> 1.234567890123μ -> 1.23μ /// 1.234567890123E-05 -> 12.34567890123μ -> 12.3μ /// 0.0001234567890123 -> 123.4567890123μ -> 123μ /// 0.001234567890123 -> 1.234567890123m -> 1.23m /// 0.01234567890123 -> 12.34567890123m -> 12.3m /// 0.1234567890123 -> 123.4567890123m -> 123m /// 1.234567890123 -> 1.234567890123 -> 1.23 /// 12.34567890123 -> 12.34567890123 -> 12.3 /// 123.4567890123 -> 123.4567890123 -> 123 /// 1234.567890123 -> 1.234567890123k -> 1.23k /// 12345.67890123 -> 12.34567890123k -> 12.3k /// 123456.7890123 -> 123.4567890123k -> 123k /// 1234567.890123 -> 1.234567890123M -> 1.23M /// 12345678.90123 -> 12.34567890123M -> 12.3M /// 123456789.0123 -> 123.4567890123M -> 123M /// 1234567890.123 -> 1.234567890123G -> 1.23G /// 12345678901.23 -> 12.34567890123G -> 12.3G /// 123456789012.3 -> 123.4567890123G -> 123G /// 1234567890123 -> 1.234567890123T -> 1.23T /// 12345678901230 -> 12.34567890123T -> 12.3T /// 123456789012300 -> 123.4567890123T -> 123T /// 1.234567890123E+15 -> 1.234567890123P -> 1.23P /// 1.234567890123E+16 -> 12.34567890123P -> 12.3P /// 1.234567890123E+17 -> 123.4567890123P -> 123P /// 1.234567890123E+18 -> 1.234567890123E -> 1.23E /// 1.234567890123E+19 -> 12.34567890123E -> 12.3E /// 1.234567890123E+20 -> 123.4567890123E -> 123E /// 1.234567890123E+21 -> 1.234567890123Z -> 1.23Z /// 1.234567890123E+22 -> 12.34567890123Z -> 12.3Z /// 1.234567890123E+23 -> 123.4567890123Z -> 123Z /// 1.234567890123E+24 -> 1.234567890123Y -> 1.23Y /// 1.234567890123E+25 -> 12.34567890123Y -> 12.3Y /// 1.234567890123E+26 -> 123.4567890123Y -> 123Y /// 1.234567890123E+27 -> 1.234567890123E+27 -> 1.23E+27 /// 1.234567890123E+28 -> 12.34567890123E+27 -> 12.3E+27 /// /// the double to convert /// The number of significant figures /// A string
要解决此问题,您需要创建一个类(称为Engineering),该类inheritance自您重写ToString()成员的Float。
编辑:好的,我现在明白了这个问题。 不过,解决方案是子类化。