A false midnight
A false midnight
Posted Mar 13, 2014 22:51 UTC (Thu) by kokada (guest, #92849)In reply to: A false midnight by pboddie
Parent article: A false midnight
One gotcha that I had with Python 2.x is the way it handles divisions. If you try the following code on a Python 2.x interpreter:
3/4
You'd get:
0
While with Python 3.x you get:
0.75
Of course, the first example is C-like, while the second is what you'd expect from a non-typed language. To get the same result on Python 2.x you'd have to write:
3.0/4
Even this doesn't work:
float(3/4) #since the 3/4 is evaluated before the conversion
This actually made me waste 20 minutes debugging a program because the error just doesn't made any sense to me. I think this is the same gotcha as the article shows.
Posted Mar 14, 2014 10:28 UTC (Fri)
by khim (subscriber, #9252)
[Link] (1 responses)
Posted Mar 14, 2014 16:09 UTC (Fri)
by hummassa (subscriber, #307)
[Link]
Posted Mar 14, 2014 18:38 UTC (Fri)
by pboddie (guest, #50784)
[Link] (2 responses)
As is pointed out already here, integer division was "fixed" with the "future" division feature - the origin of "from future" if I remember correctly - and was one of the original "Python warts" that was fixed in Python 2, although I did argue for keeping thing as they are/were and adding a new operator to produce the "expected" result. (It is only "expected" depending on context and on the user's background, of course, and I remember pointing out that the case for the change could have been made more concisely and persuasively.)
Posted Mar 16, 2014 15:31 UTC (Sun)
by kokada (guest, #92849)
[Link] (1 responses)
Posted Mar 18, 2014 20:19 UTC (Tue)
by pboddie (guest, #50784)
[Link]
I was explicitly responding to the assertion... ...which, of course, it isn't. I'm very familiar with general complaints about Python - and more specifically, Python 2 - but most of those are regarded as concerning widely known and accepted, if not widely liked, behaviour. The topic of the article concerns something that divides the opinions even of those developing Python 3 as to whether the behaviour should be fixed or is even excusable.
Posted Mar 17, 2014 13:07 UTC (Mon)
by ikm (guest, #493)
[Link] (20 responses)
Python is strongly-typed. And I would actually expect 0 in the case of 3/4 since we're dividing two integers, not floats, and in all strongly-typed languages I know of the division operation doesn't change type.
Posted Mar 18, 2014 1:18 UTC (Tue)
by hummassa (subscriber, #307)
[Link] (5 responses)
Posted Mar 18, 2014 20:23 UTC (Tue)
by pboddie (guest, #50784)
[Link] (4 responses)
Posted Mar 19, 2014 0:29 UTC (Wed)
by hummassa (subscriber, #307)
[Link]
the whole "int/int -> float" thing came from Pascal/Modula via Algol IIRC... they had "int DIV int -> int" to compensate.
Posted Mar 20, 2014 15:35 UTC (Thu)
by Wol (subscriber, #4433)
[Link] (2 responses)
While typing is a good way of getting the compiler to spot your mistakes, it is wholly unnatural from a maths p-o-v. Looking at things as a mathematician and not as a computer scientist, a number is a number is a number, and this categorisation into things like ints, longs, reals, double-precisions etc is abnormal. If I wanted 3/4 to give me 0 I'd use mod, not divide.
Cheers,
Posted Mar 21, 2014 8:20 UTC (Fri)
by mbunkus (subscriber, #87248)
[Link] (1 responses)
So what's my point? Not sure, maybe something like: computer maths aren't like real world maths anyway, so arguing we should make an operation like division more like real-world maths doesn't make much sense to me.
However, int/int = int makes a lot of sense to me, coming from C and C++. Those are languages whose explicit goal is to make you pay for what you use, but not for anything more. Back when C was specified there were no FPUs in processors. Floating point number operations were exceedingly expensive. They're still more expensive than their integer counterparts. So yes, keeping division of integer operands an integer makes a lot of sense.
Note: I'm only arguing C/C++ here, not Python, as one of the OPs mentioned C/C++, and the reason for the integer division is simply speed/efficiency and giving the programmer control over the runtime cost.
Posted Mar 24, 2014 17:21 UTC (Mon)
by quanstro (guest, #77996)
[Link]
floating point is problematic. it's not a ring. not abelian. and has all sorts of other ... rather unexpected properties.
Posted Mar 20, 2014 8:58 UTC (Thu)
by dakas (guest, #88146)
[Link] (13 responses)
Posted Mar 20, 2014 12:34 UTC (Thu)
by zuki (subscriber, #41808)
[Link] (12 responses)
Posted Mar 20, 2014 12:51 UTC (Thu)
by apoelstra (subscriber, #75205)
[Link] (11 responses)
It seems very counterintuitive to me that mathematical operators would change numeric types, I'd expect that adding extra magic type-conversion rules to a language in which everything type-related is already done magically will increase your potential for bugs.
Posted Mar 20, 2014 13:31 UTC (Thu)
by zuki (subscriber, #41808)
[Link] (8 responses)
> It seems very counterintuitive to me that mathematical operators would change numeric types
I don't understand that... In "real" math, integers are also reals, and 5 is exactly the same as 5.0, and 4.999..., and nothing should change if one is substituted with the other. And operations *do* change type, e.g. with division, raising to the power or logarithm with might get a (non-integer) real from integers, and on the other hand, floor and ceiling and division or multiplication, we might get an integer from reals... I like it when Python keeps up this illusion, whenever feasible.
Posted Mar 20, 2014 15:43 UTC (Thu)
by renox (guest, #23785)
[Link]
Posted Mar 20, 2014 16:16 UTC (Thu)
by apoelstra (subscriber, #75205)
[Link] (6 responses)
Often there are implicit embedding maps from, as in your example, the integers into the reals. As long as they are actual embeddings (i.e. injective and a homomorphism of the appropriate type) they are left implicit, but otherwise you need explicit maps to move between types. And in fact this type information can cause confusion; exponentiation might map from a group of numbers under addition to a group under multiplication --- and if these groups have the same (or nearly the same) underlying set, it can cause confusion and bad logical implications, for exactly the same reason that type conversions are a source of bugs in programming.
So it is nothing new that 2 and 2.0 are different types in programming. (Though the truncating behaviour of '/' is pretty weird --- in mathematics the integers are not a division ring and division is not done on them in general.) And in programming there are dramatic differences: for one thing, there is no 'real' type since almost all reals cannot be described in finite information, while every integer can. With floating point types, all arithmetic operations are approximations (and their errors can compound in surprising ways) while integer operations are precise (everywhere that they are defined).
Posted Mar 20, 2014 20:28 UTC (Thu)
by nybble41 (subscriber, #55106)
[Link] (5 responses)
That's certainly an interesting way to look at it. Usually one doesn't consider the set an object was *selected from* to be an identifying characteristic of the object. The same object can belong to multiple sets, and, mathematically speaking, there is no difference between 2, 2/1, and 2.0. The number 2 is a member of the set of integers, and the set of rationals, and the set of reals, all at the same time. It has a square root in the set of reals, but not in the set of integers. It's a unit in the rings of rationals and reals but not in the ring of integers. The set qualifier goes with the property (has an integer square root, is a unit in the ring of rationals), not the number.
I'm not quite sure what you mean when you say that "rational 2" is not prime... "rational 2" is the same number as "integer 2", which is a prime number. If you're referring to a different kind of prime, like a prime field, then you'll have to be more specific before you can classify 2 as prime or not.
Posted Mar 21, 2014 15:10 UTC (Fri)
by apoelstra (subscriber, #75205)
[Link]
The reason I look at it this way is because I have encountered "type errors" when building mathematical proofs, for example when working with two groups whose group operations are both denoted as multiplication. The extra mental effort to tag every object with its originating set, in order to see which operations are available and meaningful, feels exactly like the extra mental effort to analyze code in an implicitly typed language.
Often hypotheses in mathematical proofs are actually type declarations, as in "Let M be a compact closed manifold". Early "proofs" before the late 19th century would lack this type information (in fact people were unaware that they needed it) and the result was an enormous body of confused and incorrect mathematical work. For example it was common practice for "proofs" to have corresponding lists of counterexamples. This is evidence that it's very easy and natural for people to lapse into defective reasoning when faced with implicit typing. Though of course programmers have a big advantage over 18th-century mathematicians in that we know not only that the types are there, but also a complete (and small) list of all available types.
Maybe this is a confused analogy. But I don't think it is, and it's helpful to me in both mathematics and programming.
> I'm not quite sure what you mean when you say that "rational 2" is not prime... "rational 2" is the same number as "integer 2", which is a prime number. If you're referring to a different kind of prime, like a prime field, then you'll have to be more specific before you can classify 2 as prime or not.
It's a bit of an aside, but I mean prime in the sense of commutative rings: an element p is prime if it is (a) nonzero and not a unit, and (b) if p divides ab, it divides one of a or b.
In the ring of integers 2 is prime; in the ring of rationals 2 is not (because it is a unit, which means it has a multiplicative inverse).
Posted Mar 22, 2014 0:03 UTC (Sat)
by mathstuf (subscriber, #69389)
[Link] (3 responses)
Well, in algebra "1 - 1 + 1 - 1 + …" is undefined since algebra cannot reach the end; in the realm of infinite series, ½ is a valid value for the series (and the only one). There's a similar thing with any of ζ(n) when n is a negative odd integers (ζ(-1) = 1 + 2 + 3 + 4 + … = -1/12, ζ(-3) = 1 + 8 + 27 + 64 + … = 1/120, etc.). Computations definitely rely on the context in which they reside; I see no reason why numeric values would be exempt.
Posted Mar 22, 2014 9:54 UTC (Sat)
by Jandar (subscriber, #85683)
[Link] (2 responses)
Posted Mar 22, 2014 10:46 UTC (Sat)
by mathstuf (subscriber, #69389)
[Link] (1 responses)
[1]https://en.wikipedia.org/wiki/Particular_values_of_Rieman...
Posted Mar 22, 2014 15:23 UTC (Sat)
by Jandar (subscriber, #85683)
[Link]
Posted Mar 21, 2014 10:42 UTC (Fri)
by moltonel (guest, #45207)
[Link] (1 responses)
Do you also find it intuitive that adding 1 to a big enough value yields a very low negative value ? Some languages, python included, will convert that machine-level integer into an object that can store arbitrarily long numbers (BTW, it's transparent in python3, but python2 appends an 'L' suffix to that number), and that is a good thing in most cases.
Type-changing operators are actually common and expected, as long as they follow some common sense (how about the classic "date - date = duration" for example ?). And common sense says that numbers are numbers are numbers.
You only find "2/3=0" intuitive because you've been formated by years of using languages where this is the case for technical reasons. But tell that to any non-programmer or user of other languages and they'll be asking "WTF?". Python3 fixed that gotcha. This is a Good Thing.
Posted Mar 21, 2014 15:37 UTC (Fri)
by apoelstra (subscriber, #75205)
[Link]
I really don't like that in C, signed integer overflow is undefined behaviour -- in this case, I can't be sure of anything after the addition. :(
I like your "date - date = duration" example. I think you're right that POLS says division should have a floating-point output (and the truncation behaviour should be relegated to some other construct, e.g. a truncate() function), since what '/' does to ints in C is not, in fact, division.
In this case I can still be sure of the types of objects before and after the operation --- of course there is no intrinsic reason that these should be the same.
Yes, it's similar gotcha. But there are a difference: A false midnight
from __future__ import division
handlily fixes problems with division, but you count not do anything like that to fix problems with DateTime.
A false midnight
A false midnight
A false midnight
A false midnight
I think this is the same gotcha as the article shows.
A false midnight
3/4 has nothing to do with strong typing, but it is related to the type of A false midnight
operator/(int, int)
... It's just like defining complex operator+(real r, imaginary i)
and having auto c = 3 + im(4);
yielding a complex "c".
A false midnight
A false midnight
A false midnight
Wol
A false midnight
A false midnight
A false midnight
and in all strongly-typed languages I know of the division operation doesn't change type.
Algol and Pascal come to mind right away.
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight
A false midnight