C # двойная до десятичной потери точности - PullRequest
28 голосов
/ 17 сентября 2011

У меня есть двойное значение "138630.78380386264", и я хочу преобразовать его в десятичное число, однако, когда я делаю это, я делаю это либо путем приведения, либо с использованием Convert.ToDecimal(), и я теряю точность.

Что происходит? Это число может содержать как десятичное, так и двойное число:

enter image description here

double doub = double.Parse("138630.78380386264");
decimal dec = decimal.Parse("138630.78380386264");
string decs = dec.ToString("F17");
string doubse =DoubleConverter.ToExactString(doub);
string doubs = doub.ToString("F17");

decimal decC = (decimal) doub;
string doudeccs = decC.ToString("F17");
decimal decConv = Convert.ToDecimal(doub);
string doudecs = decConv.ToString("F17");

Также: как я могу получить ToString() на удвоении, чтобы распечатать тот же результат, что показывает отладчик? например 138630.78380386264

Ответы [ 2 ]

23 голосов
/ 17 сентября 2011

138630.78380386264 не совсем точно представляется с двойной точностью.Ближайшее число двойной точности (как найдено здесь ) равно 138630.783803862635977566242218017578125, что согласуется с вашими выводами.

Вы спрашиваете, почему преобразование в десятичное число не содержит большей точности.В документации для Convert.ToDecimal() есть ответ:

Десятичное значение, возвращаемое этим методом, содержит максимум 15 значащих цифр.Если параметр значения содержит более 15 значащих цифр, он округляется с использованием округления до ближайшего.В следующем примере показано, как метод Convert.ToDecimal (Double) использует округление до ближайшего значения, чтобы получить десятичное значение с 15 значащими цифрами.

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

5 голосов
/ 19 мая 2012

Это неудачно, я думаю. Около 139 000 Decimal имеет гораздо лучшую точность, чем Double. Но, тем не менее, из-за этой проблемы у нас разные Double проецируются на те же самые Decimal. Например

double doub1 = 138630.7838038626;
double doub2 = 138630.7838038628;
Console.WriteLine(doub1 < doub2);                    // true, values differ as doubles
Console.WriteLine((decimal)doub1 < (decimal)doub2);  // false, values projected onto same decimal

На самом деле существует шесть различных представляемых Double значений между doub1 и doub2 выше, поэтому они не совпадают.

Вот несколько глупая работа-aronud:

static decimal PreciseConvert(double doub)
{
  // Handle infinities and NaN-s first (throw exception)
  // Otherwise:
  return Decimal.Parse(doub.ToString("R"), NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint);
}

Строка формата "R" обеспечивает включение достаточного количества дополнительных цифр, чтобы сделать инъекцию сопоставления (в области, где Decimal имеет превосходную точность).


Обратите внимание, что в некотором диапазоне long (Int64) имеет точность, превосходящую точность Double. Таким образом, я проверил, выполняются ли здесь преобразования аналогичным образом (сначала округляя до 15 значащих десятичных разрядов). Они не! Итак:

double doub3 = 1.386307838038626e18;
double doub4 = 1.386307838038628e18;

Console.WriteLine(doub3 < doub4);              // true, values differ as doubles
Console.WriteLine((long)doub3 < (long)doub4);  // true, full precision of double used when converting to long

Кажется непоследовательным использовать другое «правило», когда целью является decimal.

Обратите внимание, что из-за этого (decimal)(long)doub3 дает более точный результат, чем просто (decimal)doub3.

...