Double to string conversion without scientific notation

前端 未结 17 1322
無奈伤痛
無奈伤痛 2020-11-22 15:33

How to convert a double into a floating-point string representation without scientific notation in the .NET Framework?

\"Small\" samples (effective numbers may be of

相关标签:
17条回答
  • 2020-11-22 16:01

    This is what I've got so far, seems to work, but maybe someone has a better solution:

    private static readonly Regex rxScientific = new Regex(@"^(?<sign>-?)(?<head>\d+)(\.(?<tail>\d*?)0*)?E(?<exponent>[+\-]\d+)$", RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture|RegexOptions.CultureInvariant);
    
    public static string ToFloatingPointString(double value) {
        return ToFloatingPointString(value, NumberFormatInfo.CurrentInfo);
    }
    
    public static string ToFloatingPointString(double value, NumberFormatInfo formatInfo) {
        string result = value.ToString("r", NumberFormatInfo.InvariantInfo);
        Match match = rxScientific.Match(result);
        if (match.Success) {
            Debug.WriteLine("Found scientific format: {0} => [{1}] [{2}] [{3}] [{4}]", result, match.Groups["sign"], match.Groups["head"], match.Groups["tail"], match.Groups["exponent"]);
            int exponent = int.Parse(match.Groups["exponent"].Value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
            StringBuilder builder = new StringBuilder(result.Length+Math.Abs(exponent));
            builder.Append(match.Groups["sign"].Value);
            if (exponent >= 0) {
                builder.Append(match.Groups["head"].Value);
                string tail = match.Groups["tail"].Value;
                if (exponent < tail.Length) {
                    builder.Append(tail, 0, exponent);
                    builder.Append(formatInfo.NumberDecimalSeparator);
                    builder.Append(tail, exponent, tail.Length-exponent);
                } else {
                    builder.Append(tail);
                    builder.Append('0', exponent-tail.Length);
                }
            } else {
                builder.Append('0');
                builder.Append(formatInfo.NumberDecimalSeparator);
                builder.Append('0', (-exponent)-1);
                builder.Append(match.Groups["head"].Value);
                builder.Append(match.Groups["tail"].Value);
            }
            result = builder.ToString();
        }
        return result;
    }
    
    // test code
    double x = 1.0;
    for (int i = 0; i < 200; i++) {
        x /= 10;
    }
    Console.WriteLine(x);
    Console.WriteLine(ToFloatingPointString(x));
    
    0 讨论(0)
  • 2020-11-22 16:02

    For a general-purpose¹ solution you need to preserve 339 places:

    doubleValue.ToString("0." + new string('#', 339))

    The maximum number of non-zero decimal digits is 16. 15 are on the right side of the decimal point. The exponent can move those 15 digits a maximum of 324 places to the right. (See the range and precision.)

    It works for double.Epsilon, double.MinValue, double.MaxValue, and anything in between.

    The performance will be much greater than the regex/string manipulation solutions since all formatting and string work is done in one pass by unmanaged CLR code. Also, the code is much simpler to prove correct.

    For ease of use and even better performance, make it a constant:

    public static class FormatStrings
    {
        public const string DoubleFixedPoint = "0.###################################################################################################################################################################################################################################################################################################################################################";
    }
    

    ¹ Update: I mistakenly said that this was also a lossless solution. In fact it is not, since ToString does its normal display rounding for all formats except r. Live example. Thanks, @Loathing! Please see Lothing’s answer if you need the ability to roundtrip in fixed point notation (i.e, if you’re using .ToString("r") today).

    0 讨论(0)
  • 2020-11-22 16:02

    The problem using #.###...### or F99 is that it doesn't preserve precision at the ending decimal places, e.g:

    String t1 = (0.0001/7).ToString("0." + new string('#', 339)); // 0.0000142857142857143
    String t2 = (0.0001/7).ToString("r");                         //      1.4285714285714287E-05
    

    The problem with DecimalConverter.cs is that it is slow. This code is the same idea as Sasik's answer, but twice as fast. Unit test method at bottom.

    public static class RoundTrip {
    
        private static String[] zeros = new String[1000];
    
        static RoundTrip() {
            for (int i = 0; i < zeros.Length; i++) {
                zeros[i] = new String('0', i);
            }
        }
    
        private static String ToRoundTrip(double value) {
            String str = value.ToString("r");
            int x = str.IndexOf('E');
            if (x < 0) return str;
    
            int x1 = x + 1;
            String exp = str.Substring(x1, str.Length - x1);
            int e = int.Parse(exp);
    
            String s = null;
            int numDecimals = 0;
            if (value < 0) {
                int len = x - 3;
                if (e >= 0) {
                    if (len > 0) {
                        s = str.Substring(0, 2) + str.Substring(3, len);
                        numDecimals = len;
                    }
                    else
                        s = str.Substring(0, 2);
                }
                else {
                    // remove the leading minus sign
                    if (len > 0) {
                        s = str.Substring(1, 1) + str.Substring(3, len);
                        numDecimals = len;
                    }
                    else
                        s = str.Substring(1, 1);
                }
            }
            else {
                int len = x - 2;
                if (len > 0) {
                    s = str[0] + str.Substring(2, len);
                    numDecimals = len;
                }
                else
                    s = str[0].ToString();
            }
    
            if (e >= 0) {
                e = e - numDecimals;
                String z = (e < zeros.Length ? zeros[e] : new String('0', e));
                s = s + z;
            }
            else {
                e = (-e - 1);
                String z = (e < zeros.Length ? zeros[e] : new String('0', e));
                if (value < 0)
                    s = "-0." + z + s;
                else
                    s = "0." + z + s;
            }
    
            return s;
        }
    
        private static void RoundTripUnitTest() {
            StringBuilder sb33 = new StringBuilder();
            double[] values = new [] { 123450000000000000.0, 1.0 / 7, 10000000000.0/7, 100000000000000000.0/7, 0.001/7, 0.0001/7, 100000000000000000.0, 0.00000000001,
             1.23e-2, 1.234e-5, 1.2345E-10, 1.23456E-20, 5E-20, 1.23E+2, 1.234e5, 1.2345E10, -7.576E-05, 1.23456e20, 5e+20, 9.1093822E-31, 5.9736e24, double.Epsilon };
    
            foreach (int sign in new [] { 1, -1 }) {
                foreach (double val in values) {
                    double val2 = sign * val;
                    String s1 = val2.ToString("r");
                    String s2 = ToRoundTrip(val2);
    
                    double val2_ = double.Parse(s2);
                    double diff = Math.Abs(val2 - val2_);
                    if (diff != 0) {
                        throw new Exception("Value {0} did not pass ToRoundTrip.".Format2(val.ToString("r")));
                    }
                    sb33.AppendLine(s1);
                    sb33.AppendLine(s2);
                    sb33.AppendLine();
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 16:03

    i think you need only to use IFormat with

    ToString(doubleVar, System.Globalization.NumberStyles.Number)
    

    example:

    double d = double.MaxValue;
    string s = d.ToString(d, System.Globalization.NumberStyles.Number);
    
    0 讨论(0)
  • 2020-11-22 16:04

    Being millions of programmers world wide, it's always a good practice to try search if someone has bumped into your problem already. Sometimes there's solutions are garbage, which means it's time to write your own, and sometimes there are great, such as the following:

    http://www.yoda.arachsys.com/csharp/DoubleConverter.cs

    (details: http://www.yoda.arachsys.com/csharp/floatingpoint.html)

    0 讨论(0)
  • 2020-11-22 16:08

    Just to build on what jcasso said what you can do is to adjust your double value by changing the exponent so that your favorite format would do it for you, apply the format, and than pad the result with zeros to compensate for the adjustment.

    0 讨论(0)
提交回复
热议问题