Python 2 和 3 中的 datetime.datetime 计算略有偏差

Slight deviation between datetime.datetime calculations in Python 2 and 3

在将一些代码从 Python 2 移植到 Python 3 时,测试突出显示了使用 datetime.datetime 计算日期时的数值回归,我很难解释。

如何重现

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))

输出

Python 2.7.12 中 print date_futur 的输出:

2016-09-28 04:21:04.844158

Python 3.5.2 中 print(date_futur) 的输出:

2016-09-28 04:21:04.844157

问题

这只是一微秒的差异,但它让我很烦恼,因为我无法解释它,所以我不知道我是否可以用新的 Python 3 行为更新我的测试结果,或者是否有更复杂的东西在眼前。

可能领先

可能是因为 Python 3 如何将 0.5 舍入到最接近的偶数,而不是像 Python 2 ?


更新

两种情况下 (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 构造函数中发生了一些奇怪的事情!

造成这种差异的原因是 Python 2 和 3 的除法行为不同。Python 2 使用整数除法,而 Python 3 使用浮点数除法。

3 / 2 在 Python 2 中输出 1,在 Python 3 中输出 1.5

所以罪魁祸首是你的这部分代码: (date_max - date_min).total_seconds() / 2

使用from __future__ import division 将导致Python 2 使用浮点除法。将 2 更改为 2.0 也会使 Python 使用浮点除法。

来自datetime官方Python3文档:

If any argument is a float and there are fractional microseconds, the fractional microseconds left over from all arguments are combined and their sum is rounded to the nearest microsecond using round-half-to-even tiebreaker. If no argument is a float, the conversion and normalization processes are exact (no information is lost).

用数值代替,我们在做:

datetime.timedelta(seconds=0.3838425)

然后将浮点值 0.3838425 转换为微秒,得到 383842.5 微秒。然后在 Python 2 中四舍五入为 383843(.5 总是四舍五入),在 Python 3 中四舍五入为 383842(四舍五入到最接近的偶数)。误导的事实是,这个值随后被添加到另一个具有奇数微秒 (460315) 的日期,翻转了最终结果的奇偶校验!