Преобразование с двойным преобразованием без научной нотации
Как преобразовать double в строковое представление с плавающей точкой без научной нотации в .NET Framework?
"Маленькие" образцы (эффективные числа могут иметь любой размер, например 1.5E200
или 1e-200
):
3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562
Ни один из форматов стандартных форматов не является таким, и custom формат также, похоже, не позволяет иметь открытое число цифр после десятичного разделителя.
Это не дубликат Как преобразовать double в строку без разрешения до 10 (E-05), потому что ответы, приведенные там, не решить проблему. В этом вопросе принято решение использовать фиксированную точку (например, 20 цифр), чего я не хочу. Форматирование с фиксированной точкой и обрезка избыточного 0 не решает проблему либо потому, что максимальная ширина для фиксированной ширины составляет 99 символов.
Примечание. Решение должно корректно обрабатывать форматы пользовательских номеров (например, другой разделитель десятичных знаков, в зависимости от информации о культуре).
Изменить: Вопрос заключается только в том, чтобы вытеснить вышеупомянутые числа. Я знаю, как работают числа с плавающей запятой, и какие числа могут использоваться и вычисляться вместе с ними.
Ответы
Ответ 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);
Ответ 10
Я мог ошибаться, но разве это не так?
data.ToString("n");
http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx
Ответ 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"