Engineering notation in C#?

前端 未结 9 2174
离开以前
离开以前 2020-12-03 17:44

Is there any code out there (or a built-in function) which allows outputting a floating point number in engineering notation?

For example, 1.5e-4 would

相关标签:
9条回答
  • 2020-12-03 18:11

    Rather than subclassing, I'd take advantage of the fact that Double implements IFormattable and write an IFormatProvider that formats the number. Then I'd have code that looks similar to:

    double d = 123.45;
    Console.WriteLine(d.ToString(null, new MyCustomFormat()));
    
    0 讨论(0)
  • 2020-12-03 18:11

    Here is another version that handles negative and without rounding

    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}";
    }
    
    0 讨论(0)
  • 2020-12-03 18:13

    Combining two of the earlier answers and adding a unit (volt, etc.) gives nice tidy answers like 11000 volts as 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);
    }
    
    0 讨论(0)
  • 2020-12-03 18:16

    This is an old thread, but the answer might as well be correct. Issues with the existing code: it doesn't handle NaN, any of the infinities, negative numbers, or very small number (like double.Epsilon). And you can't pass in a precision.

    My code is:

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

    Test cases are below. My personal standard for test cases (sorry, this doesn't follow the latest and best NUnit guidance): the public static Test() takes no parameters and return the number of errors. It normally calls a private static TestOne(args, expected) which calculates the actual value, compared to the expected value, and returns the number of errors.

       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;
        }
    
    0 讨论(0)
  • 2020-12-03 18:19

    To solve this problem, you want to create a class (call it Engineering) which inherits from Float on which you override the ToString() member.

    Edit: Okay, I understand the issue now. Still, the solution is subclassing.

    0 讨论(0)
  • 2020-12-03 18:23

    This may need refactoring:

    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";
        }
    }
    
    0 讨论(0)
提交回复
热议问题