I needed to display numbers in an engineering application. The customer wanted the numbers formatted nicely, with about four significant digits and
engineering notation. .NET has
very flexible number formatting. However, it lacks standard formatting into engineering notation.
The extension point for formatting numeric strings is through the
IFormatProvider and
ICustomFormatter interfaces. I implemented a format provider for the engineering notation. However, exclusively using engineering notation was awkward in our application. As an alternative to exclusively using engineering notation, I decided on the following format, which mixes four significant digits, engineering notation, and thousand separators:
3.1416E-05 31.42E-06
0.00031416 314.2E-06
0.0031416 0.003142
0.031416 0.03142
0.31416 0.3142
3.1416 3.142
31.416 31.42
314.16 314.2
3141.6 3,142
31416 31,416
3.1416E+05 314,159
3.1416E+06 3.142E+06
3.1416E+07 31.42E+06
3.1416E+08 314.2E+06
3.1416E+09 3.142E+09
In case you want similar formatting, here is the class that implements it:
public class EngineeringFormatProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
return (formatType == typeof(ICustomFormatter)) ? this : null;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
// for doubles, store the value of the double
var val = Double.NaN;
if (arg is Double)
val = (double)arg;
// for other types, try to convert to a double
else
{
var typeConverter = TypeDescriptor.GetConverter(arg);
if (typeConverter.CanConvertTo(typeof(Double)))
{
try
{
val = (double)typeConverter.ConvertTo(arg, typeof(Double));
}
catch
{
// ignore
}
}
// if cannot convert, return a default value
if(Double.IsNaN(val))
return arg == null ? String.Empty : arg.ToString();
}
// for special cases, just write out the string
if (val == 0.0 || Double.IsNaN(val) || Double.IsInfinity(val))
return val.ToString();
else
{
// calculate the exponents, as a power of 3
var exp = Math.Log10(Math.Abs(val));
var exp3 = (int)(Math.Floor(exp / 3.0) * 3.0);
// calculate the coefficient
var coef = val / Math.Pow(10, exp3);
// special case, for example 0.3142
if(exp3 == -3 && Math.Abs(coef / 1000.0) < 1 && Math.Abs(coef / 1000.0) > 0.1)
return String.Format("{0:G4}", val);
// for "small" numbers
if(exp3 <= -6)
return String.Format("{0:G4}E{1}{2:00}", coef, exp3 > 0 ? "+" : "", exp3);
// for "large" numbers
if(exp >= 6)
return String.Format("{0:G4}E{1}{2:00}", coef, exp3 > 0 ? "+" : "", exp3);
// for numbers needing thousand separators
if (exp >= 3)
return String.Format("{0:N0}", val);
// default
return String.Format("{0:G4}", val);
}
}
}
Here is how to use it:
var p = new EngineeringFormatProvider();
var s = String.Format(p, "{0}", number);