Frage

Wie ein Doppel in eine Floating-Point-String-Darstellung ohne wissenschaftliche Schreibweise in .NET Framework konvertieren?

"Small" Proben (effektive Anzahl von jeder Größe sein können, wie 1.5E200 oder 1e-200):

3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562

Keiner der Standard Zahlenformate wie diese sind, und ein benutzerdefiniertes Format auch scheint nicht eine offene Anzahl von Stellen zu ermöglichen, die nach den dezimal-Trennzeichen.

Dies ist kein Duplikat von Wie doppelt ohne die Kraft zu Zeichenfolge konvertieren zu 10 Darstellung (E-05) , weil die gegebenen Antworten dort tun nicht löst das Problem auf der Hand. Die akzeptierte Lösung in dieser Frage war einen Fixpunkt (zB 20 Stellen) zu verwenden, das nicht das, was ich will. Ein fester Punkt Formatierung und Trimmen der redundanten 0 das Problem nicht lösen, weil entweder die maximale Breite für feste Breite beträgt 99 Zeichen.

. Hinweis: die Lösung zu tun hat, korrekt mit benutzerdefinierten Zahlenformaten (zum Beispiel anderem Dezimaltrennzeichens, je nach Kulturinformationen)

Edit: Die Frage ist eigentlich nur über oben genannte Zahlen displaing. Ich bin mir bewusst, wie Gleitkommazahlen arbeiten und welche Zahlen können mit ihnen verwendet werden und berechnet wird.

War es hilfreich?

Lösung

Für eine allgemeine-purpose¹ Lösung benötigen Sie 339 Orte zu erhalten:

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

Die maximale Anzahl von Nicht-Null-Dezimalziffern ist 16. 15 sind auf der rechten Seite des Dezimalpunkts. Der Exponent kann diese 15 Stellen maximal 324 Stellen nach rechts bewegen. ( Siehe die Reichweite und Präzision. )

Es funktioniert für double.Epsilon , double.MinValue , double.MaxValue , und alles, was dazwischen liegt.

wird die Leistung viel größer als die regex / String-Manipulation Lösungen sein, da alle Formatierungen und String-Arbeit in einem Durchlauf von nicht verwalteten CLR-Code ausgeführt wird. Auch der Code ist viel einfacher als richtig erweisen.

Für eine einfache Bedienung und eine noch bessere Leistung, macht es eine Konstante:

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

¹ Update: Ich sagte fälschlicherweise, dass dies auch eine verlustfreie Lösung. In der Tat ist es nicht, da ToString hat seine normale Anzeige Rundung für alle Formate außer r. Live-Beispiel. Danke, @Abscheu! Bitte finden Sie unter Lothing Antwort , wenn Sie die Möglichkeit, Folgendes zu bieten: in festen Punkt Notation müssen (dh, wenn Sie mit .ToString("r") heute) .

Andere Tipps

Ich hatte ein ähnliches Problem und das ist für mich gearbeitet:

doubleValue.ToString("F99").TrimEnd('0')

F99 zu viel des Guten, aber Sie bekommen die Idee.

Dies ist ein String-Parsing-Lösung, wo die Quelle Nummer (double) in einen String umgewandelt und in ihre Bestandteile analysiert. Es wird dann durch Regeln in voller Länge numerische Darstellung zusammengesetzt. Es erklärt auch locale wie gewünscht geplant.

Aktualisieren : Die Tests der Umsätze nur einstellige ganze Zahlen sind, die die Norm ist, aber der Algorithmus funktioniert auch für so etwas wie: 239483.340901e-20

using System;
using System.Text;
using System.Globalization;
using System.Threading;

public class MyClass
{
    public static void Main()
    {
        Console.WriteLine(ToLongString(1.23e-2));            
        Console.WriteLine(ToLongString(1.234e-5));           // 0.00010234
        Console.WriteLine(ToLongString(1.2345E-10));         // 0.00000001002345
        Console.WriteLine(ToLongString(1.23456E-20));        // 0.00000000000000000100023456
        Console.WriteLine(ToLongString(5E-20));
        Console.WriteLine("");
        Console.WriteLine(ToLongString(1.23E+2));            // 123
        Console.WriteLine(ToLongString(1.234e5));            // 1023400
        Console.WriteLine(ToLongString(1.2345E10));          // 1002345000000
        Console.WriteLine(ToLongString(-7.576E-05));         // -0.00007576
        Console.WriteLine(ToLongString(1.23456e20));
        Console.WriteLine(ToLongString(5e+20));
        Console.WriteLine("");
        Console.WriteLine(ToLongString(9.1093822E-31));        // mass of an electron
        Console.WriteLine(ToLongString(5.9736e24));            // mass of the earth 

        Console.ReadLine();
    }

