
[Raymond Hettinger]
[Christian Heimes]
Traditions have gone out the window. ceil() and floor() are going to have their signatures changed (Real --> Integral) and are going to have their own magic methods. They cannot be characterized as a thin wrapper around libm. So the question stands, why is trunc() different? Can anything good come from having trunc() and int() in the same namespace? Raymond

If the ambiguity is that 'int' behaviour is unspecified for floats - is it naive to suggest we specify the behaviour?
The concern is that whatever gets specified is arbitrary. There are many ways how an int can be constructed from a float, so why is any such way better than the others, and deserves to be the meaning of int()? Regards, Martin

""Martin v. Löwis"" <martin@v.loewis.de> wrote in message news:47992363.3010402@v.loewis.de... |> If the ambiguity is that 'int' behaviour is unspecified for floats - is | > it naive to suggest we specify the behaviour? | | The concern is that whatever gets specified is arbitrary. There are many | ways how an int can be constructed from a float, so why is any such way | better than the others, and deserves to be the meaning of int()? Decades of usage, in English, with the meaning it already has in Python.

In article <47992363.3010402@v.loewis.de>, "Martin v. Lowis" <martin@v.loewis.de> wrote:
But something should be specified. Users should be able to expect consistent behavior. Surely there must be some efficiency reason why it is not specified (e.g. it uses a low-level C call that is not specified)??? I agree with the idea of putting trunc in the math library since it seems to similar to floor. -- Russell

On Jan 24, 2008 1:11 PM, Raymond Hettinger <python@rcn.com> wrote:
One of my goals for trunc() is to replace the from-float use of int(), even though we can't remove it for backward-compatibility reasons. PEP 3141 says: "Because the int() conversion implemented by float (and by decimal.Decimal) is equivalent to but less explicit than trunc(), let's remove it. (Or, if that breaks too much, just add a deprecation warning.)" That needs to be updated and implemented. I think the decision was that removing float.__int__() would break too much, so it needs a deprecation warning in 3.0. int has to be a builtin because it's a fundamental type. trunc() followed round() into the builtins. I have no opinion on whether ceil and floor should move there; it probably depends on how often they're used. -- Namasté, Jeffrey Yasskin

On Jan 24, 2008 3:32 PM, Michael Foord <fuzzyman@voidspace.org.uk> wrote:
Yes, you can, but you have to specify how you want it done by using trunc() or round() or ceil() or floor(). (In 3.0, round(x) will return an int, not a float.) -- --Guido van Rossum (home page: http://www.python.org/~guido/)

2008/1/24, Guido van Rossum <guido@python.org>:
2008/1/24, Jeffrey Yasskin <jyasskin@gmail.com>:
What I understand here is as int() is "ambiguous", in the future if you want to specify how you want to convert a float to int. But ceil and floor returns a float. And round and trunc will return an int. So, how I could convert a float to its upper int? Like this?:
trunc(math.ceil(.3)) 1
BTW, int is not giving me a deprecation warning:
int(.1) 0
Thanks! -- . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/

On Jan 25, 2008 4:28 AM, Facundo Batista <facundobatista@gmail.com> wrote:
Like this, in 3.0:
math.ceil(2.2) 3
There was a previous thread in which we decided not to change that behavior in 2.6.
Correct; that's not implemented yet. -- Namasté, Jeffrey Yasskin http://jeffrey.yasskin.info/

Jeffrey Yasskin wrote:
It seems strange to me that a release that has the avowed intention of producing a "more rational" language by breaking backwards compatibility should start out with deprecation warnings of this type. What's next: "This isn't Perl" when someone tries to add a number and a string? Surely the place to raise warnings is in 2to3. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/

On 24/01/2008, Jeffrey Yasskin <jyasskin@gmail.com> wrote:
Suggestion: - int() has to stay in builtins for obvious reasons. - put *all* of trunc, ceil, floor, round into math. - make int(float) an error The only fly in the ointment is that 2to3 can't handle the semantic issues around converting int(n) to math.trunc(n) because it can't detect the type of n. So why not make int(float) warn in -3 mode on 2.6, and then let 2to3 do the conversion (on the basis that 2to3 should only be run on code that is -3 warning free)? Did I miss a requirement here? Paul.

2008/1/25, Paul Moore <p.f.moore@gmail.com>:
- int() has to stay in builtins for obvious reasons.
+1
- put *all* of trunc, ceil, floor, round into math.
+1
- make int(float) an error
-0 (you should be able to convert between builtin datatypes without the use of a module). +1 to keep it and define exactly the behaviour, and point to math module in the documentation if you want better control over the conversion. Regards, -- . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/

Paul Moore wrote:
Slightly different suggestion: - define int(float) as int(trunc(float)) In my humble opinion lots of people expect int(-2.5) == -2 and int(2.5) == 2. Or in other words: int(float) chops of the digits after the dot. As far as I can see float.__int__ already behaves like int(trunc(float))
2.4 ( 2, 3, 2, 2, 2) 2.6 ( 2, 3, 3, 2, 2) -2.4 (-3, -2, -2, -2, -2) -2.6 (-3, -2, -3, -2, -2) Python 2.6: 2.4 ( 2.0, 3.0, 2.0, 2, 2) 2.6 ( 2.0, 3.0, 3.0, 2, 2) -2.4 (-3.0, -2.0, -2.0, -2, -2) -2.6 (-3.0, -2.0, -3.0, -2, -2) Christian

Christian Heimes wrote:
FWIW, this approach gets a +1 from me (although I'm -0 on taking round() out of builtins - that seems like a pointless incompatibility to me). Yes, blessing 'trunc' as the default mechanism for converting a non-integral number to an integer is somewhat arbitrary, but it's a piece of arbitrariness with a very long history behind it. For that matter, we could even formally define int() as falling back to trunc() if there is no direct conversion (i.e. int knows about the type natively, or the type provides an __int__ method). That way non-integral types could just implement __trunc__ without having to worry about adding "__int__ = __trunc__" to the class definition. We would still have operator.index to identify types which can be losslessly converted to a builtin integer. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org

[I am still recovering, so if I say something totally misinformed I blame my recovery. :) ] -On [20080125 15:12], Christian Heimes (lists@cheimes.de) wrote:
Am I the only one who wonders about the sudden change in decimal significance? Especially given the fact that the ISO C standard specifies floor(), for example, as returning a floating point value and the above in Python 3.0 deviates to just returning an integer. Which is also different from 2.5's behaviour. Can I assume we are all familiar with the concept of significant digits and that we agree that from this point of view 2 != 2.0? And that results such as the above would be a regression and loss in precision? -- Jeroen Ruigrok van der Werven <asmodai(-at-)in-nomine.org> / asmodai イェルーン ラウフロック ヴァン デル ウェルヴェン http://www.in-nomine.org/ | http://www.rangaku.org/ We have met the enemy and they are ours...

Jeroen Ruigrok van der Werven wrote:
Not really, because if someone cares about precision to that extent they won't be using binary floats anyway (they'll be using Decimal, or something like it). Besides, for trunc, ceil and floor, it's all zeroes after the decimal point by definition (no matter how many significant digits you started with). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org

On Jan 25, 2008 5:53 AM, Paul Moore <p.f.moore@gmail.com> wrote:
I'd deal with Facundo's objection that you should be able to convert between builtin datatypes without the use of a module by leaving trunc in the builtins, but it looks like I'm in the minority here. If the decision comes to be that int(float) should be blessed as a correct way to truncate a float, I'd agree with Raymond that trunc() is just duplication and should be eliminated. I'd, of course, rather have a spelling that says what it means. :)
I'd be happy with that too. -- Namasté, Jeffrey Yasskin

2008/1/25, Jeffrey Yasskin <jyasskin@gmail.com>:
Mmm... no. int() is a builtin way to transform the builtin data type float into the builtin data type float. There's no "correct" way for a float to become an integer, but in the math module you have several ways to do it (floor, ceil, round, trunc, choose the one that you want, but you're "notified" <wink/2> that there're different ways to do it). -- . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/

On Jan 25, 2008 9:45 AM, Facundo Batista <facundobatista@gmail.com> wrote:
I assume you meant "int" instead of your second "float". There doesn't have to be a way to convert from every builtin type to every other builtin type. Take dict(float), for example. Further, if there is a conversion, no law says it has to be named the same as the target type. If there are two plausible ways to get from the source to the target, using just the target's name to pick out one of them is really a terrible idea. But, just because the designers of C made a bad decision that we've inherited, doesn't mean that it's a terrible idea to keep it. As Nick said, defining int(float) as truncation has a long history, and there are some obvious costs to breaking away from that. I think it is time to correct the mistake, but it's not an open and shut case. -- Namasté, Jeffrey Yasskin

