Program Tip

과학적 표기법없이 Double에서 문자열로 변환

programtip 2020. 11. 8. 10:57
반응형

과학적 표기법없이 Double에서 문자열로 변환


.NET Framework에서 과학적 표기법없이 double을 부동 소수점 문자열 표현으로 변환하는 방법은 무엇입니까?

"작은"샘플 (유효 숫자는 1.5E200또는 등의 크기가 될 수 있음 1e-200) :

3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562

의 없음 표준 숫자 형식은 다음과 같이 없습니다 및 사용자 지정 형식은 또한 소수 구분 자리수의 오픈 수를 가지고 허용하지 않는 것 같습니다.

주어진 대답이 당면한 문제를 해결 하지 하기 때문에 이것은 10의 거듭 제곱 표현 (E-05)없이 double을 문자열로 변환하는 방법 의 중복이 아닙니다 . 이 질문에서 허용되는 해결책은 내가 원하는 것이 아닌 고정 소수점 (예 : 20 자리)을 사용하는 것입니다. 고정 소수점 서식을 지정하고 중복 0을 트리밍해도 고정 너비의 최대 너비가 99 자이기 때문에 문제가 해결되지 않습니다.

참고 : 솔루션은 사용자 지정 숫자 형식 (예 : 문화 정보에 따라 다른 소수 구분 기호)을 올바르게 처리해야합니다.

편집 : 질문은 실제로 앞서 언급 한 숫자를 대체하는 것에 관한 것입니다. 나는 부동 소수점 숫자가 어떻게 작동하는지, 그리고 어떤 숫자를 사용하고 계산할 수 있는지 알고 있습니다.


범용 ¹ 솔루션의 경우 339 개의 장소를 보존해야합니다.

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

0이 아닌 십진수의 최대 수는 16입니다. 15는 소수점 오른쪽에 있습니다. 지수는이 15 자리 숫자를 오른쪽으로 최대 324 자리까지 이동할 수 있습니다. ( 범위와 정밀도를 참조하십시오. )

그것은 작동 double.Epsilon, double.MinValue, double.MaxValue그 사이에, 아무것도.

모든 서식 및 문자열 작업이 관리되지 않는 CLR 코드에 의해 한 번에 수행되므로 성능은 정규식 / 문자열 조작 솔루션보다 훨씬 큽니다. 또한 코드가 정확하다는 것을 증명하는 것이 훨씬 간단합니다.

사용 편의성과 더 나은 성능을 위해 일정하게 만드십시오.

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

¹ 업데이트 : 나는 이것이 또한 무손실 솔루션이라고 실수로 말했습니다. 때문에 사실이 아니다 ToString제외한 모든 포맷의 일반 표시 라운딩을한다 r. 라이브 예. 감사합니다, @Loathing! 고정 소수점 표기법으로 왕복하는 기능이 필요하면 Lothing의 답변을 참조하십시오 (즉, .ToString("r")오늘 사용하는 경우 ).


비슷한 문제가 있었고 이것은 나를 위해 일했습니다.

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

F99는 과잉 일 수 있지만 아이디어를 얻습니다.


이것은 소스 번호 (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();
    }
}

당신은 캐스팅 수 doubledecimal다음을 수행 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));

#.###...###or를 사용하는 문제 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();
            }
        }
    }
}

우리가 직접 포맷터를 작성해야했던 옛날에는 가수와 지수를 분리하여 따로 포맷했습니다.

Jon Skeet ( https://csharpindepth.com/articles/FloatingPoint ) 의이 기사에서 그는 원하는 작업을 정확히 수행해야하는 DoubleConverter.cs 루틴에 대한 링크를 제공합니다. Skeet은 또한 c #에서 double에서 가수와 지수를 추출 할 때 이것을 참조합니다 .


필수 로그 기반 솔루션입니다. 이 솔루션은 수학을 포함하므로 숫자의 정확도를 약간 떨어 뜨릴 수 있습니다. 심하게 테스트되지 않았습니다.

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

편집 : 소수점이 숫자의 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

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

I could be wrong, but isn't it like this?

data.ToString("n");

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


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.


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

My solution was using the custom formats. try this:

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

This works fine for me...

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

//Output: "150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

참고URL : https://stackoverflow.com/questions/1546113/double-to-string-conversion-without-scientific-notation

반응형