2017-02-08 6 views
0

При портировании кода с Python 2 на Python 3 тесты подсвечивали числовую регрессию при вычислении дат с datetime.datetime, что мне трудно объяснить.Небольшое отклонение между вычислениями datetime.datetime в Python 2 и 3

Как воспроизвести

date_max = datetime.datetime(2016, 9, 28, 4, 21, 5, 228000) 
date_min = datetime.datetime(2016, 9, 28, 4, 21, 4, 460315) 
date_futur = date_min + datetime.timedelta(seconds=((date_max - date_min).total_seconds()/2)) 

Выходы

Выход из print date_futur в Python 2.7.12:

2016-09-28 04:21:04.844158 

Выход из print(date_futur) в Python 3.5.2:

2016-09-28 04:21:04.844157 

Выпуск

Это только одна разница микросекунды, но это ошибки мне, потому что я не могу это объяснить, так что я не знаю, смогу ли я обновить мои результаты испытаний с новым поведением Python 3, или если что-то более сложное под рукой.

Возможно привести

Может быть, это из-за того, как Python 3 округляет 0,5 до ближайшего четного числа, а не вверх, как Python 2?


Update

Результат (date_max - date_min).total_seconds()/2 в обоих случаях составляет 0.8441575 секунд. Однако после того, как передал datetime.timedelta конструктор:

Python 2:

datetime.timedelta(0, 0, 383843) 

Python 3:

datetime.timedelta(0, 0, 383842) 

Так что-то шаткий происходит в timedelta конструктор!

ответ

0

С датой и время официального Python 3 документации:

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

Заменив числовых значений, мы делаем:

datetime.timedelta(seconds=0.3838425) 

Поплавок значение 0,3838425 затем преобразуется в микросекундах, что дает 383842.5 микросекунд. Затем он округляется до 383843 в Python 2 (.5 всегда округляется) и до 383842 в Python 3 (округляется до самого близкого четного числа). Что вводило в заблуждение, так это то, что эта величина затем добавляется к другой дате с нечетным числом микросекунд (460315), переворачивая соотношение конечного результата!

2

Причиной этой разницы является разное поведение Python 2 и 3. Python 2 использует целочисленное деление, а Python 3 использует float-деление.

3/2 выходы 1 в Python 2 и 1.5 в Python 3.

Так преступник эта часть кода: (date_max - date_min).total_seconds()/2

Использование from __future__ import division заставит Python 2 использовать поплавок разделение. Изменение 2 на 2.0 также сделает использование Python плавающим делением.

+0

Я попытался заменить его на '/ 2.0', Python 2 не изменил его поведения. Может быть, если вы передадите float в timedelta, он применит к нему функцию «round», которая может объяснить различное поведение? –

+0

Я думаю, что это может быть на самом деле, если вы делаете расчет «вручную», '(date_max - date_min) .total_seconds()/2' дает 844157 **. 5 ** микросекунды ... Это выглядит подозрительно? –

+0

@ValentinB. Он преобразует его в 'int', используя float-версию' mod': if 'isinstance (seconds, float): secondsfrac, seconds = _math.modf (seconds) assert seconds == int (seconds) seconds = int (секунд) secondsfrac + = daysecondsfrac assert abs (secondsfrac) <= 2.0' – DeepSpace