On Jan 25, 2008 12:45 PM, Facundo Batista <facundobatista@gmail.com> wrote:
In keeping with this theme, why not define int() for floats (and other real types) as def __int__(n, method=math.trunc) or something similar, so that, by default, int() provides the same functionality as before (or whatever is decided to be preferred, I'm making no judgements on that end), but has a way --- by passing a different function --- of changing the way it rounds? The other (probably preferred) option, I suppose, would be to provide a few constants (float.FLOOR_METHOD et al.) instead of passing an arbitrary function (which, of course, makes me a bit uncomfortable). -- Cheers, Leif

Paul Moore schrieb:
That, and making int(float) == int(trunc(float)) seems like reasonable behavior to me as a person not involved in the discussion. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

-On [20080126 09:43], Georg Brandl (g.brandl@gmx.net) wrote:
+1 from my side for the above. -- Jeroen Ruigrok van der Werven <asmodai(-at-)in-nomine.org> / asmodai イェルーン ラウフロック ヴァン デル ウェルヴェン http://www.in-nomine.org/ | http://www.rangaku.org/ We have met the enemy and they are ours...

On Jan 26, 2008 12:43 AM, Georg Brandl <g.brandl@gmx.net> wrote:
That, and making int(float) == int(trunc(float)) seems like reasonable behavior to me as a person not involved in the discussion.
That's the only position in this discussion that I don't understand. Although int() and trunc() would still have very slightly different behaviors, they seem too close together (identical on most common types) to be compatible with "only one way to do it". I've been advocating trunc() under the assumption that int(float) would be deprecated and eliminated as soon as practical (with an error message similar to float(complex)). Could one of the people in favor of keeping both explain why they think that's a good idea? -- Namasté, Jeffrey Yasskin

On Jan 26, 2008 2:53 PM, Raymond Hettinger <python@rcn.com> wrote: [Jeffrey]
We'll see. Jeffrey did say "deprecated" and "as soon as practical". A -3 warning in 2.6 could do wonders.
This position is totally nuts.
Hold it right there. You may disagree, but that doesn't make it nuts. There is actually quite an important signal to the reader that is present when you see trunc(x) but absent when you see int(x): with trunc(x), the implication is that x is a (Real) number. With int(x), you can make no such assumption -- x could be a string, or it could be a totally different type that happens to define __int__, perhaps a custom date/time type (I believe mxDateTime supports this).
There is *nothing* wrong with int() as it stands now. Nobody has problems with it. The tools is ubiquitous in other languages,
Talk to C++ experts. They have a huge beef with C's confusing use of casts for two different purposes: reinterpretation of the same bit pattern vs. value conversion. Python problem isn't quite the same, but I still really wish I had followed Pascal's lead instead of C's here: Pascal requires you to use trunc() to convert a real to an integer. (BTW Pascal also had the division operator right, unlike C, and we're finally fixing this in Py3k by following Pascal's nearly-40-year-old lead.) If we had done it that way, we wouldn't have had to introduce the index() builtin and the corresponding infrastructure (__index__ and a whole slew of C APIs).
spreadsheets, and calculators.
I don't think that Excel should be held up as a shining example for Python. At least, that would be quite a change. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

. You may disagree, but that doesn't make it nuts.
Too many thoughts compressed into one adjective ;-) Deprecating int(float)-->int may not be nuts, but it is disruptive. Having both trunc() and int() in Py2.6 may not be nuts, but it is duplicative and confusing. The original impetus for facilitating a new Real type being able to trunc() into a new Integral type may not be nuts, but the use case seems far fetched (we're never had a feature request for it -- the notion was born entirely out of numeric tower considerations). The idea that programmers are confused by int(3.7)-->3 may not be nuts, but it doesn't match any experience I've had with any programmer, ever. The idea that trunc() is beneficial may not be nuts, but it is certainly questionable. In short, the idea may not be nuts, but I think it is legitimate to suggest that it is unnecessary and that it will do more harm than good.
I don't think that Excel should be held up as a shining example for Python.
It is simply a datapoint. We don't care why they chose int() for truncation. The important thing is the subsequent user experiences which indicates that people do not have a problem with it. When it comes to int() and round(), people just get it. I've taught spreadsheets to a lot people and no one has ever construed int() as meaning round(). What was your experience with 18 years of python? Was it a recurring a point of confusion? On the newsgroup, helplist, and tutorial list, I never seen this come up as problem. I looked in other places for corroboration. My HP-12C has intg and frac buttons. In BASIC, int() means to return the integer part of a floating point number. The surprise find was that int() in Matlab and Maple means integrate :-) FWIW, Perl's int() does what ours currently does. One other thought: In Python, it has been nice that we have simple type coercions using the type name: * set(p)-->q can accept a list, tuple, string, or any iterable and make a set * int(p)-->q can accept an int, long, float, or string and make an int * float(p)-->q can accept an int, long, float, or string and make an int * list(p)-->q can accept a list, tuple, string, or any iterable and make a list * unicode(p)--> can accept a str, buffer, or unicode object and make a unicode object It's a bit weird to decide that int() needs to lose that capability so we get generalized Integrals as output. What's wrong with coercion to a concrete type? Raymond

On Jan 26, 2008 11:14 PM, Raymond Hettinger <python@rcn.com> wrote:
The idea that programmers are confused by int(3.7)-->3 may not be nuts, but it doesn't match any experience I've had with any programmer, ever.
In C, I'm pretty sure I've seen people write (long)x where they'd have been better off with lround(x). They know that the cast truncates, and generally that they actually wanted to round, but the type they wanted to get to was foremost in their mind, so they didn't bother to think about it a little and write what they really wanted. It's not that they're confused; it's that they're accepting a default that shouldn't be a default. Your other points seem to have been answered already, although people will disagree on how compelling the answers are, so I won't repeat them here. -- Namasté, Jeffrey Yasskin

Raymond Hettinger wrote:
The idea that programmers are confused by int(3.7)-->3 may not be nuts, but it doesn't match any experience I've had with any programmer, ever.
You haven't been doing newbie support in #python lately. Statements like Python is rounding wrong are common. ;) It's astonishing how many major rounding and division bugs are found by newbies. Christian

On Jan 26, 2008 11:14 PM, Raymond Hettinger <python@rcn.com> wrote:
You're beginning to repeat your argument; none of that was new.
Let me get back to you on that. I first want to point out that you snipped the core of my argument in the post you otherwise quoted. I will repeat it here because I would like your explicit reaction: [Guido]
Can I assume that you agree with this? That would be progress. Coming back to your argument that other types have a constructor that takes all kinds of arguments, I'd like to point out that they all have restrictions too (except for str()): list() and tuple() only accept iterables, float() only accepts strings and certain numbers (not complex), and so on. Taking the latter as an example:
I think that (eventually) int(0.0) could do something similar:
But see below. Yet another (minor) argument that has always made me uncomfortable with int() == trunc(): the % operator. I think it's a big improvement over C that Python's % operator is defined as x%y == x - y*floor(x/y) # where / is real division rather than C's division, which uses trunc() instead of floor(). In C this nicely meshes with the definition of int(): you can define x%y as x - y*(int)(x/y); but not so in Python. I don't want to use this as an argument for defining int(x) as floor(x), but I do want to point out that it has always given me a twinge of discomfort. FInally, there's the "one way" argument. That's a nice slogan, but doesn't really hold anyways in practice. To copy a list, we can write either L[:] or list(L). To get the keys of a dict, we can write either D.keys() or list(D). To convert a number to a string we can write either "%g" % X or str(X). For octal we can write "%#o" % X or oct(X). To convert a unicode string to UTF-8, we can write either U.encode("utf8") or str(U, "utf8"). And so on. In many cases, these notations aren't exactly the same in semantics (e.g. list(X) always returns a list, X[:] returns whatever sequence type X is), but nevertheless they have large areas of overlap. This is how I see trunc() and int() live together. After all that, here's my current proposal: - Deprecating int(<float>) is pretty radical, I think it would have to happen in the distant future. OR not at all. I'm at best +0 on this, more like exactly 0. I realize that in practice this kills the idea. The "purist" argument for it would have worked better if it was made 18 years ago. - trunc(), round(), floor() and ceil() should all be built-ins, corresponding to __trunc__, __round__, __floor__ and __ceil__. Then we have the four standard ways to go from Reals to Integers, which are properly extensible for folks who write their own number types. (We can't control how folks implement __round__, but we can document expected behavior -- that's how we treat __add__ and all other operators too, after all.) - In the docs (especially for beginners) we recommend that users write trunc() instead of int(), emphasizing that trunc() ensures the argument is a number, while suggesting int(x) for conversion from strings. But the implementation won't chastise users by issuing annoying warnings. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 27, 2008 5:43 PM, Guido van Rossum <guido@python.org> wrote:
Also what happens with "%i" % 3.14 ? We incidentally found a problem with a script using python 2.5 because apparently the "%" formatting operator doesn't use "int()" for doing the conversion (to be more specific if the float is too large for a 32-bit integer then the format operator chokes while the int() operator returns a long). Anyway I want just to say that if "implicit" conversion from float to integer goes away then what happens to formatting conversion ? Removing that too IMO would break a lot of code and it's IMO very difficult to help fixing that. Andrea

