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

Вопрос о математических операциях Java с типами данных float и int.

Мне нужно рассчитать разницу дат в годах между двумя датами.

Код:

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Object date = obj.getInfoItem().getInfoValue();

Date today = new Date();
Date birthDate = null;
float dateDiff;
int age0, age1;

try {
        // unify the date format
        birthDate = format.parse(date.toString());
        today = format.parse(format.format(today));
} catch (ParseException e) {
        e.printStackTrace();
}

// calculate the age in years with round    
dateDiff = today.getTime() - birthDate.getTime();
age0     = (int)((dateDiff / (24 * 60 * 60 * 1000)) / 365);
age1     = (int)(dateDiff / (365 * 24 * 60 * 60 * 1000));

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

После выполнения кода я получил следующие результаты в отладчике:

dateDiff = 8.4896639E11  
age0 = 26  
age1 = 577

age0 - правильный результат.

Поскольку обе операции над age0 и age1 математически равны, почему результаты различны? Почему есть разница между операциями «(float / (a\*b\*c)) / d» и «(float / (a\*b\*c\*d))», где a, b, c, d это int.


person Mike B.    schedule 09.06.2014    source источник
comment
Это 365 * 24 * 60 * 60 * 1000 - целочисленное переполнение.   -  person Sotirios Delimanolis    schedule 09.06.2014
comment
Чтобы расширить это, если вы хотите выполнять вычисления в числах с плавающей запятой, используя постоянные значения, вы должны явно установить константы как числа с плавающей запятой. т.е. 365f * 24f * 60f * 60f * 1000f.   -  person Chill    schedule 09.06.2014
comment
«365f * 24f * 60f * 60f * 1000f» устранило проблему. Спасибо!   -  person Mike B.    schedule 09.06.2014


Ответы (1)


Чтобы расширить комментарий Сотириоса: все литералы целых чисел 365, 24, 60 и 1000 имеют тип int. Поэтому умножение будет выполняться с использованием типа int. Поскольку математический результат равен 3 153 600 000, а максимальное возможное число int равно 2 147 483 648, результат переполняется, и результат зацикливается. Таким образом, результатом будет int, значение которого эквивалентно 31536000000 по модулю 232, то есть 1471228928. Только тогда оно преобразуется в float для деления на dateDiff. Добавление L в конец любого целочисленного литерала исправит это, так как теперь хотя бы одно из умножений будет выполняться с использованием long. Но может быть понятнее изменить 365 на 365.0 (или 365f). (На самом деле, предложение @Chill использовать f для всех констант кажется мне лучшим, хотя на самом деле это не обязательно.)

person ajb    schedule 09.06.2014