    private static string ToLongString(double input)
    {
        string strOrig = input.ToString();
        string str = strOrig.ToUpper();

        // if string representation was collapsed from scientific notation, just return it:
        if (!str.Contains("E")) return strOrig;

        bool negativeNumber = false;

        if (str[0] == '-')
        {
            str = str.Remove(0, 1);
            negativeNumber = true;
        }

        string sep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
        char decSeparator = sep.ToCharArray()[0];

        string[] exponentParts = str.Split('E');
        string[] decimalParts = exponentParts[0].Split(decSeparator);

        // fix missing decimal point:
        if (decimalParts.Length==1) decimalParts = new string[]{exponentParts[0],"0"};

        int exponentValue = int.Parse(exponentParts[1]);

        string newNumber = decimalParts[0] + decimalParts[1];

        string result;

        if (exponentValue > 0)
        {
            result = 
                newNumber + 
                GetZeros(exponentValue - decimalParts[1].Length);
        }
        else // negative exponent
        {
            result = 
                "0" + 
                decSeparator + 
                GetZeros(exponentValue + decimalParts[0].Length) + 
                newNumber;

            result = result.TrimEnd('0');
        }

        if (negativeNumber)
            result = "-" + result;

        return result;
    }

    private static string GetZeros(int zeroCount)
    {
        if (zeroCount < 0) 
            zeroCount = Math.Abs(zeroCount);

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < zeroCount; i++) sb.Append("0");    

        return sb.ToString();
    }
}

Sie können die double zu decimal gegossen und dann ToString() tun.

(0.000000005).ToString()   // 5E-09
((decimal)(0.000000005)).ToString()   // 0,000000005

Ich habe nicht Performance-Tests durchgeführt, die schneller ist, Gießen von 64-Bit-double zu 128-Bit-decimal oder ein Format-String von mehr als 300 Zeichen. Oh, und es könnte möglicherweise Überlauf sein, Fehler bei der Konvertierung, aber wenn Sie Ihre Werte ein decimal passen sollte gut funktionieren.

Update: Das Casting scheint viel schneller zu sein. wie in der anderen Antwort gegeben, eine vorbereitete Formatstring verwenden, die Formatierung eine Million Mal dauert 2,3 Sekunden und nur 0,19 Sekunden Gießen. Wiederholbar. Das ist 10x schneller . Jetzt ist es nur über den Wertebereich.

Dies ist, was ich bisher habe, scheint zu funktionieren, aber vielleicht hat jemand eine bessere Lösung:

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

Das Problem #.###...### oder F99 ist, dass es nicht Präzision bei der Endung Dezimalstellen nicht erhalten, z:

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

Das Problem mit DecimalConverter.cs ist, dass es langsam ist. Dieser Code ist die gleiche Idee wie Sasik Antwort, aber doppelt so schnell. Unit-Test-Methode unten.

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

In den alten Tagen, wenn wir unsere eigenen Formatierer schreiben mussten, würden wir die Mantisse und Exponent isolieren und zu formatieren sie getrennt.

