سؤال

كيفية تحويل مزدوج إلى تمثيل سلسلة الفاصلة العائمة بدون تدوين علمي في .NET Framework؟

العينات "الصغيرة" (قد تكون الأرقام الفعالة بأي حجم، مثل 1.5E200 أو 1e-200) :

3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562

لا شيء من تنسيقات الأرقام القياسية هم مثل هذا، و تنسيق مخصص لا يبدو أيضًا أنه يسمح بوجود عدد مفتوح من الأرقام بعد الفاصل العشري.

هذه ليست نسخة مكررة كيفية تحويل المزدوج إلى سلسلة بدون تمثيل القدرة إلى 10 (E-05) لأن الإجابات المقدمة هناك تفعل ذلك لا حل المشكلة المطروحة.كان الحل المقبول في هذا السؤال هو استخدام نقطة ثابتة (مثل 20 رقمًا)، وهذا ليس ما أريده.لا يؤدي تنسيق النقطة الثابتة وتقليص الرقم 0 الزائد إلى حل المشكلة أيضًا لأن الحد الأقصى للعرض الثابت هو 99 حرفًا.

ملحوظة: يجب أن يتعامل الحل بشكل صحيح مع تنسيقات الأرقام المخصصة (على سبيل المثالفاصل عشري آخر، اعتمادًا على معلومات الثقافة).

يحرر: السؤال في الحقيقة يتعلق فقط بإظهار الأرقام المذكورة أعلاه.أنا على دراية بكيفية عمل أرقام الفاصلة العائمة وما هي الأرقام التي يمكن استخدامها وحسابها.

هل كانت مفيدة؟

المحلول

للحصول على حل للأغراض العامة¹، تحتاج إلى الاحتفاظ بـ 339 مكانًا:

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

الحد الأقصى لعدد الأرقام العشرية غير الصفرية هو 16.15 على الجانب الأيمن من العلامة العشرية.يمكن للأس نقل تلك الأرقام الخمسة عشر بحد أقصى 324 مكانًا إلى اليمين.(انظر النطاق والدقة.)

يعمل من أجل double.Epsilon, double.MinValue, double.MaxValue, ، وأي شيء بينهما.

سيكون الأداء أكبر بكثير من حلول معالجة regex/السلسلة نظرًا لأن جميع أعمال التنسيق والسلسلة تتم في مسار واحد بواسطة رمز CLR غير مُدار.كما أن إثبات صحة الكود أسهل كثيرًا.

لسهولة الاستخدام والأداء الأفضل، اجعله ثابتًا:

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

¹ تحديث: لقد قلت خطأً أن هذا كان أيضًا حلاً بلا خسارة.في الواقع ليس كذلك، منذ ذلك الحين ToString يقوم بتقريب العرض العادي لجميع التنسيقات باستثناء r. مثال حي. شكرا @الكراهية!لطفا أنظر إجابة لوتينغ إذا كنت بحاجة إلى القدرة على السفر ذهابًا وإيابًا في تدوين النقطة الثابتة (على سبيل المثال، إذا كنت تستخدم .ToString("r") اليوم).

نصائح أخرى

واجهت مشكلة مماثلة وهذا نجح معي:

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

قد يكون F99 مبالغًا فيه، لكنك فهمت الفكرة.

هذا هو حل تحليل السلسلة حيث يتم تحويل رقم المصدر (مزدوج) إلى سلسلة ويتم تحليله إلى المكونات المكونة له.ثم يتم إعادة تجميعها بواسطة القواعد في تمثيل رقمي كامل الطول.كما أنه يمثل اللغة حسب الطلب.

تحديث:تتضمن اختبارات التحويلات فقط أرقامًا صحيحة مكونة من رقم واحد، وهو المعيار، ولكن الخوارزمية تعمل أيضًا مع شيء مثل: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();
    }
}

هل يمكن أن يلقي double ل decimal ومن ثم القيام به ToString().

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

