Подтвердить что ты не робот

Преобразование с двойным преобразованием без научной нотации

Как преобразовать double в строковое представление с плавающей точкой без научной нотации в .NET Framework?

"Маленькие" образцы (эффективные числа могут иметь любой размер, например 1.5E200 или 1e-200):

3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562

Ни один из форматов стандартных форматов не является таким, и custom формат также, похоже, не позволяет иметь открытое число цифр после десятичного разделителя.

Это не дубликат Как преобразовать double в строку без разрешения до 10 (E-05), потому что ответы, приведенные там, не решить проблему. В этом вопросе принято решение использовать фиксированную точку (например, 20 цифр), чего я не хочу. Форматирование с фиксированной точкой и обрезка избыточного 0 не решает проблему либо потому, что максимальная ширина для фиксированной ширины составляет 99 символов.

Примечание. Решение должно корректно обрабатывать форматы пользовательских номеров (например, другой разделитель десятичных знаков, в зависимости от информации о культуре).

Изменить: Вопрос заключается только в том, чтобы вытеснить вышеупомянутые числа. Я знаю, как работают числа с плавающей запятой, и какие числа могут использоваться и вычисляться вместе с ними.

4b9b3361

Ответ 1

Для решения без потерь, универсального решения вам необходимо сохранить 339 мест:

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

Максимальное число ненулевых десятичных цифр составляет 16. 15 находятся в правой части десятичной точки. Экспонент может переместить эти 15 цифр максимум на 324 места справа. (См. диапазон и точность.)

Он работает для double.Epsilon, double.MinValue, double.MaxValue, и все, что находится между ними.

Производительность будет намного больше, чем решения для обработки регулярных выражений/строк, поскольку все форматирование и строчная работа выполняется за один проход неуправляемым кодом CLR. Кроме того, код намного проще доказать правильность.

Для удобства использования и даже повышения производительности сделайте его постоянным:

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

Ответ 2

У меня была аналогичная проблема, и это сработало для меня:

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

F99 может быть излишним, но вы получаете идею.

Ответ 3

Это решение для разбора строк, где исходный номер (double) преобразуется в строку и анализируется на его составные компоненты. Затем он снова собирается правилами в полноразмерное числовое представление. Он также учитывает локаль в соответствии с запросом.

Обновление. Тесты конверсий включают только однозначные целые числа, что является нормой, но алгоритм также работает для чего-то вроде: 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();
    }
}

Ответ 4

Это то, что у меня до сих пор, похоже, работает, но, возможно, у кого-то есть лучшее решение:

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

Ответ 5

Вы можете наложить 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 раз быстрее. Теперь это только о диапазоне значений.

Ответ 6

В старые времена, когда нам приходилось писать наши собственные форматирующие элементы, мы изолировали мантиссы и экспонента и отформатировали их отдельно.

В этой статье Джона Скита (http://www.yoda.arachsys.com/csharp/floatingpoint.html) он предоставляет ссылку на свою процедуру DoubleConverter.cs, которая должна делать именно то, что вы хотеть. Скит также ссылается на это при извлечении мантиссы и экспоненте из double в С#.

Ответ 7

Обязательное решение на основе логарифма. Обратите внимание, что это решение, потому что оно включает в себя математику, может немного уменьшить точность вашего номера. Не тестировалось сильно.

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

Изменить: если десятичная точка пересекает ненулевую часть числа, этот алгоритм терпит неудачу. Я пробовал просто и зашел слишком далеко.

Ответ 8

Я только что импровизировал на приведенный выше код, чтобы заставить его работать для отрицательных экспоненциальных значений.

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

Этот код принимает входной каталог и на основе разделителя преобразует все значения в научной нотации в числовой формат.

Спасибо

Ответ 9

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

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

Ответ 11

Просто чтобы понять, что jcasso говорит, что вы можете сделать, это настроить двойное значение, изменив экспоненту так, чтобы ваш любимый формат сделал это для вас, применил формат и сравнил результат с нулями, чтобы компенсировать регулировка.

Ответ 12

попробуйте следующее:

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

Ответ 13

Будучи миллионом программистов по всему миру, всегда полезно попробовать, если кто-то уже столкнулся с вашей проблемой. Иногда есть решения - это мусор, а это значит, что настало время написать свои собственные, а иногда и отличные, например:

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

(подробнее: http://www.yoda.arachsys.com/csharp/floatingpoint.html)

Ответ 14

Думаю, вам нужно использовать только IFormat с

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

Пример:

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

Ответ 15

В моем решении использовались пользовательские форматы. попробуйте следующее:

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

Ответ 16

Это отлично работает для меня...

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

//Output: "150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"