In diesem Artikel von Jon Skeet ( https://csharpindepth.com/articles/FloatingPoint ) er stellt eine Verbindung zu seiner DoubleConverter.cs Routine, die genau das tun soll, was Sie wollen. Skeet bezieht sich auch auf diesem unter Extraktions Mantisse und Exponenten von Doppel in C # .

Die obligatorische Logarithm-basierte Lösung. Beachten Sie, dass diese Lösung, weil es Mathematik beinhaltet tun, kann die Genauigkeit der Zahl ein wenig reduzieren. Nicht stark getestet.

private static string DoubleToLongString(double x)
{
    int shift = (int)Math.Log10(x);
    if (Math.Abs(shift) <= 2)
    {
        return x.ToString();
    }

    if (shift < 0)
    {
        double y = x * Math.Pow(10, -shift);
        return "0.".PadRight(-shift + 2, '0') + y.ToString().Substring(2);
    }
    else
    {
        double y = x * Math.Pow(10, 2 - shift);
        return y + "".PadRight(shift - 2, '0');
    }
}

Edit: Wenn der Dezimalpunkt kreuzt nicht Null Teil der Zahl, wird dieser Algorithmus kläglich scheitern. Ich habe versucht, für eine einfache und ging zu weit.

Ich habe gerade auf dem Code improvisierten oben, um es für negative exponentielle Werte zu arbeiten.

using System;
using System.Text.RegularExpressions;
using System.IO;
using System.Text;
using System.Threading;

namespace ConvertNumbersInScientificNotationToPlainNumbers
{
    class Program
    {
        private static string ToLongString(double input)
        {
            string str = input.ToString(System.Globalization.CultureInfo.InvariantCulture);

            // if string representation was collapsed from scientific notation, just return it:
            if (!str.Contains("E")) return str;

            var positive = true;
            if (input < 0)
            {
                positive = false;
            }

            string sep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
            char decSeparator = sep.ToCharArray()[0];

            string[] exponentParts = str.Split('E');
            string[] decimalParts = exponentParts[0].Split(decSeparator);

            // fix missing decimal point:
            if (decimalParts.Length == 1) decimalParts = new string[] { exponentParts[0], "0" };

            int exponentValue = int.Parse(exponentParts[1]);

            string newNumber = decimalParts[0].Replace("-", "").
                Replace("+", "") + decimalParts[1];

            string result;

            if (exponentValue > 0)
            {
                if (positive)
                    result =
                        newNumber +
                        GetZeros(exponentValue - decimalParts[1].Length);
                else

                    result = "-" +
                     newNumber +
                     GetZeros(exponentValue - decimalParts[1].Length);


            }
            else // negative exponent
            {
                if (positive)
                    result =
                        "0" +
                        decSeparator +
                        GetZeros(exponentValue + decimalParts[0].Replace("-", "").
                                   Replace("+", "").Length) + newNumber;
                else
                    result =
                    "-0" +
                    decSeparator +
                    GetZeros(exponentValue + decimalParts[0].Replace("-", "").
                             Replace("+", "").Length) + newNumber;

                result = result.TrimEnd('0');
            }
            float temp = 0.00F;

            if (float.TryParse(result, out temp))
            {
                return result;
            }
            throw new Exception();
        }

        private static string GetZeros(int zeroCount)
        {
            if (zeroCount < 0)
                zeroCount = Math.Abs(zeroCount);

            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < zeroCount; i++) sb.Append("0");

            return sb.ToString();
        }

        public static void Main(string[] args)
        {
            //Get Input Directory.
            Console.WriteLine(@"Enter the Input Directory");
            var readLine = Console.ReadLine();
            if (readLine == null)
            {
                Console.WriteLine(@"Enter the input path properly.");
                return;
            }
            var pathToInputDirectory = readLine.Trim();

            //Get Output Directory.
            Console.WriteLine(@"Enter the Output Directory");
            readLine = Console.ReadLine();
            if (readLine == null)
            {
                Console.WriteLine(@"Enter the output path properly.");
                return;
            }
            var pathToOutputDirectory = readLine.Trim();

            //Get Delimiter.
            Console.WriteLine("Enter the delimiter;");
            var columnDelimiter = (char)Console.Read();

            //Loop over all files in the directory.
            foreach (var inputFileName in Directory.GetFiles(pathToInputDirectory))
            {
                var outputFileWithouthNumbersInScientificNotation = string.Empty;
                Console.WriteLine("Started operation on File : " + inputFileName);

                if (File.Exists(inputFileName))
                {
                    // Read the file
                    using (var file = new StreamReader(inputFileName))
                    {
                        string line;
                        while ((line = file.ReadLine()) != null)
                        {
                            String[] columns = line.Split(columnDelimiter);
                            var duplicateLine = string.Empty;
                            int lengthOfColumns = columns.Length;
                            int counter = 1;
                            foreach (var column in columns)
                            {
                                var columnDuplicate = column;
                                try
                                {
                                    if (Regex.IsMatch(columnDuplicate.Trim(),
                                                      @"^[+-]?[0-9]+(\.[0-9]+)?[E]([+-]?[0-9]+)$",
                                                      RegexOptions.IgnoreCase))
                                    {
                                        Console.WriteLine("Regular expression matched for this :" + column);

                                        columnDuplicate = ToLongString(Double.Parse
                                                                           (column,
                                                                            System.Globalization.NumberStyles.Float));

                                        Console.WriteLine("Converted this no in scientific notation " +
                                                          "" + column + "  to this number " +
                                                          columnDuplicate);
                                    }
                                }
                                catch (Exception)
                                {

                                }
                                duplicateLine = duplicateLine + columnDuplicate;

                                if (counter != lengthOfColumns)
                                {
                                    duplicateLine = duplicateLine + columnDelimiter.ToString();
                                }
                                counter++;
                            }
                            duplicateLine = duplicateLine + Environment.NewLine;
                            outputFileWithouthNumbersInScientificNotation = outputFileWithouthNumbersInScientificNotation + duplicateLine;
                        }

                        file.Close();
                    }

                    var outputFilePathWithoutNumbersInScientificNotation
                        = Path.Combine(pathToOutputDirectory, Path.GetFileName(inputFileName));

                    //Create Directory If it does not exist.
                    if (!Directory.Exists(pathToOutputDirectory))
                        Directory.CreateDirectory(pathToOutputDirectory);

                    using (var outputFile =
                        new StreamWriter(outputFilePathWithoutNumbersInScientificNotation))
                    {
                        outputFile.Write(outputFileWithouthNumbersInScientificNotation);
                        outputFile.Close();
                    }

                    Console.WriteLine("The transformed file is here :" +
                        outputFilePathWithoutNumbersInScientificNotation);
                }
            }
        }
    }
}