لم أقم بإجراء اختبار الأداء وهو أسرع، من 64 بت double إلى 128 بت decimal أو سلسلة تنسيق تتكون من أكثر من 300 حرف.أوه، ومن المحتمل أن تكون هناك أخطاء زائدة أثناء التحويل، ولكن إذا كانت قيمك مناسبة decimal هذا يجب أن يعمل بشكل جيد.

تحديث: يبدو أن عملية الصب أسرع كثيرًا.باستخدام سلسلة تنسيق معدة كما هو موضح في الإجابة الأخرى، يستغرق التنسيق مليون مرة 2.3 ثانية والإرسال 0.19 ثانية فقط.قابل للتكرار.هذا أسرع بـ 10 مرات.الآن يتعلق الأمر فقط بنطاق القيمة.

هذا ما لدي حتى الآن، ويبدو أنه يعمل، ولكن ربما يكون لدى شخص ما حل أفضل:

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

المشكلة باستخدام #.###...### أو F99 هو أنها لا تحافظ على الدقة في نهاية المنازل العشرية، على سبيل المثال:

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

المشكلة مع DecimalConverter.cs هو أنه بطيء.هذا الرمز هو نفس فكرة إجابة Sasik، ولكن أسرع مرتين.طريقة اختبار الوحدة في الأسفل.

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

في الأيام الخوالي عندما كان علينا كتابة المنسقات الخاصة بنا، كنا نعزل الجزء العشري والأس وننسقهما بشكل منفصل.

في هذا المقال بقلم جون سكيت (https://csharpin Deep.com/articles/FloatingPoint) فهو يوفر رابطًا إلى روتين DoubleConverter.cs الخاص به والذي من المفترض أن يفعل ما تريده بالضبط.يشير السكيت أيضًا إلى هذا في استخراج الجزء العشري والأس من مزدوج في C#.

الحل الإلزامي القائم على اللوغاريتم.لاحظ أن هذا الحل، نظرًا لأنه يتضمن إجراء العمليات الحسابية، قد يقلل من دقة رقمك قليلاً.لم يتم اختباره بشكل كبير.

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

يحرر: إذا تجاوزت العلامة العشرية جزءًا غير الصفر من الرقم، فستفشل هذه الخوارزمية فشلاً ذريعًا.لقد حاولت ببساطة وذهبت بعيدا جدا.

لقد ارتجلت للتو الكود أعلاه لجعله يعمل مع القيم الأسية السالبة.

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

يأخذ هذا الرمز دليل إدخال ويقوم بتحويل جميع القيم بالترميز العلمي إلى تنسيق رقمي بناءً على المحدد.

شكرًا

جرب هذه:

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

نظرًا لكوننا ملايين المبرمجين في جميع أنحاء العالم، فمن الممارسات الجيدة دائمًا محاولة البحث عما إذا كان شخص ما قد اصطدم بمشكلتك بالفعل.في بعض الأحيان تكون هناك حلول تافهة، مما يعني أن الوقت قد حان لتكتب حلولك الخاصة، وفي بعض الأحيان تكون هناك حلول رائعة، مثل ما يلي:

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

(تفاصيل: 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);

يمكن أن أكون مخطئا، ولكن أليس الأمر هكذا؟

data.ToString("n");

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

فقط للبناء على ما قاله jcasso، ما يمكنك فعله هو ضبط القيمة المزدوجة عن طريق تغيير الأس بحيث يقوم التنسيق المفضل لديك بذلك نيابةً عنك، وتطبيق التنسيق، ثم إضافة الأصفار إلى النتيجة للتعويض عن التعديل.

أعتقد أنك تحتاج فقط إلى استخدام IFormat مع

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

مثال:

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

كان الحل الخاص بي هو استخدام التنسيقات المخصصة.جرب هذا:

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

هذا يفي بالغرض بالنسبة لي...

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

//Output: "150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top