On Jan 27, 2008 9:26 AM, Andrea Griffini <agriff@tin.it> wrote:
Well, it seems like it would be as easy to make some formatting conversions raise a warning on float inputs as it would be to make int() do it. But I think you're safe here; it doesn't look like either will be deprecated. -- Namasté, Jeffrey Yasskin http://jeffrey.yasskin.info/

On Jan 27, 2008 9:26 AM, Andrea Griffini <agriff@tin.it> wrote:
That's quite a separate issue. Please ses http://bugs.python.org/issue1742669.
The formatting code could assign specific meanings. I suspect though that it was never meant to be possible to use %d with a float -- it just is one of the artifacts of using implicit conversion, and one not well-thought through. Note:
I think the latter is wrong and confusing. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

I agree that it provides a cue that that input is Real. I don't agree that that has any value. We had a lot of tools that can accept multiple input types. For instance, float() can accept a string or number. We could do like C and split-out the atof() functionality but that just makes two functions where one would suffice.
It hasn't bugged me much, but I do understand.
"pretty radical" is what I meant when I said "it's nuts" ;-) This is a major piece of progress. Most of the prior proposal was predicated on int(<float>) being deprecated.
ISTM, you really don't need trunc() in this mix. The ceil() and floor() functions do an excellent job of convering most use cases. The overlap of trunc() and int() just isn't worth it. There's the mental cost of having to explain the difference on day one to every beginner. There's the back end cost of every class that has __int__ also needing to decide whether to provide __trunc__ which will typically be the same thing. In time, I believe it will become self-evident that having both int() and trunc() is a wart. If trunc() goes into 2.6 and 3.0, then we're going to have to live with this for a long time. Purity may suggest that you need trunc(). Practicality says it isn't worth the cost. At least we've made important progress by saving int(<float>). Raymond

On Jan 27, 2008 10:29 AM, Raymond Hettinger <python@rcn.com> wrote:
All I claim is that it has *some* value for me. If it doesn't have value for you, that doesn't make it worthless.
Progress.
Good.
Not really. You'd have to use an if predicated on the sign to emulate trunc() using ceil() and floor(). Or something like sign(x) * floor(abs(x)) -- but we don't have sign().
I don't see much cost for end users at all. It's not like trunc() represents a difficult concept. Having trunc() in addition to ceil(), floor() and round() creates a nicely complete set of float->int conversions. Then int(<float>) can be defined by deferring to trunc() -- as opposed to round(). My proposal stands (less any talk of deprecation of int(<float>)). -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 27, 2008 10:54 AM, Raymond Hettinger <python@rcn.com> wrote:
A single type wouln't need both. But int() should still try both, because it doesn't make sense for e.g. a date type to have to define __trunc__ for conversion to an int. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 27, 2008 12:12 PM, Raymond Hettinger <python@rcn.com> wrote:
When imported from math, yes, for 2.5 compatibility. If we add builtins, the builtins won't have this restriction. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Is this a valid summary of the arguments so far? I see two arguments for the change: 1) The semantics of trunc() are clear: it maps R -> Z in a specific fashion 2) The semantics of int() are fuzzy; even non-numeric types (strings) are handled Yet there will be a __trunc__ that will allow any chosen mapping to be implemented, so long as it results in an integer, so (1) is only guaranteed true for the builtin types. This leaves us with (2) which seems strongly tied to string parsing (as __index__ resolved the other common X -> integer case). I see one main argument against: *) trunc() results in duplication at best, zealous deprecation at worst Given that the deprecation or removal of int(2.3) has been dropped, the argument is about pointless duplication. What problem is trunc() supposed to solve if it does to floats what int() does now? I've done some initial code searches for: lang:python "int(", and I've seen three primary uses for calling int(x): a) parsing strings, such as from a user, a config file, or other serialized format b) limiting the input to a function to an integer, such as in a calendar trying to ensure it has integer months c) truncation, such as throwing away sub-seconds from time.time(), or ensuring integer results from division It's unclear to me whether (b) could be better served by more type-specific operations that would prevent passing in strings, or whether uses like (c) often have latent bugs due to truncation instead of rounding. If trunc() is to clarify round vs. integer-portion, it's something people learn -- the general lack of comments in (c) usages indicates nobody considers it special behavior. If it's to clarify the argument's type (the parsing of strings vs. getting integers from other numeric types), would separating parsing from the int (and float) constructors also solve this? Is the aim to "clean up" the following fake example? (Real world uses of map(int, ...) seem almost uniformly related to string parsing.)
map(int, ("42", 6.022, 2**32)) [42, 6, 4294967296L]
-- Michael Urman

On Jan 27, 2008 10:39 AM, Michael Urman <murman@gmail.com> wrote:
We can easily add docs to the Real ABC indicating that __trunc__ *should* implement a certain semantics, just like we do (or should do) for __add__ and everything else. While this doesn't provide a hard guarantee in the presence of non-conforming implementations, it's as good as it gets anywhere in Python. Given that __int__ may be implemented for things that aren't reals (like dates), it's much harder to prescribe what it *should* do.
To some it's pointless. To others there's a fine point to it.
Case (b) should be using index() instead. Most likely the code either predates index() or needs to be compatible with Python versions that don't have it, or the programmer wasn't aware of index(), which hasn't received a lot of publicity.
But there's a long-standing tradition that all numeric types in Python accept a string as argument. This was just added to decimal too.
That's an artificial example and hence it is impossible to derive the intent of the programmer. Heterogeneous lists are pretty rare. Let me give another artificial example. Suppose I have a need to implement floats and ints that print themselves in hex. I can make it so that this property is maintained across addition etc. without having to change the code that *uses* these numbers. But code that uses int() to convert a float to an int will lose the property. If that code used trunc() instead I can provide a __trunc__ on my hexfloat that returns a hexint. QED. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

"Michael Urman" <murman@gmail.com> wrote in message news:dcbbbb410801271039s4bf2b669ob919c53e43cb0cea@mail.gmail.com... | 2) The semantics of int() are fuzzy; even non-numeric types | (strings) are handled One could just as well say that the semantics of float() are fuzzy since it also handles strings. The actual claim seems to have been that the semantics of int(<float>) itself is fuzzy. This in turn seems to be based one of two ideas (I am not sure which) a. 'int' might either mean to some people 'some int associated with the float input' rather than the more precise 'the integer part of the float input', or b. that some people might think the latter means the same as the former. Possibility a is easy taken care of by documentation, especially when the behavior is what people expect. Possibility b conflicts with what I believe to be both the plain English meaning of 'integer part' and what I believe Americans, at least, learn in elementary school. Moreover, I challenge the notion that 'truncate' is unambiguous. It simply means to cut off, without specifying how much. '3.141' is pi truncated to 3 decimal places. Indeed, it would not be unreasonable to expect trunc() to have a second parameter specifying where to cut. For one data point, I asked my bright 13-year-old for the 'integer part' of 3.1, 3.9, -3.1, and -3.9 and got the expected 3,3,-3,-3 (as with int()). But asking 'truncate' the same numbers or even 'truncate toward zero' got a blank stare, which did not surprise me too much as it is not a common (American) English word. tjr

