Ответ 1
Почему тип по умолчанию двойной?
Это вопрос, который лучше всего задать разработчикам языка Java. Они - единственные люди, которые знают реальные причины, по которым было принято решение о создании языка. Но я ожидаю, что рассуждение было следующим:
Им нужно было различать два типа литералов, потому что они фактически означают разные значения... с математической точки зрения.
Предположим, что они сделали "поплавок" по умолчанию для литералов, рассмотрим этот пример
// (Hypothetical "java" code ... )
double d = 0.1;
double d2 = 0.1d;
В приведенном выше тексте d
и d2
будут иметь разные значения. В первом случае значение низкой точности float
преобразуется в значение с более высокой точностью double
в точке назначения. Но вы не можете восстановить точность, которой нет.
Я полагаю, что дизайн языка, в котором эти два утверждения являются законными, и означает разные вещи, является идеей BAD... учитывая, что фактический смысл первого утверждения отличается от "естественного" значения.
Выполняя это так, как они это сделали:
double d = 0.1f;
double d2 = 0.1;
являются законными и снова означают разные вещи. Но в первом утверждении намерение программиста ясно, а второе утверждение "естественным" значением является то, что получает программист. И в этом случае:
float f = 0.1f;
float f2 = 0.1; // compilation error!
... компилятор подбирает несоответствие.
Я предполагаю, что использование float - это исключение, а не правило (используя вместо него двойники) с современным оборудованием, поэтому в какой-то момент было бы разумно предположить, что пользователь намеревается 0.1f, когда пишет
float f = 0.1;
Они уже могли это сделать. Но проблема связана с набором правил преобразования типов, которые работают... и достаточно просты, что вам не нужна степень в Java-ology, чтобы реально понять. Наличие 0.1
означает, что разные вещи в разных контекстах будут путать. И подумайте об этом:
void method(float f) { ... }
void method(double d) { ... }
// Which overload is called in the following?
this.method(1.0);
Дизайн языка программирования сложный. Изменение в одной области может иметь последствия для других.
UPDATE, чтобы ответить на некоторые вопросы, поднятые @supercat.
@supercat: Учитывая приведенные выше перегрузки, какой метод будет вызываться для метода (16777217)? Это лучший выбор?
Я неправильно комментировал... ошибку компиляции. На самом деле ответ method(float)
.
JLS говорит об этом:
15.12.2.5. Выбор наиболее конкретного метода
Если более чем один метод-член доступен и применим к необходимо вызвать один из них, чтобы обеспечить дескриптор для отправки времени выполнения. Программирование на Java язык использует правило, в котором выбран наиболее специфический метод.
...
[Символы m1 и m2 обозначают методы, которые применимы.]
[If] m2 не является общим, а m1 и m2 применимы строгими или free invocation и где m1 имеет формальные типы параметров S1,..., Sn и m2 имеет формальные типы параметров T1,..., Tn, тип Si больше специфический, чем Ti для аргумента ei для всех я (1 ≤ я ≤ n, n = k).
...
Вышеуказанные условия являются единственными обстоятельствами, при которых один метод может быть более конкретным, чем другое.
Тип S более специфичен, чем тип T для любого выражения, если S <: T (§4.10).
В этом случае мы сравниваем method(float)
и method(double)
, которые оба применимы к вызову. Поскольку float
<: double
, это более специфично, и поэтому будет выбран method(float)
.
@supercat: Такое поведение может вызвать проблемы, если, например, выражение например,
int2 = (int) Math.Round(int1 * 3.5)
илиlong2 = Math.Round(long1 * 3.5)
заменяется наint1 = (int) Math.Round(int2 * 3)
илиlong2 = Math.Round(long1 * 3)
Изменение будет выглядеть безобидно, но первые два выражения исправить до
613566756
или2573485501354568
, а последние два сбой выше5592405
[последний полностью подделка выше715827882
].
Если вы говорите о человеке, делающем это изменение... ну да.
Однако компилятор не будет делать это за вашей спиной. Например, int1 * 3.5
имеет тип double
(int
преобразуется в double
), поэтому вы вызываете Math.Round(double)
.
Как правило, арифметика Java будет неявно преобразовывать из "меньших" в "более крупные" числовые типы, но не от "большего" до "меньшего".
Однако вам все равно нужно быть осторожным, так как (в примере округления):
-
произведение целого числа и плавающей запятой не может быть представлено с достаточной точностью, поскольку (скажем) a
float
имеет меньшее количество бит точности, чемint
. -
листинг результата
Math.Round(double)
для целочисленного типа может привести к преобразованию в наименьшее/наибольшее значение целочисленного типа.
Но все это иллюстрирует, что арифметическая поддержка на языке программирования сложна, и есть неизбежная информация для нового или неосторожного программиста.