문제

.NET 프레임 워크에서 과학적 표기법없이 두 배를 플로팅 포인트 문자열 표현으로 변환하는 방법은 무엇입니까?

"작은"샘플 (유효 숫자는 모든 크기 일 수 있습니다. 1.5E200 또는 1e-200) :

3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562

아무것도 표준 번호 형식 이와 같고 a 맞춤형 형식 또한 소수점 분리기 후에는 열린 수의 숫자를 허용하지 않는 것 같습니다.

이것은 복제본이 아닙니다 전원없이 두 배를 문자열로 변환하는 방법 10 표현으로 (E-05) 주어진 대답이 있기 때문입니다 ~ 아니다 당면한 문제를 해결하십시오. 이 질문에서 허용되는 솔루션은 고정 점 (예 : 20 자리)을 사용하는 것이 었습니다. 고정 포인트 포맷 및 중복 0을 트리밍하는 것은 고정 너비의 최대 너비가 99 자이기 때문에 문제를 해결하지 못합니다.

메모: 솔루션은 사용자 정의 번호 형식 (예 : 배양 정보에 따라 다른 소수점 분리기)을 올바르게 처리해야합니다.

편집하다: 문제는 실제로 위에서 언급 한 숫자를 표시하는 것입니다. 플로팅 포인트 수가 어떻게 작동하고 어떤 숫자를 사용하고 계산할 수 있는지 알고 있습니다.

도움이 되었습니까?

해결책

일반 목적 ¹ 솔루션의 경우 339 개의 장소를 보존해야합니다.

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

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

그것은 작동합니다 double.Epsilon, double.MinValue, double.MaxValue, 그리고 그 사이에있는 모든 것.

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

사용 편의성과 더 나은 성능을 위해 일정하게 만듭니다.

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

¹ 업데이트: 나는 이것이 무한한 솔루션이라고 잘못 말했다. 실제로 그것은 그 이후로는 아닙니다 ToString 제외한 모든 형식에 대해 정상적인 디스플레이 라운드를 수행합니다 r. 라이브 예. 감사합니다, @loathing! 참조하십시오 lothing의 대답 고정점 표기법으로 라운드 트립을 할 수있는 기능이 필요한 경우 (예 : 사용하는 경우 .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();
            }
        }
    }
}

우리가 우리 자신의 포피터를 써야했던 옛날에, 우리는 Mantissa와 지수를 분리하여 별도로 포맷 할 것입니다.

Jon Skeet 의이 기사에서https://csharpindepth.com/articles/floatingpoint) 그는 자신의 DoubleConverter.cs 루틴에 대한 링크를 제공하여 원하는 것을 정확하게 수행해야합니다. Skeet은 또한 이것을 말합니다 C#에서 두 배에서 Mantissa 및 지수 추출.

의무적 인 로그 기반 솔루션. 이 솔루션은 수학을 수행하기 때문에 숫자의 정확도를 조금 줄일 수 있습니다. 심하게 테스트되지 않았습니다.

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

(세부: 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/dwawy9k.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