On Jan 27, 2008 3:37 PM, Terry Reedy <tjreedy@udel.edu> wrote:
Just like set(sequence) is the set associated with that sequence, not the set part of that sequence, and float('3.14') is the float associated with '3.14', not the float part of '3.14', etc. Type names do not normally retrieve pieces of other objects. When there's no unique or otherwise special instance of a given type associated with an instance of another type, the first should not take the second in its constructor. (This does not imply the inverse.) On the other hand, to retrieve a piece of another object, you do normally call a member on that object, so I'd have no objections to changing trunc() and round() (although the second isn't strictly retrieving a piece) to methods.
b. that some people might think the latter means the same as the former.
I don't understand this sentence. I haven't seen anyone advocating trunc() say that "some int associated with" means the same as "the integer part of".
Possibility a is easy taken care of by documentation, especially when the behavior is what people expect.
I'll grant that the behavior is relatively easily learned. It'll take some more evidence to convince me that it's what people expect, given that it _looks_ like a type conversion, and many systems implement that conversion by rounding.
You may understand the same thing from "int" and "integer part", but that's a learned association that I didn't have before either you or Raymond brought it up. Among their other differences, "int" is not an English word.
No more unreasonable than round() having the same second parameter. And it would be reasonable to default it to "0" as round() does, since that's the only distinguished point in its range.
You asked a different question than the one we're talking about. You should have asked your 13-year-old what the "int" of 3.9 was, or, even better, how to convert 3.9 to an integer. For the first, you'd likely have gotten the same blank stare. For the second, I expect you'd have gotten either 4, or an objection that it's simply not an integer and can't be "converted" to one. -- Namasté, Jeffrey Yasskin

Jeffrey Yasskin wrote: [...]
[...] Surely the real issue here is that int() grew up purely as a conversion function, and metamorphosed into a type when the classic classes were moved into the background. A brief scan of the 2.4 library (the nearest to hand) shows no uses of int() without an argument in the top level modules. There's clearly no point calling int() with a literal integer argument, so its uses for conversion clearly dominate its use as a pure type constructor. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/

On Jan 27, 2008 10:43 AM, Guido van Rossum <guido@python.org> wrote:
If I'm following this discussion properly, the advantage of trunc() is that a Real-class-that-isn't-float can define a __trunc__ that can return an Integer-class-that-isn't-int, right? In that case, why not have the Real ABC grow trunc(), ceil(), floor(), and round() methods (replacing the __ varieties), and get rid of the builtins/math-module functions? x.trunc() is just as clear as trunc(x), and doesn't require a builtin. The syntax when used on float literals is ugly ("2.56 .round()"), but there's no use case for these methods on literals (just write "3"). 2to3 could handle this conversion pretty easily in most cases, so there won't be much breakage. -- Daniel Stutzbach, Ph.D. President, Stutzbach Enterprises LLC

Depends on what you compare to. Compared to int(), the advantage is that trunc() sends a clear message what its semantics is.
+1. Students just asked me why len() is not a method, and I didn't know a good answer; the same holds for many other builtins. This is a clear candidate for a method, IMO.
The syntax when used on float literals is ugly ("2.56 .round()"), but there's no use case for these methods on literals (just write "3").
Actually, it works fine for float literals: py> 2.45.round() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'float' object has no attribute 'round' It's only int literals which have a problem with methods, but then, you won't call any of these on an int literal, normally. Regards, Martin

On Sun, Jan 27, 2008, "Martin v. L?wis" wrote:
This is why len() is not a method: map(len, list_of_strings) What I don't know is to what extent this argument still holds in the presence of listcomps and genexps: [s.len() for s in list_of_strings] However, you still need ``len`` as a function to pass around as a callback in other cases where you need a generic function because the type of your data is not predetermined. In any event, I consider dropping len() from builtins to be gratuitous breakage, even in 3.0. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "All problems in computer science can be solved by another level of indirection." --Butler Lampson

On Jan 27, 2008 12:53 PM, Aahz <aahz@pythoncraft.com> wrote:
Or you just use ``lambda x: x.len()`` for the callback. And a built-in could easily be created that does what the lambda is doing. I know for me the reason I always thought the built-ins were there on top of using a method naming convention for operator overloading was so that a reasonable default could be provided by the built-in (e.g., iter() being able to work with an object that only provides __getitem__(), on top of its second argument allowing for better iteration control).
In any event, I consider dropping len() from builtins to be gratuitous breakage, even in 3.0.
I don't think anyone is proposing that. -Brett

On Jan 27, 2008 12:53 PM, Aahz <aahz@pythoncraft.com> wrote:
This is why len() is not a method:
map(len, list_of_strings)
I disagree with that explanation -- I couldn't know that when I made len() a function, because map() wasn't to become part of the language for another 5-6 years. My explanation is (a) I translated ABC's #x notation, and (b) I like the way it looks better. Your explanation is at best a handy side effect. :-) -- --Guido van Rossum (home page: http://www.python.org/~guido/)

This is why len() is not a method:
map(len, list_of_strings)
That's a good use case - but is that the reason? I would think that the true historical reason is that when len() was introduced, there weren't methods in the language. And even when support for methods was added, many types supporting len wouldn't easily support methods (e.g. the string type).
You don't *need* it for that; you could also pass around lambda x:x.len() It's clear that you need functions for a functional programming style. However, I would question that typical Python code is inherently functional, and instead I believe that it could just as well or better be object-oriented - it's just that Python mandates functions at certain places.
In any event, I consider dropping len() from builtins to be gratuitous breakage, even in 3.0.
I wouldn't want to propose removal of len(), no. However, I do think that adding more builtins (trunc in particular) is bad, especially when they make perfect methods. Regards, Martin

On Jan 28, 2008 1:54 PM, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Well, there were methods, but there were no "special" methods, and the length of a sequence was intended to be implemented in C. The very first version of the language (used internally at CWI for a while) didn't even have classic classes -- the only way to add a new type was to write a C extension.
I think that for certain things (e.g. len()) the functional notation is just more readable than the method notation, because it provides more information to the reader: len(x) guarantees to return an int. x.len() has no such guarantee, it could be an unrelated len method on an object that has nothing in common with sequences.
No, using trunc(x) makes it clear that the argument and return value are numbers. Using x.trunc() doesn't. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 28, 2008 4:00 PM, Guido van Rossum <guido@python.org> wrote:
No, using trunc(x) makes it clear that the argument and return value are numbers. Using x.trunc() doesn't.
How often do you expect someone to be looking at code where a trunc() method is being called and the variable could plausibly be both a number or something else? (a Google Code search for "def trunc(self)" lang:python returns 1 hit) How does the that additional value weigh against the cost of adding another builtin and trying to explain trunc() versus int() to new users? -- Daniel Stutzbach, Ph.D. President, Stutzbach Enterprises LLC

On Jan 28, 2008 2:28 PM, Daniel Stutzbach <daniel@stutzbachenterprises.com> wrote:
It's all pretty hypothetical at this point. I do think that the *concept* of trunc() is very easy to understand so the cost to the user of having to learn it (only when they encounter code that uses it or feel the need to use it themselves) is negligible. One thing I'm beginning to feel more and more strongly about is that round, trunc, ceil and floor all belong in the same category, and either should all be builtins or should all be in math. I should also admit that the 2-arg version of round() was borrowed from ABC, but the use case for it there doesn't map to Python: in ABC it's the only tool you have for floating point formatting on output, while in Python the same thing would typically be done with "%.2f" % x rather than round(x, 2). -- --Guido van Rossum (home page: http://www.python.org/~guido/)

No, using trunc(x) makes it clear that the argument and return value are numbers. Using x.trunc() doesn't.
Not sure where this is notion comes from. Terry Reedy's post provides a datapoint to the contrary. Besides, there is no problem along these lines that can't be cured by a better method name: f.integer_portion() Also, if you go the method route, then the API can easily be expanded to cover all the normal rounding methods: f.round_to_even() f.round_half_up() ... These are all specific and explicit. Also, we can take advantage of the ABC mixin capabilities to automatically provide all of these given one or two of them as primitives. Raymond P.S I get no shortage of hits for searches like: http://www.google.com/search?q=truncate+string

On Jan 27, 2008 11:54 AM, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Well, there's the generic functions line of thought, which isn't quite dead yet. And there's the idea that the built-in function can check that the result has a certain type, like len(), which insists on returning an int. But mostly it's because I find things like len(x), round(x) and cos(x) read more natural than method notation. It builds on a long tradition in math and applied math in programming language -- at least round() and cos() do, and so does trunc(). IOW it's a matter of aesthetics, and will never be explainable to everyone's satisfaction. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On 2008-01-27 08:14, Raymond Hettinger wrote:
All this reminds me a lot of discussions we've had when we needed a new way to spell out string.join(). In the end, we ended up adding a method to strings (thanks to Tim Peters, IIRC) instead of adding a builtin join(). Since all of the suggested builtins are only meant to work on floats, why not simply add methods for them to the float object ?! E.g. x = 3.141 print x.trunc(), x.floor(), x.ceil() etc. This approach also makes it possible to write types or classes that expose the same API without having to resort to new special methods (we have too many of those already). Please consider that type constructors have a different scope than helper functions. Helper functions should only be made builtins if they are really really useful and often needed. If they don't meet this criteria, they are better off in a separate module. I don't see any of the suggested helper functions meeting this criteria and we already have math.floor() and math.ceil(). -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jan 28 2008)
:::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,MacOSX for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611

In article <79990c6b0801250553l2e6247adudf48112436dcda70@mail.gmail.com>, "Paul Moore" <p.f.moore@gmail.com> wrote:
I like all of your suggestions except the last one. Remember the problem with a/b depending on whether b happened to be a float or an int? I think you'll be creating a very similar problem here. In my opinion int(foo) should do its best to turn foo into an int with *predictable* behavior. The least surprising behavior for int(float) is probably trunc(float). Personally I prefer round(float), but I doubt it is worth breaking code and retraining everybody. -- Russell

If the ambiguity is that 'int' behaviour is unspecified for floats - is it naive to suggest we specify the behaviour?
The concern is that whatever gets specified is arbitrary. There are many ways how an int can be constructed from a float, so why is any such way better than the others, and deserves to be the meaning of int()? Regards, Martin

""Martin v. Löwis"" <martin@v.loewis.de> wrote in message news:47992363.3010402@v.loewis.de... |> If the ambiguity is that 'int' behaviour is unspecified for floats - is | > it naive to suggest we specify the behaviour? | | The concern is that whatever gets specified is arbitrary. There are many | ways how an int can be constructed from a float, so why is any such way | better than the others, and deserves to be the meaning of int()? Decades of usage, in English, with the meaning it already has in Python.

In article <47992363.3010402@v.loewis.de>, "Martin v. Lowis" <martin@v.loewis.de> wrote:
But something should be specified. Users should be able to expect consistent behavior. Surely there must be some efficiency reason why it is not specified (e.g. it uses a low-level C call that is not specified)??? I agree with the idea of putting trunc in the math library since it seems to similar to floor. -- Russell

On Jan 24, 2008 1:11 PM, Raymond Hettinger <python@rcn.com> wrote:
One of my goals for trunc() is to replace the from-float use of int(), even though we can't remove it for backward-compatibility reasons. PEP 3141 says: "Because the int() conversion implemented by float (and by decimal.Decimal) is equivalent to but less explicit than trunc(), let's remove it. (Or, if that breaks too much, just add a deprecation warning.)" That needs to be updated and implemented. I think the decision was that removing float.__int__() would break too much, so it needs a deprecation warning in 3.0. int has to be a builtin because it's a fundamental type. trunc() followed round() into the builtins. I have no opinion on whether ceil and floor should move there; it probably depends on how often they're used. -- Namasté, Jeffrey Yasskin

On Jan 24, 2008 3:32 PM, Michael Foord <fuzzyman@voidspace.org.uk> wrote:
Yes, you can, but you have to specify how you want it done by using trunc() or round() or ceil() or floor(). (In 3.0, round(x) will return an int, not a float.) -- --Guido van Rossum (home page: http://www.python.org/~guido/)

2008/1/24, Guido van Rossum <guido@python.org>:
2008/1/24, Jeffrey Yasskin <jyasskin@gmail.com>:
What I understand here is as int() is "ambiguous", in the future if you want to specify how you want to convert a float to int. But ceil and floor returns a float. And round and trunc will return an int. So, how I could convert a float to its upper int? Like this?:
trunc(math.ceil(.3)) 1
BTW, int is not giving me a deprecation warning:
int(.1) 0
Thanks! -- . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/

On Jan 25, 2008 4:28 AM, Facundo Batista <facundobatista@gmail.com> wrote:
Like this, in 3.0:
math.ceil(2.2) 3
There was a previous thread in which we decided not to change that behavior in 2.6.
Correct; that's not implemented yet. -- Namasté, Jeffrey Yasskin http://jeffrey.yasskin.info/

Jeffrey Yasskin wrote:
It seems strange to me that a release that has the avowed intention of producing a "more rational" language by breaking backwards compatibility should start out with deprecation warnings of this type. What's next: "This isn't Perl" when someone tries to add a number and a string? Surely the place to raise warnings is in 2to3. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/

On 24/01/2008, Jeffrey Yasskin <jyasskin@gmail.com> wrote:
Suggestion: - int() has to stay in builtins for obvious reasons. - put *all* of trunc, ceil, floor, round into math. - make int(float) an error The only fly in the ointment is that 2to3 can't handle the semantic issues around converting int(n) to math.trunc(n) because it can't detect the type of n. So why not make int(float) warn in -3 mode on 2.6, and then let 2to3 do the conversion (on the basis that 2to3 should only be run on code that is -3 warning free)? Did I miss a requirement here? Paul.

2008/1/25, Paul Moore <p.f.moore@gmail.com>:
- int() has to stay in builtins for obvious reasons.
+1
- put *all* of trunc, ceil, floor, round into math.
+1
- make int(float) an error
-0 (you should be able to convert between builtin datatypes without the use of a module). +1 to keep it and define exactly the behaviour, and point to math module in the documentation if you want better control over the conversion. Regards, -- . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/

Paul Moore wrote:
Slightly different suggestion: - define int(float) as int(trunc(float)) In my humble opinion lots of people expect int(-2.5) == -2 and int(2.5) == 2. Or in other words: int(float) chops of the digits after the dot. As far as I can see float.__int__ already behaves like int(trunc(float))
2.4 ( 2, 3, 2, 2, 2) 2.6 ( 2, 3, 3, 2, 2) -2.4 (-3, -2, -2, -2, -2) -2.6 (-3, -2, -3, -2, -2) Python 2.6: 2.4 ( 2.0, 3.0, 2.0, 2, 2) 2.6 ( 2.0, 3.0, 3.0, 2, 2) -2.4 (-3.0, -2.0, -2.0, -2, -2) -2.6 (-3.0, -2.0, -3.0, -2, -2) Christian

Christian Heimes wrote:
FWIW, this approach gets a +1 from me (although I'm -0 on taking round() out of builtins - that seems like a pointless incompatibility to me). Yes, blessing 'trunc' as the default mechanism for converting a non-integral number to an integer is somewhat arbitrary, but it's a piece of arbitrariness with a very long history behind it. For that matter, we could even formally define int() as falling back to trunc() if there is no direct conversion (i.e. int knows about the type natively, or the type provides an __int__ method). That way non-integral types could just implement __trunc__ without having to worry about adding "__int__ = __trunc__" to the class definition. We would still have operator.index to identify types which can be losslessly converted to a builtin integer. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org

[I am still recovering, so if I say something totally misinformed I blame my recovery. :) ] -On [20080125 15:12], Christian Heimes (lists@cheimes.de) wrote:
Am I the only one who wonders about the sudden change in decimal significance? Especially given the fact that the ISO C standard specifies floor(), for example, as returning a floating point value and the above in Python 3.0 deviates to just returning an integer. Which is also different from 2.5's behaviour. Can I assume we are all familiar with the concept of significant digits and that we agree that from this point of view 2 != 2.0? And that results such as the above would be a regression and loss in precision? -- Jeroen Ruigrok van der Werven <asmodai(-at-)in-nomine.org> / asmodai イェルーン ラウフロック ヴァン デル ウェルヴェン http://www.in-nomine.org/ | http://www.rangaku.org/ We have met the enemy and they are ours...

Jeroen Ruigrok van der Werven wrote:
Not really, because if someone cares about precision to that extent they won't be using binary floats anyway (they'll be using Decimal, or something like it). Besides, for trunc, ceil and floor, it's all zeroes after the decimal point by definition (no matter how many significant digits you started with). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org

On Jan 25, 2008 5:53 AM, Paul Moore <p.f.moore@gmail.com> wrote:
I'd deal with Facundo's objection that you should be able to convert between builtin datatypes without the use of a module by leaving trunc in the builtins, but it looks like I'm in the minority here. If the decision comes to be that int(float) should be blessed as a correct way to truncate a float, I'd agree with Raymond that trunc() is just duplication and should be eliminated. I'd, of course, rather have a spelling that says what it means. :)
I'd be happy with that too. -- Namasté, Jeffrey Yasskin

2008/1/25, Jeffrey Yasskin <jyasskin@gmail.com>:
Mmm... no. int() is a builtin way to transform the builtin data type float into the builtin data type float. There's no "correct" way for a float to become an integer, but in the math module you have several ways to do it (floor, ceil, round, trunc, choose the one that you want, but you're "notified" <wink/2> that there're different ways to do it). -- . Facundo Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/

On Jan 25, 2008 9:45 AM, Facundo Batista <facundobatista@gmail.com> wrote:
I assume you meant "int" instead of your second "float". There doesn't have to be a way to convert from every builtin type to every other builtin type. Take dict(float), for example. Further, if there is a conversion, no law says it has to be named the same as the target type. If there are two plausible ways to get from the source to the target, using just the target's name to pick out one of them is really a terrible idea. But, just because the designers of C made a bad decision that we've inherited, doesn't mean that it's a terrible idea to keep it. As Nick said, defining int(float) as truncation has a long history, and there are some obvious costs to breaking away from that. I think it is time to correct the mistake, but it's not an open and shut case. -- Namasté, Jeffrey Yasskin

On Jan 25, 2008 12:45 PM, Facundo Batista <facundobatista@gmail.com> wrote:
In keeping with this theme, why not define int() for floats (and other real types) as def __int__(n, method=math.trunc) or something similar, so that, by default, int() provides the same functionality as before (or whatever is decided to be preferred, I'm making no judgements on that end), but has a way --- by passing a different function --- of changing the way it rounds? The other (probably preferred) option, I suppose, would be to provide a few constants (float.FLOOR_METHOD et al.) instead of passing an arbitrary function (which, of course, makes me a bit uncomfortable). -- Cheers, Leif

Paul Moore schrieb:
That, and making int(float) == int(trunc(float)) seems like reasonable behavior to me as a person not involved in the discussion. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

-On [20080126 09:43], Georg Brandl (g.brandl@gmx.net) wrote:
+1 from my side for the above. -- Jeroen Ruigrok van der Werven <asmodai(-at-)in-nomine.org> / asmodai イェルーン ラウフロック ヴァン デル ウェルヴェン http://www.in-nomine.org/ | http://www.rangaku.org/ We have met the enemy and they are ours...

On Jan 26, 2008 12:43 AM, Georg Brandl <g.brandl@gmx.net> wrote:
That, and making int(float) == int(trunc(float)) seems like reasonable behavior to me as a person not involved in the discussion.
That's the only position in this discussion that I don't understand. Although int() and trunc() would still have very slightly different behaviors, they seem too close together (identical on most common types) to be compatible with "only one way to do it". I've been advocating trunc() under the assumption that int(float) would be deprecated and eliminated as soon as practical (with an error message similar to float(complex)). Could one of the people in favor of keeping both explain why they think that's a good idea? -- Namasté, Jeffrey Yasskin

On Jan 26, 2008 2:53 PM, Raymond Hettinger <python@rcn.com> wrote: [Jeffrey]
We'll see. Jeffrey did say "deprecated" and "as soon as practical". A -3 warning in 2.6 could do wonders.
This position is totally nuts.
Hold it right there. You may disagree, but that doesn't make it nuts. There is actually quite an important signal to the reader that is present when you see trunc(x) but absent when you see int(x): with trunc(x), the implication is that x is a (Real) number. With int(x), you can make no such assumption -- x could be a string, or it could be a totally different type that happens to define __int__, perhaps a custom date/time type (I believe mxDateTime supports this).
There is *nothing* wrong with int() as it stands now. Nobody has problems with it. The tools is ubiquitous in other languages,
Talk to C++ experts. They have a huge beef with C's confusing use of casts for two different purposes: reinterpretation of the same bit pattern vs. value conversion. Python problem isn't quite the same, but I still really wish I had followed Pascal's lead instead of C's here: Pascal requires you to use trunc() to convert a real to an integer. (BTW Pascal also had the division operator right, unlike C, and we're finally fixing this in Py3k by following Pascal's nearly-40-year-old lead.) If we had done it that way, we wouldn't have had to introduce the index() builtin and the corresponding infrastructure (__index__ and a whole slew of C APIs).
spreadsheets, and calculators.
I don't think that Excel should be held up as a shining example for Python. At least, that would be quite a change. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

. You may disagree, but that doesn't make it nuts.
Too many thoughts compressed into one adjective ;-) Deprecating int(float)-->int may not be nuts, but it is disruptive. Having both trunc() and int() in Py2.6 may not be nuts, but it is duplicative and confusing. The original impetus for facilitating a new Real type being able to trunc() into a new Integral type may not be nuts, but the use case seems far fetched (we're never had a feature request for it -- the notion was born entirely out of numeric tower considerations). The idea that programmers are confused by int(3.7)-->3 may not be nuts, but it doesn't match any experience I've had with any programmer, ever. The idea that trunc() is beneficial may not be nuts, but it is certainly questionable. In short, the idea may not be nuts, but I think it is legitimate to suggest that it is unnecessary and that it will do more harm than good.
I don't think that Excel should be held up as a shining example for Python.
It is simply a datapoint. We don't care why they chose int() for truncation. The important thing is the subsequent user experiences which indicates that people do not have a problem with it. When it comes to int() and round(), people just get it. I've taught spreadsheets to a lot people and no one has ever construed int() as meaning round(). What was your experience with 18 years of python? Was it a recurring a point of confusion? On the newsgroup, helplist, and tutorial list, I never seen this come up as problem. I looked in other places for corroboration. My HP-12C has intg and frac buttons. In BASIC, int() means to return the integer part of a floating point number. The surprise find was that int() in Matlab and Maple means integrate :-) FWIW, Perl's int() does what ours currently does. One other thought: In Python, it has been nice that we have simple type coercions using the type name: * set(p)-->q can accept a list, tuple, string, or any iterable and make a set * int(p)-->q can accept an int, long, float, or string and make an int * float(p)-->q can accept an int, long, float, or string and make an int * list(p)-->q can accept a list, tuple, string, or any iterable and make a list * unicode(p)--> can accept a str, buffer, or unicode object and make a unicode object It's a bit weird to decide that int() needs to lose that capability so we get generalized Integrals as output. What's wrong with coercion to a concrete type? Raymond

On Jan 26, 2008 11:14 PM, Raymond Hettinger <python@rcn.com> wrote:
The idea that programmers are confused by int(3.7)-->3 may not be nuts, but it doesn't match any experience I've had with any programmer, ever.
In C, I'm pretty sure I've seen people write (long)x where they'd have been better off with lround(x). They know that the cast truncates, and generally that they actually wanted to round, but the type they wanted to get to was foremost in their mind, so they didn't bother to think about it a little and write what they really wanted. It's not that they're confused; it's that they're accepting a default that shouldn't be a default. Your other points seem to have been answered already, although people will disagree on how compelling the answers are, so I won't repeat them here. -- Namasté, Jeffrey Yasskin

Raymond Hettinger wrote:
The idea that programmers are confused by int(3.7)-->3 may not be nuts, but it doesn't match any experience I've had with any programmer, ever.
You haven't been doing newbie support in #python lately. Statements like Python is rounding wrong are common. ;) It's astonishing how many major rounding and division bugs are found by newbies. Christian

On Jan 26, 2008 11:14 PM, Raymond Hettinger <python@rcn.com> wrote:
You're beginning to repeat your argument; none of that was new.
Let me get back to you on that. I first want to point out that you snipped the core of my argument in the post you otherwise quoted. I will repeat it here because I would like your explicit reaction: [Guido]
Can I assume that you agree with this? That would be progress. Coming back to your argument that other types have a constructor that takes all kinds of arguments, I'd like to point out that they all have restrictions too (except for str()): list() and tuple() only accept iterables, float() only accepts strings and certain numbers (not complex), and so on. Taking the latter as an example:
I think that (eventually) int(0.0) could do something similar:
But see below. Yet another (minor) argument that has always made me uncomfortable with int() == trunc(): the % operator. I think it's a big improvement over C that Python's % operator is defined as x%y == x - y*floor(x/y) # where / is real division rather than C's division, which uses trunc() instead of floor(). In C this nicely meshes with the definition of int(): you can define x%y as x - y*(int)(x/y); but not so in Python. I don't want to use this as an argument for defining int(x) as floor(x), but I do want to point out that it has always given me a twinge of discomfort. FInally, there's the "one way" argument. That's a nice slogan, but doesn't really hold anyways in practice. To copy a list, we can write either L[:] or list(L). To get the keys of a dict, we can write either D.keys() or list(D). To convert a number to a string we can write either "%g" % X or str(X). For octal we can write "%#o" % X or oct(X). To convert a unicode string to UTF-8, we can write either U.encode("utf8") or str(U, "utf8"). And so on. In many cases, these notations aren't exactly the same in semantics (e.g. list(X) always returns a list, X[:] returns whatever sequence type X is), but nevertheless they have large areas of overlap. This is how I see trunc() and int() live together. After all that, here's my current proposal: - Deprecating int(<float>) is pretty radical, I think it would have to happen in the distant future. OR not at all. I'm at best +0 on this, more like exactly 0. I realize that in practice this kills the idea. The "purist" argument for it would have worked better if it was made 18 years ago. - trunc(), round(), floor() and ceil() should all be built-ins, corresponding to __trunc__, __round__, __floor__ and __ceil__. Then we have the four standard ways to go from Reals to Integers, which are properly extensible for folks who write their own number types. (We can't control how folks implement __round__, but we can document expected behavior -- that's how we treat __add__ and all other operators too, after all.) - In the docs (especially for beginners) we recommend that users write trunc() instead of int(), emphasizing that trunc() ensures the argument is a number, while suggesting int(x) for conversion from strings. But the implementation won't chastise users by issuing annoying warnings. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 27, 2008 5:43 PM, Guido van Rossum <guido@python.org> wrote:
Also what happens with "%i" % 3.14 ? We incidentally found a problem with a script using python 2.5 because apparently the "%" formatting operator doesn't use "int()" for doing the conversion (to be more specific if the float is too large for a 32-bit integer then the format operator chokes while the int() operator returns a long). Anyway I want just to say that if "implicit" conversion from float to integer goes away then what happens to formatting conversion ? Removing that too IMO would break a lot of code and it's IMO very difficult to help fixing that. Andrea

On Jan 27, 2008 9:26 AM, Andrea Griffini <agriff@tin.it> wrote:
Well, it seems like it would be as easy to make some formatting conversions raise a warning on float inputs as it would be to make int() do it. But I think you're safe here; it doesn't look like either will be deprecated. -- Namasté, Jeffrey Yasskin http://jeffrey.yasskin.info/

On Jan 27, 2008 9:26 AM, Andrea Griffini <agriff@tin.it> wrote:
That's quite a separate issue. Please ses http://bugs.python.org/issue1742669.
The formatting code could assign specific meanings. I suspect though that it was never meant to be possible to use %d with a float -- it just is one of the artifacts of using implicit conversion, and one not well-thought through. Note:
I think the latter is wrong and confusing. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

I agree that it provides a cue that that input is Real. I don't agree that that has any value. We had a lot of tools that can accept multiple input types. For instance, float() can accept a string or number. We could do like C and split-out the atof() functionality but that just makes two functions where one would suffice.
It hasn't bugged me much, but I do understand.
"pretty radical" is what I meant when I said "it's nuts" ;-) This is a major piece of progress. Most of the prior proposal was predicated on int(<float>) being deprecated.
ISTM, you really don't need trunc() in this mix. The ceil() and floor() functions do an excellent job of convering most use cases. The overlap of trunc() and int() just isn't worth it. There's the mental cost of having to explain the difference on day one to every beginner. There's the back end cost of every class that has __int__ also needing to decide whether to provide __trunc__ which will typically be the same thing. In time, I believe it will become self-evident that having both int() and trunc() is a wart. If trunc() goes into 2.6 and 3.0, then we're going to have to live with this for a long time. Purity may suggest that you need trunc(). Practicality says it isn't worth the cost. At least we've made important progress by saving int(<float>). Raymond

On Jan 27, 2008 10:29 AM, Raymond Hettinger <python@rcn.com> wrote:
All I claim is that it has *some* value for me. If it doesn't have value for you, that doesn't make it worthless.
Progress.
Good.
Not really. You'd have to use an if predicated on the sign to emulate trunc() using ceil() and floor(). Or something like sign(x) * floor(abs(x)) -- but we don't have sign().
I don't see much cost for end users at all. It's not like trunc() represents a difficult concept. Having trunc() in addition to ceil(), floor() and round() creates a nicely complete set of float->int conversions. Then int(<float>) can be defined by deferring to trunc() -- as opposed to round(). My proposal stands (less any talk of deprecation of int(<float>)). -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 27, 2008 10:54 AM, Raymond Hettinger <python@rcn.com> wrote:
A single type wouln't need both. But int() should still try both, because it doesn't make sense for e.g. a date type to have to define __trunc__ for conversion to an int. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 27, 2008 12:12 PM, Raymond Hettinger <python@rcn.com> wrote:
When imported from math, yes, for 2.5 compatibility. If we add builtins, the builtins won't have this restriction. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Is this a valid summary of the arguments so far? I see two arguments for the change: 1) The semantics of trunc() are clear: it maps R -> Z in a specific fashion 2) The semantics of int() are fuzzy; even non-numeric types (strings) are handled Yet there will be a __trunc__ that will allow any chosen mapping to be implemented, so long as it results in an integer, so (1) is only guaranteed true for the builtin types. This leaves us with (2) which seems strongly tied to string parsing (as __index__ resolved the other common X -> integer case). I see one main argument against: *) trunc() results in duplication at best, zealous deprecation at worst Given that the deprecation or removal of int(2.3) has been dropped, the argument is about pointless duplication. What problem is trunc() supposed to solve if it does to floats what int() does now? I've done some initial code searches for: lang:python "int(", and I've seen three primary uses for calling int(x): a) parsing strings, such as from a user, a config file, or other serialized format b) limiting the input to a function to an integer, such as in a calendar trying to ensure it has integer months c) truncation, such as throwing away sub-seconds from time.time(), or ensuring integer results from division It's unclear to me whether (b) could be better served by more type-specific operations that would prevent passing in strings, or whether uses like (c) often have latent bugs due to truncation instead of rounding. If trunc() is to clarify round vs. integer-portion, it's something people learn -- the general lack of comments in (c) usages indicates nobody considers it special behavior. If it's to clarify the argument's type (the parsing of strings vs. getting integers from other numeric types), would separating parsing from the int (and float) constructors also solve this? Is the aim to "clean up" the following fake example? (Real world uses of map(int, ...) seem almost uniformly related to string parsing.)
map(int, ("42", 6.022, 2**32)) [42, 6, 4294967296L]
-- Michael Urman

On Jan 27, 2008 10:39 AM, Michael Urman <murman@gmail.com> wrote:
We can easily add docs to the Real ABC indicating that __trunc__ *should* implement a certain semantics, just like we do (or should do) for __add__ and everything else. While this doesn't provide a hard guarantee in the presence of non-conforming implementations, it's as good as it gets anywhere in Python. Given that __int__ may be implemented for things that aren't reals (like dates), it's much harder to prescribe what it *should* do.
To some it's pointless. To others there's a fine point to it.
Case (b) should be using index() instead. Most likely the code either predates index() or needs to be compatible with Python versions that don't have it, or the programmer wasn't aware of index(), which hasn't received a lot of publicity.
But there's a long-standing tradition that all numeric types in Python accept a string as argument. This was just added to decimal too.
That's an artificial example and hence it is impossible to derive the intent of the programmer. Heterogeneous lists are pretty rare. Let me give another artificial example. Suppose I have a need to implement floats and ints that print themselves in hex. I can make it so that this property is maintained across addition etc. without having to change the code that *uses* these numbers. But code that uses int() to convert a float to an int will lose the property. If that code used trunc() instead I can provide a __trunc__ on my hexfloat that returns a hexint. QED. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

"Michael Urman" <murman@gmail.com> wrote in message news:dcbbbb410801271039s4bf2b669ob919c53e43cb0cea@mail.gmail.com... | 2) The semantics of int() are fuzzy; even non-numeric types | (strings) are handled One could just as well say that the semantics of float() are fuzzy since it also handles strings. The actual claim seems to have been that the semantics of int(<float>) itself is fuzzy. This in turn seems to be based one of two ideas (I am not sure which) a. 'int' might either mean to some people 'some int associated with the float input' rather than the more precise 'the integer part of the float input', or b. that some people might think the latter means the same as the former. Possibility a is easy taken care of by documentation, especially when the behavior is what people expect. Possibility b conflicts with what I believe to be both the plain English meaning of 'integer part' and what I believe Americans, at least, learn in elementary school. Moreover, I challenge the notion that 'truncate' is unambiguous. It simply means to cut off, without specifying how much. '3.141' is pi truncated to 3 decimal places. Indeed, it would not be unreasonable to expect trunc() to have a second parameter specifying where to cut. For one data point, I asked my bright 13-year-old for the 'integer part' of 3.1, 3.9, -3.1, and -3.9 and got the expected 3,3,-3,-3 (as with int()). But asking 'truncate' the same numbers or even 'truncate toward zero' got a blank stare, which did not surprise me too much as it is not a common (American) English word. tjr

