Movatterモバイル変換


[0]ホーム

URL:


homepage

Issue1289118

This issue trackerhas been migrated toGitHub, and is currentlyread-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title:timedelta multiply and divide by floating point
Type:enhancementStage:resolved
Components:Library (Lib)Versions:Python 3.2
process
Status:closedResolution:accepted
Dependencies:Superseder:
Assigned To: belopolskyNosy List: belopolsky, mark.dickinson, mcherm, rhettinger, stutzbach, tim.peters, vstinner
Priority:normalKeywords:patch

Created on2005-09-12 21:41 byagthorr, last changed2022-04-11 14:56 byadmin. This issue is nowclosed.

Files
File nameUploadedDescriptionEdit
dt.diffskip.montanaro,2007-09-02 01:54
issue1289118-py3k.diffbelopolsky,2010-05-20 20:10
timedelta_arith.pymark.dickinson,2010-05-21 09:22Python reference implementation for timedelta * float, timedelta / float
issue1289118-nodoc.diffbelopolsky,2010-05-24 18:46Code + tests, round to nearest even
issue1289118+issue8817-nodoc.diffbelopolsky,2010-05-25 15:10Use _PyLong_Divmod_Near
issue1289118+issue8817-withdoc.diffbelopolsky,2010-05-25 23:28Updated "Use _PyLong_Divmod_Near"
issue1289118-withdoc.diffbelopolsky,2010-05-27 22:19Updated patch after issue8817 patch commited
Messages (33)
msg26263 -(view)Author: Daniel Stutzbach (agthorr)Date: 2005-09-12 21:41
In python 2.4.1, the datetime.timedelta type allows forthe multiplication and division by integers.  However,it raises a TypeError for multiplication or division byfloating point numbers.  This is a counterintutiverestriction and I can't think of any good reason for it.For example:>>> import datetime>>> datetime.timedelta(minutes=5)/2datetime.timedelta(0, 150)>>> datetime.timedelta(minutes=5)*0.5Traceback (most recent call last):  File "<stdin>", line 1, in ?TypeError: unsupported operand type(s) for *:'datetime.timedelta' and 'float'
msg26264 -(view)Author: Raymond Hettinger (rhettinger)*(Python committer)Date: 2005-09-13 04:11
Logged In: YES user_id=80475Tim, do you prefer the current behavior?
msg26265 -(view)Author: Michael Chermside (mcherm)(Python triager)Date: 2005-09-15 16:03
Logged In: YES user_id=99874I, too, would like to know what Tim thinks, but for what it's worth (not much) I find Daniel's point fairly convincing... multiplication by floats is an operation that makes sense, has only one possible obvious meaning, and is not particularly likely to cause errors (the way multiplying Decimal's with floats does). So IF it's easy to implement, I say go for it.
msg26266 -(view)Author: Tim Peters (tim.peters)*(Python committer)Date: 2005-09-15 21:04
Logged In: YES user_id=31435timedelta arithmetic is 100% portable now, and wholly explainable in terms of universally understood integer arithmetic.  Throw floats into it, and that's lost.That said, I don't have a strong objection to complicating the implementation if there _are_ strong use cases.  The OP's example isn't "a use case":  it's not worth anything to let someone multiply a timedelta by 0.5 instead of dividing by 2.  I don't have a use case to offer in its place (never felt a need here).If someone wants to work on it, note that a timedelta can contain more than 53 bits of information, so, e.g., trying to represent a timedelta as an IEEE double-precision number of microseconds can lose information.  This makes a high-qualty "computed as if to infinite precision with one rounding at the end" implementation of mixed datetime/float arithmetic tricky to do right.
msg26267 -(view)Author: Daniel Stutzbach (agthorr)Date: 2005-09-15 22:00
Logged In: YES user_id=6324Let me elaborate on the use-case where I originally ran intothis.I'm conducting a series of observation experiments where Imeasure the duration of an event.  I then want to do variousstatistical analysis such as computing the mean, median,etc.  Originally, I tried using standard functions such aslmean from the stats.py package.  However, these sorts offunctions divide by a float at the end, causing them to failon timedelta objects.  Thus, I have to either write my ownspecial functions, or convert the timedelta objects tointegers first (then convert them back afterwards).Basically, I want timedelta objects to look and act likefixed-point arithmetic objects so that I can reuse otherfunctions on them that were originally developed to operateon floats.  More importantly, I'd rather not have tomaintain two different versions of the functions to dealwith different types.For implementation, why not multiply the float times .day,.seconds, and .microseconds separately, then propagate andfraction parts into the next smallest group (e.g., 0.5 daysbecomes 24*60*60*0.5 seconds).I agree it'd be possible to lose information with the wrongsequence of operations, but that's always the case whenusing floating point numbers.  In other words, that, too, iswhat I would expect from the timedelta implementation.
msg26268 -(view)Author: Skip Montanaro (skip.montanaro)*(Python triager)Date: 2005-09-17 01:48
Logged In: YES user_id=44345>> Thus, I have to either write my own special functions, or convert>> the timedelta objects to integers first (then convert them back>> afterwards).How about adding tolong() that returns the number of microsecondsin the timedelta and fromlong() that accepts a long representingmicroseconds and returns a timedelta object?  That way the timedeltaobject does a reasonably simple thing and the user is still responsiblefor overflow the normal arithmetic stuff.  You can do any sort ofarithmetic operations on the long (including converting to othernumeric types) with all the attendant caveats, then convert back toa timedelta object at the end.
msg55569 -(view)Author: Skip Montanaro (skip.montanaro)*(Python triager)Date: 2007-09-02 01:54
Attached is a diff to the datetime module thatimplements floating point division.  Comments?Is it worthwhile to pursue?  If so, I'llimplement the other floating point arithmeticoperations.
msg55570 -(view)Author: Skip Montanaro (skip.montanaro)*(Python triager)Date: 2007-09-02 02:59
Ummm... make that: "I'll implement multiplication."
msg78185 -(view)Author: STINNER Victor (vstinner)*(Python committer)Date: 2008-12-22 14:25
I like this idea, it's the opposite of the issue#2706.
msg103761 -(view)Author: Alexander Belopolsky (Alexander.Belopolsky)Date: 2010-04-20 21:04
This is in a way more similar toissue1083 than toissue2706.  I am -1 on this RFE for the same reason as I am opposing allowing true division of timedelta by an int.  The timedelta type is fundamentally an integer type.  A type delta is just a certain number of microseconds.  A timedelta divided by a number or multiplied by a float is logically a fractional number of microseconds and python does not have a type to represent it.Daniel's use case of passing timedeltas to a statistical packages is neatly addressed byissue2706's timedelta / timedelta (true) division.  Just strip the dimensionality from your data by dividing each time delta by a chosen unit interval (depending on the problem, a second, a microsecond or even a day may be appropriate).  The result will be a set of floats that your number crunching package will be happy to process.Another advantage of this approach is that floats can be processed more efficiently than timedeltas with FP arithmetics and intermediate results will be more accurate in most cases.I recommend acceptingissue2706 and rejecting this issue together withissue2706.
msg103764 -(view)Author: Alexander Belopolsky (Alexander.Belopolsky)Date: 2010-04-20 21:06
I meant rejectingissue1083, of course.
msg103770 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-04-20 21:36
> The timedelta type is fundamentally an integer type.I disagree strongly with this, and find this a bizarre point of view.  Regardless of how the timedelta is stored internally, it's used to represent physical times.  I doubt there are many applications that care about the fact that each timedelta is an integral number of microseconds.Multiplication or division of a time by a float or int makes perfect sense  physically, and I think it should be a legal operation here.
msg103772 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-04-20 21:41
Not sure why this is marked for 3.3.
msg103774 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-04-20 21:44
I'll take a look at Skip's patch.
msg103775 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-04-20 21:45
Whoops.  I meant to assign this to me, not Skip.
msg106166 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-20 16:35
Sorry, dropping this again.  I've got caught up with too many non-datetime related issues.
msg106186 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-20 20:10
dt.diff does not apply to current SVN version anymore.  I am attaching a quick update that does not change the actual calculation performed.  Seeissue1289118-py3k.diff.I am still -1 for the reason I stated before, but I would like to review a working patch first before proposing a resolution.
msg106216 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-21 08:42
Alexander, I still don't understand your objection.  What's the downside of allowing the multiplication or division of a timedelta by a float?Perhaps it's true that there are applications where timedeltas are best viewed as integers (with an implicitt 'microsecond' unit), but I think it's also true that there are plenty of applications where they're just regarded as a representation of a physical quantity, and there this proposal seems entirely appropriate.I *would* want the timedelta * float and timedelta / float operations to be correctly rounded, so that behaviour is entirely predictable;  the current patch doesn't do that.  But it wouldn't be hard to implement:  there are functions available to express a float as a quotient of two integers, and after that the computation can be performed in integer arithmetic.
msg106220 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-21 09:22
Python reference implementation showing how to do correct rounding.
msg106223 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-21 10:05
N.B.  There's already logic for doing div_nearest (i.e., divide one integer by another, returning the closest integer to the result) in the long_round function inObjects/longobject.c.  It might be worth pulling that logic out and making it available in a _Py function so that it can be reused in other modules.
msg106378 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-24 18:46
I am attaching a patch that implements Mark's timedelta_arith.py algorithms in C.With rounding well defined, I am close to withdrawing my opposition to supporting mixed timedelta with float operations.  There is still one issue that I believe is worth discussing before this feature is accepted.  Time, unlike most other physical quantities has a non-arbitrary notion of direction.  Therefore, in many applications, rounding towards past or towards future may be preferable to rounding to nearest.For example, one of the likely applications of floating point division would be to construct time series from continuous or differently sampled functions.  If such series are used to measure correlations between cause and effect, it is important that effect is measured at a time following the cause and not at an early or the same moment.As Mark noted in private correspondence, this issue is mitigated by the fact that "with correct rounding, for timedeltas t and s, and a positive float x, it is guaranteed that t <= s implies t op x <= s op x" (where op is either * or /).  It is still possible however, that even the case of t < s and t op x == s op x present a problem in some applications.Despite this issue, I would support round to nearest even choice over round to past or to future mainly because it is less likely to lead to surprises where (d1/d2) * d2 != d1.  This choice also conforms with the round() builtin definition and is somewhat more difficult to implement right using existing means.Daniel, would you like to chime in on the questions of how the results of these operations should be rounded?If I don't hear principle objections from the "nosy" list, I'll add a documentation patch.
msg106381 -(view)Author: Daniel Stutzbach (stutzbach)(Python committer)Date: 2010-05-24 19:50
I don't have a strong feeling about the method of rounding.  My thinking is: If my application is sensitive to how the last microsecond is rounded, then I shouldn't be using a type that only gives me 1-microsecond precision.(Likewise, if my application is sensitive to how the last binary digital of the floating point mantissa is rounded ... I'm in trouble)That said, round-to-nearest strikes me as the least-surprising approach.
msg106383 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-24 19:58
Re rounding:  I'll just note that timedelta / timedelta -> float currently does round to nearest;  I'd find it quite surprising if float * timedelta -> timedelta didn't round to nearest.
msg106387 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-24 20:44
It looks like we have a consensus on the rounding mode.  Note, howeverthat timedelta constructor rounds away from zero at least onIntel/MacOS X:[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Should this be considered a bug?For comparison,[-10, -8, -8, -6, -6, -4, -4, -2, -2, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10][-10, -8, -8, -6, -6, -4, -4, -2, -2, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10]
msg106388 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-24 20:48
Alexander, it looks like Roundup ate some of your message there. :)Yes, ideally I'd say that the constructor should be doing round-half-to-even.  Though last time I looked, the constructor looked quite complicated (especially for float inputs);  it may not be feasible to fix this easily.At any rate, we should open a separate issue for this.
msg106389 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-24 20:54
Indeed.  Here is what I intended:""">>> from datetime import timedelta as d>>> [d(microseconds=i + .5)//d.resolution for i in range(-10,10)][-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]Should this be considered a bug?For comparison,>>> [d.resolution*(i+0.5)//d.resolution for i in range(-10,10)][-10, -8, -8, -6, -6, -4, -4, -2, -2, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10]and>>> [round(i+0.5) for i in range(-10,10)][-10, -8, -8, -6, -6, -4, -4, -2, -2, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10]"""I checked the documentation and while it says: "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." it does not specify how half-integers should be handled.While it may not be a bug in strict sense, it looks like the code in question can be improved.  I'll open a separate issue for this.
msg106421 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-25 09:10
By the way, does your patch do the right thing fortimedelta(microseconds=1) / -4.0?  Because my Python code doesn't.  :) [If n is negative, then the 2*r > n condition in div_nearest should be 2*r < n instead.]
msg106432 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-25 12:57
There's a patch inissue 8817 that exposes a round-to-nearest form of divmod in a function called _PyLong_Divmod_Near;  this would save on duplication of code.
msg106435 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-25 14:31
> By the way, does your patch do the right thing for> timedelta(microseconds=1) / -4.0No.>>> timedelta(microseconds=1) / -4.0datetime.timedelta(-1, 86399, 999999)(I just copied your python algorithm ...)I will merge withissue 8817 patch and that should fix the problem.
msg106439 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-25 15:10
Attaching a combinedissue1289118 +issue8817 patch.  Datetime code now usesissue8817's _PyLong_Divmod_Near.
msg106492 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-25 23:28
Attaching a new patch with documentation changes, additional tests, updatedissue8817 patch and a reference leak fix.
msg106733 -(view)Author: Mark Dickinson (mark.dickinson)*(Python committer)Date: 2010-05-29 19:48
The patch looks good to me.  Please replace the tab characters in datetimemodule.c with spaces, though. :)
msg106800 -(view)Author: Alexander Belopolsky (belopolsky)*(Python committer)Date: 2010-05-31 19:01
Committed inr81625.  Fixed white space and added a note to "new in 3.2" section of the RST doc.
History
DateUserActionArgs
2022-04-11 14:56:13adminsetgithub: 42364
2010-06-06 01:25:31belopolskylinkissue1083 superseder
2010-05-31 19:01:14belopolskysetstatus: open -> closed
resolution: accepted
messages: +msg106800

stage: commit review -> resolved
2010-05-29 19:48:37mark.dickinsonsetmessages: +msg106733
2010-05-27 22:19:49belopolskysetfiles: +issue1289118-withdoc.diff
stage: commit review
2010-05-25 23:28:54belopolskysetfiles: +issue1289118+issue8817-withdoc.diff

messages: +msg106492
2010-05-25 15:10:11belopolskysetfiles: +issue1289118+issue8817-nodoc.diff

messages: +msg106439
2010-05-25 14:31:35belopolskysetmessages: +msg106435
2010-05-25 12:57:41mark.dickinsonsetmessages: +msg106432
2010-05-25 09:10:10mark.dickinsonsetmessages: +msg106421
2010-05-24 20:54:59belopolskysetmessages: +msg106389
2010-05-24 20:48:29mark.dickinsonsetmessages: +msg106388
2010-05-24 20:44:40belopolskysetmessages: +msg106387
2010-05-24 19:58:57mark.dickinsonsetmessages: +msg106383
2010-05-24 19:50:01stutzbachsetmessages: +msg106381
2010-05-24 18:46:46belopolskysetfiles: +issue1289118-nodoc.diff

messages: +msg106378
2010-05-21 10:05:23mark.dickinsonsetmessages: +msg106223
2010-05-21 09:22:21mark.dickinsonsetfiles: +timedelta_arith.py

messages: +msg106220
2010-05-21 08:42:44mark.dickinsonsetmessages: +msg106216
2010-05-20 20:41:36skip.montanarosetnosy: -skip.montanaro
2010-05-20 20:22:49stutzbachsetnosy: -agthorr
2010-05-20 20:10:14belopolskysetfiles: +issue1289118-py3k.diff
keywords: +patch
messages: +msg106186
2010-05-20 18:13:05belopolskysetassignee:belopolsky
nosy: +belopolsky, -Alexander.Belopolsky
2010-05-20 16:35:12mark.dickinsonsetassignee:mark.dickinson -> (no value)
messages: +msg106166
2010-04-27 14:08:12stutzbachsetnosy: +stutzbach
2010-04-20 21:45:02mark.dickinsonsetassignee:skip.montanaro ->mark.dickinson
messages: +msg103775
2010-04-20 21:44:40mark.dickinsonsetassignee:skip.montanaro
messages: +msg103774
2010-04-20 21:41:52mark.dickinsonsetmessages: +msg103772
versions: + Python 3.2, - Python 3.3
2010-04-20 21:36:32mark.dickinsonsetnosy: +mark.dickinson
messages: +msg103770
2010-04-20 21:06:25Alexander.Belopolskysetmessages: +msg103764
2010-04-20 21:04:55Alexander.Belopolskysetversions: + Python 3.3, - Python 2.6
nosy: +Alexander.Belopolsky

messages: +msg103761

type: enhancement
2008-12-22 14:25:54vstinnersetnosy: +vstinner
messages: +msg78185
2007-09-02 02:59:53skip.montanarosetmessages: +msg55570
2007-09-02 01:54:54skip.montanarosetfiles: +dt.diff
messages: +msg55569
versions: + Python 2.6
2005-09-12 21:41:10agthorrcreate
Supported byThe Python Software Foundation,
Powered byRoundup
Copyright © 1990-2022,Python Software Foundation
Legal Statements

[8]ページ先頭

©2009-2026 Movatter.jp