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

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

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

 3248971234698200000000000000000000000000000000 0.00000000000000000000000000000000000023897356978234562 

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

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

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

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

Для решения общего назначения необходимо сохранить 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.###################################################################################################################################################################################################################################################################################################################################################"; } 

¹ Обновление: я ошибочно сказал, что это также решение без потерь. На самом деле это не так, поскольку ToString делает свое обычное округление отображения для всех форматов, кроме r . Живой пример. Спасибо, @Loathing! Пожалуйста, см . Ответ Lothing, если вам нужна возможность совершить кругооборот в фиксированной точке (например, если вы используете .ToString("r") сегодня).

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

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

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

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

Обновление : тесты конверсий include только однозначные целые числа, что является нормой, но алгоритм также работает для чего-то вроде: 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(@"^(?-?)(?\d+)(\.(?\d*?)0*)?E(?[+\-]\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)); 

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

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

Проблема с использованием #.###...### или 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 том, что она медленная. Этот код является той же идеей, что и ответ Сасика, но в два раза быстрее. Единичный метод тестирования внизу.

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

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

 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

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

я думаю, вам нужно только использовать 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" 
  • Двойной против BigDecimal?
  • Является ли наиболее значительная десятичная цифра точностью, которая может быть преобразована в двоичную и обратно в десятичную без потери значимости 6 или 7.225?
  • Как выполнить поразрядную операцию с числами с плавающей запятой
  • Как мне сделать сравнение с плавающей запятой?
  • Сколько двойных чисел существует между 0.0 и 1.0?
  • Приведение float в int (побитовое) в C
  • Почему бы не использовать Double или Float для представления валюты?
  • Что означают F и D в конце числовых литералов?
  • Получение фактической ширины элемента с плавающей запятой
  • Почему я не могу использовать значение float в качестве параметра шаблона?
  • Как может примитивное значение float быть -0.0? Что это значит?
  • Interesting Posts

    Прикрепите источник в затмении фляги

    Можно ли установить порядок укладки псевдоэлементов ниже их родительского элемента?

    Как получить дату и время клиента в ASP.NET?

    В чем разница между неограниченным подстановочным типом List И сырым списком типов?

    Outlook 2007 (2003) Правило: «Отметить как прочитанное» «Переместить в определенную папку» Отображение конвертов в системном трее после чтения сообщения

    Фильтрация дублированных / неповторных строк в data.table

    Где живет постоянный пул Java String, куча или стек?

    Эффективный, лаконичный способ найти следующий соответствующий друг?

    постоянные переменные, не работающие в заголовке

    Как я могу вырезать ArrayList из ArrayList в Java?

    Можно ли изменить значок, используемый для идентификации профилей в браузере Google Chrome?

    Несколько объектов в форме Rails

    Как мне получить доступ к SmartCtl в Windows 7?

    XFS и потеря данных при отключении питания

    Шаблоны C ++, неопределенная ссылка

    Давайте будем гением компьютера.