Dieser Code nimmt ein Eingangsverzeichnis und auf der Basis der Trennzeichen konvertiert alle Werte in wissenschaftlicher Notation zu numerischem Format.

Danke

versuchen Sie dieses:

public static string DoubleToFullString(double value, 
                                        NumberFormatInfo formatInfo)
{
    string[] valueExpSplit;
    string result, decimalSeparator;
    int indexOfDecimalSeparator, exp;

    valueExpSplit = value.ToString("r", formatInfo)
                         .ToUpper()
                         .Split(new char[] { 'E' });

    if (valueExpSplit.Length > 1)
    {
        result = valueExpSplit[0];
        exp = int.Parse(valueExpSplit[1]);
        decimalSeparator = formatInfo.NumberDecimalSeparator;

        if ((indexOfDecimalSeparator 
             = valueExpSplit[0].IndexOf(decimalSeparator)) > -1)
        {
            exp -= (result.Length - indexOfDecimalSeparator - 1);
            result = result.Replace(decimalSeparator, "");
        }

        if (exp >= 0) result += new string('0', Math.Abs(exp));
        else
        {
            exp = Math.Abs(exp);
            if (exp >= result.Length)
            {
                result = "0." + new string('0', exp - result.Length) 
                             + result;
            }
            else
            {
                result = result.Insert(result.Length - exp, decimalSeparator);
            }
        }
    }
    else result = valueExpSplit[0];

    return result;
}

Millionen von Programmierern weltweit Seine breit, es ist immer eine gute Praxis suchen, um zu versuchen, wenn jemand bereits in Ihr Problem gestoßen hat. Manchmal gibt es Lösungen sind Müll, die es an der Zeit bedeutet, schreiben Sie Ihre eigenen, und manchmal gibt es große, wie die folgenden:

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

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

string strdScaleFactor = dScaleFactor.ToString(); // where dScaleFactor = 3.531467E-05

decimal decimalScaleFactor = Decimal.Parse(strdScaleFactor, System.Globalization.NumberStyles.Float);

Ich könnte falsch sein, aber ist es nicht so?

data.ToString("n");

http://msdn.microsoft.com/en-us/library /dwhawy9k.aspx

Just zu bauen auf, was jcasso sagte, was Sie tun können, ist Ihre doppelten Wert einzustellen, indem die Exponenten zu ändern, so dass Ihr Lieblingsformat es für Sie tun würden, wenden Sie das Format und als Unterlage das Ergebnis mit Nullen zum Ausgleich Einstellung.

Ich glaube, Sie brauchen nur zu Verwendung IFormat mit

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

Beispiel:

double d = double.MaxValue;
string s = d.ToString(d, System.Globalization.NumberStyles.Number);

Meine Lösung, die benutzerdefinierten Formate verwendet hat. versuchen Sie dies:

double d;
d = 1234.12341234;
d.ToString("#########0.#########");

Dies funktioniert gut für mich ...

double number = 1.5E+200;
string s = number.ToString("#");

//Output: "150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top