On Jan 27, 2008 3:37 PM, Terry Reedy <tjreedy@udel.edu> wrote:
Just like set(sequence) is the set associated with that sequence, not the set part of that sequence, and float('3.14') is the float associated with '3.14', not the float part of '3.14', etc. Type names do not normally retrieve pieces of other objects. When there's no unique or otherwise special instance of a given type associated with an instance of another type, the first should not take the second in its constructor. (This does not imply the inverse.) On the other hand, to retrieve a piece of another object, you do normally call a member on that object, so I'd have no objections to changing trunc() and round() (although the second isn't strictly retrieving a piece) to methods.
b. that some people might think the latter means the same as the former.
I don't understand this sentence. I haven't seen anyone advocating trunc() say that "some int associated with" means the same as "the integer part of".
Possibility a is easy taken care of by documentation, especially when the behavior is what people expect.
I'll grant that the behavior is relatively easily learned. It'll take some more evidence to convince me that it's what people expect, given that it _looks_ like a type conversion, and many systems implement that conversion by rounding.
You may understand the same thing from "int" and "integer part", but that's a learned association that I didn't have before either you or Raymond brought it up. Among their other differences, "int" is not an English word.
No more unreasonable than round() having the same second parameter. And it would be reasonable to default it to "0" as round() does, since that's the only distinguished point in its range.
You asked a different question than the one we're talking about. You should have asked your 13-year-old what the "int" of 3.9 was, or, even better, how to convert 3.9 to an integer. For the first, you'd likely have gotten the same blank stare. For the second, I expect you'd have gotten either 4, or an objection that it's simply not an integer and can't be "converted" to one. -- Namasté, Jeffrey Yasskin

Jeffrey Yasskin wrote: [...]
[...] Surely the real issue here is that int() grew up purely as a conversion function, and metamorphosed into a type when the classic classes were moved into the background. A brief scan of the 2.4 library (the nearest to hand) shows no uses of int() without an argument in the top level modules. There's clearly no point calling int() with a literal integer argument, so its uses for conversion clearly dominate its use as a pure type constructor. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/

On Jan 27, 2008 10:43 AM, Guido van Rossum <guido@python.org> wrote:
If I'm following this discussion properly, the advantage of trunc() is that a Real-class-that-isn't-float can define a __trunc__ that can return an Integer-class-that-isn't-int, right? In that case, why not have the Real ABC grow trunc(), ceil(), floor(), and round() methods (replacing the __ varieties), and get rid of the builtins/math-module functions? x.trunc() is just as clear as trunc(x), and doesn't require a builtin. The syntax when used on float literals is ugly ("2.56 .round()"), but there's no use case for these methods on literals (just write "3"). 2to3 could handle this conversion pretty easily in most cases, so there won't be much breakage. -- Daniel Stutzbach, Ph.D. President, Stutzbach Enterprises LLC

Depends on what you compare to. Compared to int(), the advantage is that trunc() sends a clear message what its semantics is.
+1. Students just asked me why len() is not a method, and I didn't know a good answer; the same holds for many other builtins. This is a clear candidate for a method, IMO.
The syntax when used on float literals is ugly ("2.56 .round()"), but there's no use case for these methods on literals (just write "3").
Actually, it works fine for float literals: py> 2.45.round() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'float' object has no attribute 'round' It's only int literals which have a problem with methods, but then, you won't call any of these on an int literal, normally. Regards, Martin

On Sun, Jan 27, 2008, "Martin v. L?wis" wrote:
This is why len() is not a method: map(len, list_of_strings) What I don't know is to what extent this argument still holds in the presence of listcomps and genexps: [s.len() for s in list_of_strings] However, you still need ``len`` as a function to pass around as a callback in other cases where you need a generic function because the type of your data is not predetermined. In any event, I consider dropping len() from builtins to be gratuitous breakage, even in 3.0. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "All problems in computer science can be solved by another level of indirection." --Butler Lampson

On Jan 27, 2008 12:53 PM, Aahz <aahz@pythoncraft.com> wrote:
Or you just use ``lambda x: x.len()`` for the callback. And a built-in could easily be created that does what the lambda is doing. I know for me the reason I always thought the built-ins were there on top of using a method naming convention for operator overloading was so that a reasonable default could be provided by the built-in (e.g., iter() being able to work with an object that only provides __getitem__(), on top of its second argument allowing for better iteration control).
In any event, I consider dropping len() from builtins to be gratuitous breakage, even in 3.0.
I don't think anyone is proposing that. -Brett

On Jan 27, 2008 12:53 PM, Aahz <aahz@pythoncraft.com> wrote:
This is why len() is not a method:
map(len, list_of_strings)
I disagree with that explanation -- I couldn't know that when I made len() a function, because map() wasn't to become part of the language for another 5-6 years. My explanation is (a) I translated ABC's #x notation, and (b) I like the way it looks better. Your explanation is at best a handy side effect. :-) -- --Guido van Rossum (home page: http://www.python.org/~guido/)

This is why len() is not a method:
map(len, list_of_strings)
That's a good use case - but is that the reason? I would think that the true historical reason is that when len() was introduced, there weren't methods in the language. And even when support for methods was added, many types supporting len wouldn't easily support methods (e.g. the string type).
You don't *need* it for that; you could also pass around lambda x:x.len() It's clear that you need functions for a functional programming style. However, I would question that typical Python code is inherently functional, and instead I believe that it could just as well or better be object-oriented - it's just that Python mandates functions at certain places.
In any event, I consider dropping len() from builtins to be gratuitous breakage, even in 3.0.
I wouldn't want to propose removal of len(), no. However, I do think that adding more builtins (trunc in particular) is bad, especially when they make perfect methods. Regards, Martin

On Jan 28, 2008 1:54 PM, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Well, there were methods, but there were no "special" methods, and the length of a sequence was intended to be implemented in C. The very first version of the language (used internally at CWI for a while) didn't even have classic classes -- the only way to add a new type was to write a C extension.
I think that for certain things (e.g. len()) the functional notation is just more readable than the method notation, because it provides more information to the reader: len(x) guarantees to return an int. x.len() has no such guarantee, it could be an unrelated len method on an object that has nothing in common with sequences.
No, using trunc(x) makes it clear that the argument and return value are numbers. Using x.trunc() doesn't. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Jan 28, 2008 4:00 PM, Guido van Rossum <guido@python.org> wrote:
No, using trunc(x) makes it clear that the argument and return value are numbers. Using x.trunc() doesn't.
How often do you expect someone to be looking at code where a trunc() method is being called and the variable could plausibly be both a number or something else? (a Google Code search for "def trunc(self)" lang:python returns 1 hit) How does the that additional value weigh against the cost of adding another builtin and trying to explain trunc() versus int() to new users? -- Daniel Stutzbach, Ph.D. President, Stutzbach Enterprises LLC

On Jan 28, 2008 2:28 PM, Daniel Stutzbach <daniel@stutzbachenterprises.com> wrote:
It's all pretty hypothetical at this point. I do think that the *concept* of trunc() is very easy to understand so the cost to the user of having to learn it (only when they encounter code that uses it or feel the need to use it themselves) is negligible. One thing I'm beginning to feel more and more strongly about is that round, trunc, ceil and floor all belong in the same category, and either should all be builtins or should all be in math. I should also admit that the 2-arg version of round() was borrowed from ABC, but the use case for it there doesn't map to Python: in ABC it's the only tool you have for floating point formatting on output, while in Python the same thing would typically be done with "%.2f" % x rather than round(x, 2). -- --Guido van Rossum (home page: http://www.python.org/~guido/)

No, using trunc(x) makes it clear that the argument and return value are numbers. Using x.trunc() doesn't.
Not sure where this is notion comes from. Terry Reedy's post provides a datapoint to the contrary. Besides, there is no problem along these lines that can't be cured by a better method name: f.integer_portion() Also, if you go the method route, then the API can easily be expanded to cover all the normal rounding methods: f.round_to_even() f.round_half_up() ... These are all specific and explicit. Also, we can take advantage of the ABC mixin capabilities to automatically provide all of these given one or two of them as primitives. Raymond P.S I get no shortage of hits for searches like: http://www.google.com/search?q=truncate+string

On Jan 27, 2008 11:54 AM, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Well, there's the generic functions line of thought, which isn't quite dead yet. And there's the idea that the built-in function can check that the result has a certain type, like len(), which insists on returning an int. But mostly it's because I find things like len(x), round(x) and cos(x) read more natural than method notation. It builds on a long tradition in math and applied math in programming language -- at least round() and cos() do, and so does trunc(). IOW it's a matter of aesthetics, and will never be explainable to everyone's satisfaction. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On 2008-01-27 08:14, Raymond Hettinger wrote:
All this reminds me a lot of discussions we've had when we needed a new way to spell out string.join(). In the end, we ended up adding a method to strings (thanks to Tim Peters, IIRC) instead of adding a builtin join(). Since all of the suggested builtins are only meant to work on floats, why not simply add methods for them to the float object ?! E.g. x = 3.141 print x.trunc(), x.floor(), x.ceil() etc. This approach also makes it possible to write types or classes that expose the same API without having to resort to new special methods (we have too many of those already). Please consider that type constructors have a different scope than helper functions. Helper functions should only be made builtins if they are really really useful and often needed. If they don't meet this criteria, they are better off in a separate module. I don't see any of the suggested helper functions meeting this criteria and we already have math.floor() and math.ceil(). -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jan 28 2008)
:::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,MacOSX for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611

In article <79990c6b0801250553l2e6247adudf48112436dcda70@mail.gmail.com>, "Paul Moore" <p.f.moore@gmail.com> wrote:
I like all of your suggestions except the last one. Remember the problem with a/b depending on whether b happened to be a float or an int? I think you'll be creating a very similar problem here. In my opinion int(foo) should do its best to turn foo into an int with *predictable* behavior. The least surprising behavior for int(float) is probably trunc(float). Personally I prefer round(float), but I doubt it is worth breaking code and retraining everybody. -- Russell
participants (22)
-
"Martin v. Löwis"
-
Aahz
-
Andrea Griffini
-
Brett Cannon
-
Christian Heimes
-
Daniel Stutzbach
-
Eric Smith
-
Facundo Batista
-
Georg Brandl
-
Guido van Rossum
-
Jeffrey Yasskin
-
Jeroen Ruigrok van der Werven
-
Leif Walsh
-
M.-A. Lemburg
-
Michael Foord
-
Michael Urman
-
Nick Coghlan
-
Paul Moore
-
Raymond Hettinger
-
Russell E. Owen
-
Steve Holden
-
Terry Reedy