User: Password:
|
|
Subscribe / Log in / New account

A false midnight

A false midnight

Posted Mar 20, 2014 12:34 UTC (Thu) by zuki (subscriber, #41808)
In reply to: A false midnight by dakas
Parent article: A false midnight

I've been using mixed Python 2/3 environments recently, and after getting accustomed to the new division behaviour, when I use Python 2, the old truncating behaviour is one of the bug "sources". I've had to add 'from __future__ import division' more than once in the last few weeks to fix actual bugs in code. Of course one can say that I should simply remember to do a cast to float here and there, but at least for me, it feels much easier and results in fewer bugs to do an explicit truncating division with // when necessary.


(Log in to post comments)

A false midnight

Posted Mar 20, 2014 12:51 UTC (Thu) by apoelstra (subscriber, #75205) [Link]

I think in general if you are doing a project of sufficient complexity that you need to be aware of the type of your objects (but are not), Python is the wrong language for you, and something where types are explicitly described and tracked would be better.

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.

A false midnight

Posted Mar 20, 2014 13:31 UTC (Thu) by zuki (subscriber, #41808) [Link]

If I do a division, I *am* aware of the type... They are numbers, and specifically numbers.Real. In Python, both an int and and a float are subclasses of numbers.Real. For almost all intents and purposes, the difference between them is unimportant, but division is one them. Things might work when called with 5.0, but break if that argument changed to 6. I'd rather avoid that.

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

A false midnight

Posted Mar 20, 2014 15:43 UTC (Thu) by renox (subscriber, #23785) [Link]

I think that the main reason why other language doesn't do this is that reals have several possible representations: float32, float64, float intervals, rationals,etc which make choosing the 'real' type for the division's result quite arbitrary..

A false midnight

Posted Mar 20, 2014 16:16 UTC (Thu) by apoelstra (subscriber, #75205) [Link]

Well, even in mathematics there are different 'types' corresponding to the set in which an object is supposed to lie. For example, the integer 2 is prime while the rational 2 is not; the rational 2 is a unit while the integer 2 is not. The real 2 is a square while the rational or integer 2 is not, etc. etc.

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

A false midnight

Posted Mar 20, 2014 20:28 UTC (Thu) by nybble41 (subscriber, #55106) [Link]

> Well, even in mathematics there are different 'types' corresponding to the set in which an object is supposed to lie. For example, the integer 2 is prime while the rational 2 is not; the rational 2 is a unit while the integer 2 is not. The real 2 is a square while the rational or integer 2 is not, etc. etc.

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.

A false midnight

Posted Mar 21, 2014 15:10 UTC (Fri) by apoelstra (subscriber, #75205) [Link]

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

A false midnight

Posted Mar 22, 2014 0:03 UTC (Sat) by mathstuf (subscriber, #69389) [Link]

> Usually one doesn't consider the set an object was *selected from* to be an identifying characteristic of the object.

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.

A false midnight

Posted Mar 22, 2014 9:54 UTC (Sat) by Jandar (subscriber, #85683) [Link]

ζ(n) with negative odd n is infinite.

A false midnight

Posted Mar 22, 2014 10:46 UTC (Sat) by mathstuf (subscriber, #69389) [Link]

Depends on the context[1] :) . Each series resulting from the ζ(n) with n as a negative, odd integer has a single, useful value which can be used in its place when it occurs in equations (physics, chemistry, etc.).

[1]https://en.wikipedia.org/wiki/Particular_values_of_Rieman...

A false midnight

Posted Mar 22, 2014 15:23 UTC (Sat) by Jandar (subscriber, #85683) [Link]

It's embarrassing to see how much one can forget. My mathematical studies lie some 20 years in the past and almost anything about sheaf and germs is lost.

A false midnight

Posted Mar 21, 2014 10:42 UTC (Fri) by moltonel (subscriber, #45207) [Link]

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

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.

A false midnight

Posted Mar 21, 2014 15:37 UTC (Fri) by apoelstra (subscriber, #75205) [Link]

Integer overflow is a property of the integer type in many languages (though as you say, not Python). It's intuitive that a fixed-width type is going to overflow at some point, by the pigeonhole principle. In languages like Python or Lisp which hold arbitrarily-sized integers, this overflow is not a property of the type. In either case there is no type transition caused by adding 1 repeatedly to an integer, so I can be sure of the types of my objects before and after the addition.

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.


Copyright © 2017, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds