Подтвердить что ты не робот

С# с двойной точностью до десятичной точности

У меня есть двойной "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() on double распечатать тот же результат, что и отладчик? например 138630.78380386264?

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Это, к сожалению, неудачно. Около 139 000, a 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 выше, поэтому они не совпадают.

Вот несколько глупый work-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 имеет превосходную точность).


Обратите внимание, что в некотором диапазоне a 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.