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);