Почему компилятор решает, что 2.3 является двойным, а не десятичным? - PullRequest
13 голосов
/ 07 декабря 2011

Почему компилятор решает, что 2.3 является двойным, поэтому этот код не будет компилироваться:

decimal x;
x = 2.3; // Compilation error - can not convert double to decimal.
x = (decimal) 2.3 // O.k.

Почему компилятор так не думает:
Он хочет получить десятичную дробь, ондайте мне значение, которое может быть десятичным, поэтому оно десятичное!

И почему это не приводит к ошибке компиляции:

short x;
x = 23; // O.K.

Кто сказал, что 23 не является целым числом?

Ответы [ 4 ]

21 голосов
/ 07 декабря 2011

Здесь много вопросов.Давайте разберем их на маленькие вопросы.

Почему литерал 2.3 типа double, а не десятичный?

Исторические причины.C # разработан, чтобы быть членом семейства языков "C-like syntax", так что его внешний вид и основные идиомы знакомы программистам, которые используют C-подобные языки.Почти во всех этих языках литералы с плавающей запятой обрабатываются как двоичные , а не десятичные с плавающей запятой, потому что именно так C это делал изначально.

Разрабатывал ли я новый язык изцарапина, которую я, вероятно, сделал бы неоднозначными литералами незаконными;каждый литерал с плавающей точкой должен быть однозначно двойным, одинарным или десятичным и т. д. и т. д.

Почему вообще запрещается неявное преобразование между двойным и десятичным?

Потому что это, вероятно, ошибка в двух отношениях.

Во-первых, двойные и десятичные числа имеют разные диапазоны и различную величину «ошибки представления», то есть насколько отличается фактически представленная величина от точной математической величины, которую вы хотите представить.Преобразование двойного в десятичное или наоборот опасно, и вы должны быть уверены, что делаете это правильно;когда вы произносите заклинание, обращаете внимание на тот факт, что вы потенциально теряете точность или величину.

Во-вторых, двойные и десятичные числа имеют очень разные применения.Двойные числа обычно используются для научных расчетов, где разница между 1.000000000001 и 0.99999999999 намного меньше, чем ошибка эксперимента.Накапливание небольших ошибок представления не имеет значения.Десятичные дроби обычно используются для точных финансовых расчетов, которые должны быть с точностью до копейки.Смешивание двух случайно кажется опасным.

Бывают моменты, когда вам приходится это делать;например, легче решить «экспоненциальные» проблемы, такие как амортизация ипотеки или сложное начисление процентов в двойном размере.В этих случаях мы снова заставляем вас указать, что вы конвертируете из двойного в десятичное, чтобы было очень ясно, что это та точка в программе, где могут произойти потери точности или величины, если вы не поняли это правильно.

Почему незаконно преобразовывать двойной литерал в десятичный литерал?Почему бы просто не притвориться, что это был десятичный литерал?

C # не является языком типа "спрятать свои ошибки для себя".Это язык типа «расскажу вам о своих ошибках, чтобы вы могли их исправить».Если вы хотели сказать «2.3m» и забыли «m», то компилятор должен сообщить вам об этом.

Тогда почему допустимо преобразовать целочисленный литерал (или любую целочисленную константу)?к короткому, байтовому и т. д.?

Поскольку целочисленную константу можно проверить, чтобы убедиться, что она находится в правильном диапазоне во время компиляции.И преобразование из целого числа в диапазоне в меньший целочисленный тип всегда является точным;он никогда не теряет точность или величину, в отличие от двойных / десятичных преобразований.Кроме того, арифметика с целочисленной константой всегда выполняется в «проверенном» контексте, если вы не переопределите это с непроверенным блоком, так что даже не возникает опасность переполнения.

И менее вероятно, что целочисленная / короткая арифметика пересекает границу «домена», как двойная / десятичная арифметика.Двойная арифметика может быть научной, а десятичная арифметика - финансовой.Но целочисленная и краткая арифметика не всегда четко привязаны к разным бизнес-доменам.

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

Следовательно, нет веских оснований для того, чтобы сделать его незаконным, и веских оснований для того, чтобы сделать его законным.

11 голосов
/ 07 декабря 2011

Здесь происходит несколько вещей:

  • В первом примере вы пытаетесь неявно преобразовать литерал double в float.Это не сработает.
  • Предположительно рабочая строка фактически пытается выполнить явное преобразование double в decimal (что разрешено, но, как правило, не очень хорошая идея), а затем неявное преобразование.от decimal до float (что не разрешено).Если x должен быть объявлен как decimal, то единственное требуемое преобразование - от double до decimal - что обычно не очень хорошая идея.
  • рабочее преобразование целочисленного литерала происходит из-за "неявного преобразования константного выражения", как указано в разделе 6.1.9 спецификации C # 4:

    A константа-выражение изтип int может быть преобразован в тип sbyte, byte, short, ushort, uint или ulong, при условии, что значение константа-выражения находится в пределахдиапазон типа назначения.

    Есть что-то похожее для long, но не для double.

В основном,когда вы пишете константу с плавающей точкой, рекомендуется явно указать тип с суффиксом:

double d = 2.3d;
float f = 2.3f;
decimal m = 2.3m;
2 голосов
/ 07 декабря 2011

2,3 - double.Это правила языка;любой числовой литерал с десятичной точкой в ​​нем является double, если только он не имеет суффикса F (float) или суффикса M (decimal):

x = 2.3F; // fine

компилятор также предупреждает меня об этом:

Литерал типа double не может быть неявно преобразован в тип 'float';используйте суффикс 'F' для создания литерала этого типа

1 голос
/ 07 декабря 2011

Поскольку числа с плавающей точкой всегда немного сложны в вычислениях и в Valuerange, они в непосредственном обозначении всегда самый большой возможный тип. (в вашем случае: Double).

Non-Floating Points имеют некоторую аналогичную обработку ниже, поэтому они могут быть преобразованы без проблем. Если ваше значение превышает диапазон значений переменной, это может привести к ошибке (например, 257 для байта).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...