Понимание интегрального деления

У меня есть следующий фрагмент кода:

public static final long KILOMETER_IN_METERS = 1000;

public static int getKilometers(int distanceInMeter) {
    return (int) Math.floor(distanceInMeter / KILOMETER_IN_METERS);
}

А для строки с оператором return Сонар говорит:

Integral division result cast to double or float

Вот полное описание этой ошибки.


Насколько я понимаю, там говорится о делении int/int, результатом которого может быть значение с плавающей запятой, но я могу ошибаться.
Как это сделать? могу ли я правильно обработать этот случай?


person devger    schedule 07.10.2014    source источник


Ответы (4)


Здесь нет необходимости использовать Math.floor. Math.floor — это операция над double, и она ничего не делает, когда вы указываете ей int.

Returns the largest (closest to positive infinity)
double value that is less than or equal to the argument and is equal to a mathematical integer. Special cases:

  • If the argument value is already equal to a mathematical integer, then the result is the same as the argument.
  • If the argument is NaN or an infinity or positive zero or negative zero, then the result is the same as the argument.

(Источник)

Целочисленное деление уже имеет то же логическое поведение, что и Math.floor, то есть усекает оставшуюся часть операции деления. int/int всегда возвращает int.

Например, 100/3 вернет 33, а 40/50 вернет 0.

Просто используйте этот код:

public static int getKilometers(int distanceInMeter) {
    return distanceInMeter / KILOMETER_IN_METERS;
}
person Eran    schedule 07.10.2014
comment
Ха, вы расширили свой ответ, пока я расширял ваш ответ. :) - person KathyA.; 07.10.2014
comment
Спасибо, Эран, но у меня есть константа типа long и в результате return distanceInMeter / KILOMETER_IN_METERS; у меня есть исключение, потому что ожидалось int, но найдено long. - person devger; 07.10.2014
comment
@josser Ну, у вашей константы нет причин быть длинной, поскольку ее значение довольно мало. Если он должен быть длинным, вам понадобится приведение к типу int. - person Eran; 07.10.2014
comment
Да, вы правы - достаточно просто быть целым числом. Что же касается Math.floor, то здесь его использовать, по-видимому, не нужно. Спасибо. - person devger; 07.10.2014

Это потому, что Math.floor принимает в качестве аргумента значение типа double, а целое число путем целочисленного деления уже является целым числом. Таким образом, все, о чем скулит сонар при кастинге, делается там, а не на вашем явном приведении к int.

Вот документ для floor: http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#floor%28double%29

person zubergu    schedule 07.10.2014

Чтобы расширить ответ Эрана, вот что вы делаете:

distanceInMeter / KILOMETER_IN_METERS делит целое число на целое число. В Java этот результат представляет собой усеченное целое число. Затем вы помещаете это в Math.floor, что принимает двойное значение. Итак, Java пытается вам помочь, превращая int в double. Затем вы преобразуете double, возвращенное Math.floor, обратно в int.

Если вы следили за этим, вы, возможно, заметили, что у вас уже есть усеченный int. Итак, ваш код должен быть простым:

public static int getKilometers(int distanceInMeter) {
    return distanceInMeter / KILOMETER_IN_METERS;
}
person KathyA.    schedule 07.10.2014
comment
этот код не компилируется, KILOMETER_IN_METERS — это долго. вам нужно сначала привести его к int. - person dube; 07.10.2014

Когда вы делите два целых числа, вы всегда получаете целое число, округленное в меньшую сторону. если вы разделите distanceInMeter / KILOMETER_IN_METERS, а затем присвоите его двойному значению, оно сначала будет разделено как integer, прежде чем присваиваться, поэтому вы получите округленное значение, приведенное к double.

Sonar не жалуется из-за Math.floor — он ругается из-за того, что вы преобразовали результат целочисленного деления (который сам по себе является округленным вниз integer) в double. В большинстве случаев это ошибка (Зачем намеренно автоматически округлять и сразу после этого приводить к двойному)?

int distanceInMeter = 505;

double result = distanceInMeter / KILOMETER_IN_METERS;
System.out.println(result); // 0.0

double result2 = distanceInMeter * 1.0 / KILOMETER_IN_METERS;
System.out.println(result2); // 0.505

double result3 = ((double) distanceInMeter) / KILOMETER_IN_METERS;
System.out.println(result3); // 0.505

1.0 * distanceInMeter / KILOMETER_IN_METERS
((double)distanceInMeter) / KILOMETER_IN_METERS
person dube    schedule 07.10.2014