return type of __complex__
Hi, while fixing pypy to pass CPython 3.2 tests, I found what I think it's a inconsistency in how CPython (both 2.7 and 3.2) handles __complex__:
class Obj: ... def __complex__(self): ... return 2.0 ... obj = Obj() complex(obj) (2+0j)
import cmath cmath.acos(obj) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object
i.e., the complex constructor does not check that __complex__ returns an actual complex, while the cmath functions do. To me it looks like a bug in complex_new which should do the check as well; however, there is a test in test_complex.test_constructor which checks that returning a float actually works. Is that the real intended behavior? ciao, Anto
On Fri, Oct 19, 2012 at 11:08 PM, Antonio Cuni
Is that the real intended behavior?
Given the way complex numbers interact with floats generally, returning a complex number with no imaginary component as a floating point value seems legitimate and the checks in cmath overly strict. Otherwise you would get redundancy like: def __complex__(self): return complex(value) or def __complex__(self): return value + 0j More importantly, relaxing the checks in cmath is backwards compatible. while tightening up the checks in complex_new is not. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Fri, Oct 19, 2012 at 4:13 PM, Nick Coghlan
On Fri, Oct 19, 2012 at 11:08 PM, Antonio Cuni
wrote: Is that the real intended behavior?
Given the way complex numbers interact with floats generally, returning a complex number with no imaginary component as a floating point value seems legitimate and the checks in cmath overly strict. Otherwise you would get redundancy like:
def __complex__(self): return complex(value)
or
def __complex__(self): return value + 0j
More importantly, relaxing the checks in cmath is backwards compatible. while tightening up the checks in complex_new is not.
Cheers, Nick.
-- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fijall%40gmail.com
Cool, can we get it for 2.7?
On 10/19/2012 04:13 PM, Nick Coghlan wrote:
On Fri, Oct 19, 2012 at 11:08 PM, Antonio Cuni
wrote: Is that the real intended behavior?
Given the way complex numbers interact with floats generally, returning a complex number with no imaginary component as a floating point value seems legitimate and the checks in cmath overly strict. Otherwise you would get redundancy like:
def __complex__(self): return complex(value)
or
def __complex__(self): return value + 0j
More importantly, relaxing the checks in cmath is backwards compatible. while tightening up the checks in complex_new is not.
indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x? ciao, Anto
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 10/19/2012 11:26 AM, Benjamin Peterson wrote:
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
Why not 2.7? It is a perfectly-backward-compatible change: no currenly-working code could possibly break if cmath's restriction was relaxed. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://www.enigmail.net/ iEYEARECAAYFAlCBdnUACgkQ+gerLs4ltQ77AQCgz90IKFRobiymE8yJmYhK+Axd R3IAoMfZRBz40rOXk31QJmtQCnafaOnR =dean -----END PGP SIGNATURE-----
2012/10/19 Tres Seaver
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:26 AM, Benjamin Peterson wrote:
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
Why not 2.7? It is a perfectly-backward-compatible change: no currenly-working code could possibly break if cmath's restriction was relaxed.
It's a new feature. Also, it's possible that someone is relying on it throwing for non-complex values. -- Regards, Benjamin
On Fri, Oct 19, 2012 at 5:56 PM, Benjamin Peterson
2012/10/19 Tres Seaver
: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:26 AM, Benjamin Peterson wrote:
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
Why not 2.7? It is a perfectly-backward-compatible change: no currenly-working code could possibly break if cmath's restriction was relaxed.
It's a new feature. Also, it's possible that someone is relying on it throwing for non-complex values.
Nick just said it's a bug that cmath type checks are too strict.
Am 19.10.2012 18:23, schrieb Maciej Fijalkowski:
Nick just said it's a bug that cmath type checks are too strict.
I'm not so sure about that. cmath just uses PyArg_ParseTuple with "D" which itself relies upon PyComplex_AsCComplex(). D (complex) [Py_complex] Convert a Python complex number to a C Py_complex structure. In order to fix the bug the code in PyComplex_AsCComplex() must be altered to support float as return type from __complex__(). That's a major change. Christian
On Fri, Oct 19, 2012 at 5:31 PM, Christian Heimes
In order to fix the bug the code in PyComplex_AsCComplex() must be altered to support float as return type from __complex__(). That's a major change.
Agreed that this doesn't seem appropriate for bugfix releases. We might also want to consider having PyComplex_AsCComplex check for __float__, again for consistency with the 'complex' constructor:
class A(object): ... def __float__(self): ... return 42.0 ... a = A() float(a) 42.0 complex(a) (42+0j)
Mark
On Sat, Oct 20, 2012 at 2:23 AM, Maciej Fijalkowski
On Fri, Oct 19, 2012 at 5:56 PM, Benjamin Peterson
wrote: It's a new feature. Also, it's possible that someone is relying on it throwing for non-complex values.
Nick just said it's a bug that cmath type checks are too strict.
It wouldn't be the first time PyPy has picked up on something that is technically a bug in CPython that we nevertheless deemed too risky to fix in CPython maintenance releases (the other main one I'm aware of is the fact we sometimes get the + and * operand precedence wrong for sequence types implemented in C). I don't see the same level of risk here that Benjamin does (it's not like the conversion process would be *returning* a float value, just taking the "+ 0j" as implied when handed a floating point value by __complex__, the same way the constructor does), but it's still the RMs call as to what's acceptable for a maintenance release. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Stephen J. Turnbull wrote:
It's a design bug, yes. The question is, does it conform to documented behavior?
The 2.7 docs say this about __complex__: Called to implement the built-in function complex() ... Should return a value of the appropriate type. So the question is whether float is an "appropriate type" when you're expecting a complex. -- Greg
Greg Ewing writes:
Stephen J. Turnbull wrote:
It's a design bug, yes. The question is, does it conform to documented behavior?
The 2.7 docs say this about __complex__:
Called to implement the built-in function complex() ... Should return a value of the appropriate type.
So the question is whether float is an "appropriate type" when you're expecting a complex.
I probably not say that, but even so my personal taste would be to fix the docs to describe the current behavior in 2.7. Evidently somebody thought "float" was appropriate, or they would have just written "Returns a complex value." Stability is more important than catering to my taste (even if it happens to represent a majority in some sense).
On Sun, Oct 21, 2012 at 1:23 PM, Stephen J. Turnbull
I probably not say that, but even so my personal taste would be to fix the docs to describe the current behavior in 2.7. Evidently somebody thought "float" was appropriate
The implementation of complex_new is complicated enough that it's not at all evident that anyone thought 'float' was appropriate; that it's accepted may have just been a side-effect of the implementation. -- Mark
On 10/21/2012 8:23 AM, Stephen J. Turnbull wrote:
Greg Ewing writes:
Stephen J. Turnbull wrote:
It's a design bug, yes. The question is, does it conform to documented behavior?
The 2.7 docs say this about __complex__:
Called to implement the built-in function complex() ... Should return a value of the appropriate type.
I would take that as meaning complex or subclass thereof or whatever is consistent with float() and int().
So the question is whether float is an "appropriate type" when you're expecting a complex.
I probably not say that, but even so my personal taste would be to fix the docs to describe the current behavior in 2.7. Evidently somebody thought "float" was appropriate, or they would have just written "Returns a complex value." Stability is more important than catering to my taste (even if it happens to represent a majority in some sense).
-- Terry Jan Reedy
Stephen J. Turnbull wrote:
Evidently somebody thought "float" was appropriate, or they would have just written "Returns a complex value."
It's not quite as simple as that, because that paragraph in the docs is actually shared between the descriptions of __int__, __float__ and __complex__. So it's ambiguous whether "appropriate" means of the exact type corresponding to the name of the function, or something substitutable for that type. -- Greg
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 10/19/2012 11:56 AM, Benjamin Peterson wrote:
2012/10/19 Tres Seaver
: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:26 AM, Benjamin Peterson wrote:
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
Why not 2.7? It is a perfectly-backward-compatible change: no currenly-working code could possibly break if cmath's restriction was relaxed.
It's a new feature.
That is an assertion; I can dqually assert that the current over-strict typechecking is a bug, because it doesn't conform to the semandics of 'comples_new'.
Also, it's possible that someone is relying on it throwing for non-complex values.
No already working, non-contrived code would break, becuase float is perfectly-promotable to complex. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://www.enigmail.net/ iEYEARECAAYFAlCBg2cACgkQ+gerLs4ltQ5SMACfbhAMwQCwkZi1dF4WGL9uZoeR wv4AoII2FVW8TPchCcmsh3llo7QPxroW =gqkG -----END PGP SIGNATURE-----
2012/10/19 Tres Seaver
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:56 AM, Benjamin Peterson wrote:
2012/10/19 Tres Seaver
: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:26 AM, Benjamin Peterson wrote:
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
Why not 2.7? It is a perfectly-backward-compatible change: no currenly-working code could possibly break if cmath's restriction was relaxed.
It's a new feature.
That is an assertion; I can dqually assert that the current over-strict typechecking is a bug, because it doesn't conform to the semandics of 'comples_new'.
Nobody claimed it did conform to semantics of complex_new.
Also, it's possible that someone is relying on it throwing for non-complex values.
No already working, non-contrived code would break, becuase float is perfectly-promotable to complex.
I'm not saying the code which would break is good, I'm just saying it shouldn't be broken in bugfix releases. -- Regards, Benjamin
On Fri, Oct 19, 2012 at 12:48 PM, Benjamin Peterson
2012/10/19 Tres Seaver
: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:56 AM, Benjamin Peterson wrote:
2012/10/19 Tres Seaver
: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:26 AM, Benjamin Peterson wrote:
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
Why not 2.7? It is a perfectly-backward-compatible change: no currenly-working code could possibly break if cmath's restriction was relaxed.
It's a new feature.
That is an assertion; I can dqually assert that the current over-strict typechecking is a bug, because it doesn't conform to the semandics of 'comples_new'.
Nobody claimed it did conform to semantics of complex_new.
This also leads to a voluntary dovetailing of what would be accepted between bugfix releases. Anyone remember True/False coming into existence between bugfix releases? Wasn't that fun? And in case someone didn't pick up on the sarcasm, it wasn't fun and it's why we don't widen acceptability of things between bugfix releases unless it is actually breaking code as-is (which this isn't).
Also, it's possible that someone is relying on it throwing for non-complex values.
No already working, non-contrived code would break, becuase float is perfectly-promotable to complex.
I'm not saying the code which would break is good, I'm just saying it shouldn't be broken in bugfix releases
Agreed.
On 10/19/2012 06:44 PM, Tres Seaver wrote:
On 10/19/2012 11:56 AM, Benjamin Peterson wrote:
2012/10/19 Tres Seaver
: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 11:26 AM, Benjamin Peterson wrote:
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
Why not 2.7? It is a perfectly-backward-compatible change: no currenly-working code could possibly break if cmath's restriction was relaxed.
It's a new feature.
That is an assertion; I can dqually assert that the current over-strict typechecking is a bug, because it doesn't conform to the semandics of 'comples_new'.
Maybe, but an assertion made by the 2.7 release manager tends to be the stronger one :) Georg
On Fri, Oct 19, 2012 at 4:26 PM, Benjamin Peterson
2012/10/19 Antonio Cuni
: indeed, you are right. So I suppose that in pypy we could just relax the check in cmath and be happy. Is there any chance that this will be changed in 2.7 and/or 3.x?
Certainly 3.x, but not 2.7.
+1 for relaxing the check in 3.x. The cmath code uses "PyArg_ParseTuple(args, "D", ...) for this; perhaps it's the "D" format for PyArg_ParseTuple that should be relaxed. It seems more than reasonable to allow floats wherever a complex number is expected. Mark
On Fri, Oct 19, 2012 at 10:13 AM, Nick Coghlan
On Fri, Oct 19, 2012 at 11:08 PM, Antonio Cuni
wrote: Is that the real intended behavior?
Given the way complex numbers interact with floats generally, returning a complex number with no imaginary component as a floating point value seems legitimate and the checks in cmath overly strict. Otherwise you would get redundancy like:
def __complex__(self): return complex(value)
or
def __complex__(self): return value + 0j
No you wouldn't: def __float__(self): return value -- Devin
On Fri, Oct 19, 2012 at 3:13 PM, Nick Coghlan
Given the way complex numbers interact with floats generally, returning a complex number with no imaginary component as a floating point value seems legitimate and the checks in cmath overly strict. Otherwise you would get redundancy like:
def __complex__(self): return complex(value)
or
def __complex__(self): return value + 0j
I've opened bugs.python.org/issue16290 to track this. -- Mark
On 20/10/12 01:13, Nick Coghlan wrote:
On Fri, Oct 19, 2012 at 11:08 PM, Antonio Cuni
wrote: Is that the real intended behavior?
Given the way complex numbers interact with floats generally, returning a complex number with no imaginary component as a floating point value seems legitimate
Surely the intention is for __complex__ to return a complex number? That is, that it is desirable to have the invariant: isinstance(x.__complex__(), complex) ? We expect that __int__ returns an int, and raise an exception if it doesn't, even in the case that the value returned is numerically integral: py> class X: ... def __int__(self): ... return 2.0 ... py> x = X() py> int(x) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __int__ returned non-int (type float) Conceptually, I see returning a float when you expect a complex as equally dubious as returning an integral float when you expect an int. I think there is something dirty about a __complex__ that returns a non-complex. Why would you deliberately do such a thing? If a class does so, that's surely indicative of a bug, so the earlier it gets caught, the better.
and the checks in cmath overly strict.
Otherwise you would get redundancy like:
def __complex__(self): return complex(value) or
def __complex__(self): return value + 0j
For the record, I think Nick is referring to the fact that the complex constructor will fall back on __float__ if __complex__ does not exist, adding 0j to x.__float__. I don't see this as a problem. So what if people write redundant code? It still works. And when they learn better, they can write better code. -- Steven
On Sat, 20 Oct 2012 00:13:51 +1000
Nick Coghlan
On Fri, Oct 19, 2012 at 11:08 PM, Antonio Cuni
wrote: Is that the real intended behavior?
Given the way complex numbers interact with floats generally, returning a complex number with no imaginary component as a floating point value seems legitimate and the checks in cmath overly strict. Otherwise you would get redundancy like:
def __complex__(self): return complex(value)
or
def __complex__(self): return value + 0j
The redundancy sounds like a non-issue to me, since you can implement __float__ instead:
class C: ... def __float__(self): return 5.0 ... complex(C()) (5+0j) cmath.cos(C()) (0.28366218546322625+0j)
Regards Antoine. -- Software development and contracting: http://pro.pitrou.net
On 21.10.12 03:15, Antoine Pitrou wrote:
The redundancy sounds like a non-issue to me, since you can implement __float__ instead:
class C: ... def __init__(self, v): ... self.v = v ... def __complex__(self): ... return self.v**0.5 ... import cmath cmath.cos(C(-1)) (1.5430806348152437-7.195794243779206e-17j) cmath.cos(C(1)) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object
Returned value can be computed and result can be float or complex depending on values. The author of the class with defined __complex__() may not even be aware that under certain circumstances he would get a float. *Always* write "return complex(...)" is too redundant.
Antonio Cuni wrote:
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object
i.e., the complex constructor does not check that __complex__ returns an actual complex, while the cmath functions do.
Looks to me like cmath is being excessively finicky here. Why shouldn't a float be usable in *any* context expecting a complex? -- Greg
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 10/19/2012 07:35 PM, Greg Ewing wrote:
Antonio Cuni wrote:
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object
i.e., the complex constructor does not check that __complex__ returns an actual complex, while the cmath functions do.
Looks to me like cmath is being excessively finicky here. Why shouldn't a float be usable in *any* context expecting a complex?
Exactly: float is perfectly Liskov-substituable for complex; only applications which do explicit type sniffing can tell the difference, which makes the sniffing bogus. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://www.enigmail.net/ iEYEARECAAYFAlCC+1wACgkQ+gerLs4ltQ7S1QCfUjvUsmMiHuW8DDXue0HPzvXE Qv4Anissl8zNnx8KZyJQfcDxWlddTXGA =6KUH -----END PGP SIGNATURE-----
On Sat, Oct 20, 2012 at 12:28 PM, Tres Seaver
Exactly: float is perfectly Liskov-substituable for complex; only applications which do explicit type sniffing can tell the difference, which makes the sniffing bogus.
You don't have to do explicit sniffing. You could also be catching exceptions, e.g. ordering floats works, but ordering complex raises TypeError. All in all I'm still in favor of treating this as a new feature. It would otherwise encourage people writing code "for Python 2.7" that would actually break on older versions of 2.7. Yes, I'm aware that every bugfix release makes *some* code work that was broken before. But this is still different. "API X now supports argument type Y" smells like a new feature to me, no matter what you substitute for X and Y. -- --Guido van Rossum (python.org/~guido)
I think I've changed my mind on this, since it was pointed out that if you're going to return a float instead of a complex, you should really be implementing __float__, not __complex__. So I think it's fine to require __complex__ to return a complex, and the docs should perhaps be clarified on that point. Also PyComplex_AsComplex() should perhaps enforce that. -- Greg
On Sun, Oct 21, 2012 at 6:26 AM, Greg Ewing
I think I've changed my mind on this, since it was pointed out that if you're going to return a float instead of a complex, you should really be implementing __float__, not __complex__.
Yes, I'm wavering on this, too. I'm reasonably convinced that the complex constructor is wrong to accept a float return from __complex__. But it's not clear to me whether it's better to break backwards compatibility by fixing that in 3.4, or to accept the mistake and make cmath behave analogously.
Also PyComplex_AsComplex() should perhaps enforce that.
It already does. `complex_new` doesn't use `PyComplex_AsCComplex`. -- Mark
On 10/21/2012 5:45 AM, Mark Dickinson wrote:
On Sun, Oct 21, 2012 at 6:26 AM, Greg Ewing
wrote: I think I've changed my mind on this, since it was pointed out that if you're going to return a float instead of a complex, you should really be implementing __float__, not __complex__.
Yes, I'm wavering on this, too. I'm reasonably convinced that the complex constructor is wrong to accept a float return from __complex__. But it's not clear to me whether it's better to break backwards compatibility by fixing that in 3.4, or to accept the mistake and make cmath behave analogously.
I think we should fix it. A float return appears very rare, as well as wrong. -- Terry Jan Reedy
On 21/10/12 06:28, Tres Seaver wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 07:35 PM, Greg Ewing wrote:
Antonio Cuni wrote:
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object
i.e., the complex constructor does not check that __complex__ returns an actual complex, while the cmath functions do.
Looks to me like cmath is being excessively finicky here. Why shouldn't a float be usable in *any* context expecting a complex?
Exactly: float is perfectly Liskov-substituable for complex; only applications which do explicit type sniffing can tell the difference, which makes the sniffing bogus.
But float is not *numerically* substitutable for complex, which is why type-sniffing is not bogus at all. If you have an application which assumes numeric quantities represent real values, then you need to distinguish between real-valued and complex-valued arithmetic, and treating floats as implicitly complex is the wrong thing to do. Since most applications are based on real-values, implicit promotion to complex is in my opinion an anti-feature. Python 2.x legitimately distinguished between floats and complex, e.g.: py> (-100.0)**0.5 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: negative number cannot be raised to a fractional power If you wanted a complex result, you can explicitly ask for one: py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j) I see that behaviour is changed in Python 3. Was this discussed before being changed? I see a single sample docstring buried in PEP 3141 that: """a**b; should promote to float or complex when necessary.""" but I can't find any discussion of the consequences of this for the majority of users who would be surprised by the unexpected appearance of a "j" inside their numbers. Nor is there any hint in the docs for pow and ** that they will promote floats to complex: http://docs.python.org/dev/library/functions.html#pow http://docs.python.org/dev/library/stdtypes.html#numeric-types-int-float-com... -- Steven
You can check the .image attribute, which exists on floats too (and ints).
--Guido van Rossum (sent from Android phone)
On Oct 20, 2012 6:54 PM, "Steven D'Aprano"
On 21/10/12 06:28, Tres Seaver wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 07:35 PM, Greg Ewing wrote:
Antonio Cuni wrote:
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object
i.e., the complex constructor does not check that __complex__ returns an actual complex, while the cmath functions do.
Looks to me like cmath is being excessively finicky here. Why shouldn't a float be usable in *any* context expecting a complex?
Exactly: float is perfectly Liskov-substituable for complex; only applications which do explicit type sniffing can tell the difference, which makes the sniffing bogus.
But float is not *numerically* substitutable for complex, which is why type-sniffing is not bogus at all. If you have an application which assumes numeric quantities represent real values, then you need to distinguish between real-valued and complex-valued arithmetic, and treating floats as implicitly complex is the wrong thing to do.
Since most applications are based on real-values, implicit promotion to complex is in my opinion an anti-feature.
Python 2.x legitimately distinguished between floats and complex, e.g.:
py> (-100.0)**0.5 Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: negative number cannot be raised to a fractional power
If you wanted a complex result, you can explicitly ask for one:
py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j)
I see that behaviour is changed in Python 3. Was this discussed before being changed? I see a single sample docstring buried in PEP 3141 that:
"""a**b; should promote to float or complex when necessary."""
but I can't find any discussion of the consequences of this for the majority of users who would be surprised by the unexpected appearance of a "j" inside their numbers.
Nor is there any hint in the docs for pow and ** that they will promote floats to complex:
http://docs.python.org/dev/**library/functions.html#powhttp://docs.python.org/dev/library/functions.html#pow
http://docs.python.org/dev/**library/stdtypes.html#numeric-** types-int-float-complexhttp://docs.python.org/dev/library/stdtypes.html#numeric-types-int-float-com...
-- Steven ______________________________**_________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/**mailman/listinfo/python-devhttp://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/**mailman/options/python-dev/** guido%40python.orghttp://mail.python.org/mailman/options/python-dev/guido%40python.org
On Sun, Oct 21, 2012 at 12:53 PM, Steven D'Aprano
Python 2.x legitimately distinguished between floats and complex, e.g.:
py> (-100.0)**0.5
Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: negative number cannot be raised to a fractional power
If you wanted a complex result, you can explicitly ask for one:
py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j)
I see that behaviour is changed in Python 3.
Python 2 (future directives aside) also required you to explicitly ask for floating point. That was also changed in Python 3. That doesn't mean that this is necessarily the right thing to do, but it does have precedent. The square root of a negative number is by nature a complex number. ChrisA
On Sun, Oct 21, 2012 at 1:38 PM, Chris Angelico
Python 2 (future directives aside) also required you to explicitly ask for floating point. That was also changed in Python 3.
Er, should have said that I was referring to division here... incomplete information. Anyway. Py3 says that int/int --> float is acceptable, so float**float --> complex is equally plausible. ChrisA
Chris Angelico wrote:
Python 2 (future directives aside) also required you to explicitly ask for floating point. That was also changed in Python 3.
The solution adopted was different, though: use different operators for int and float division. This means you can't accidentally end up with a float when an int is what you intended. The equivalent solution here would be to add a new operator for complex exponentiation that coerces its operands to complex, and restrict the existing one to floats only. -- Greg
On 21.10.12 09:05, Greg Ewing wrote:
The equivalent solution here would be to add a new operator for complex exponentiation that coerces its operands to complex, and restrict the existing one to floats only.
In case of division a new operator (//) restricted to ints only, and the existing one coerces its operands to floats.
On 21/10/12 19:47, Serhiy Storchaka wrote:
On 21.10.12 09:05, Greg Ewing wrote:
The equivalent solution here would be to add a new operator for complex exponentiation that coerces its operands to complex, and restrict the existing one to floats only.
In case of division a new operator (//) restricted to ints only, and the existing one coerces its operands to floats.
That is incorrect. // works fine with non-ints. py> 42.9//3.4 12.0 -- Steven
On Sun, 21 Oct 2012 13:38:48 +1100
Chris Angelico
On Sun, Oct 21, 2012 at 12:53 PM, Steven D'Aprano
wrote: Python 2.x legitimately distinguished between floats and complex, e.g.:
py> (-100.0)**0.5
Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: negative number cannot be raised to a fractional power
If you wanted a complex result, you can explicitly ask for one:
py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j)
I see that behaviour is changed in Python 3.
Python 2 (future directives aside) also required you to explicitly ask for floating point. That was also changed in Python 3. That doesn't mean that this is necessarily the right thing to do, but it does have precedent. The square root of a negative number is by nature a complex number.
In the set of complex numbers, it is. But in the set of float numbers, a negative number has no square root. I think Steven may be worried about the impact on people who don't know about complex numbers, which is a reasonable concern. I'm also not sure why we have several variants of the power operator: **, built-in pow(), math.pow(). Regards Antoine.
On Sun, Oct 21, 2012 at 8:11 PM, Antoine Pitrou
I'm also not sure why we have several variants of the power operator: **, built-in pow(), math.pow().
Built-in pow() is provided for efficient modular arithmetic (via the 3-argument "pow(x, y, z)" form that means "x ** y % z") I don't know the rationale math.pow() though - it may just stem from the time when the math module was just a really thin wrapper around the underlying C library's floating point math support. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 21/10/12 21:11, Antoine Pitrou wrote:
On Sun, 21 Oct 2012 13:38:48 +1100 Chris Angelico
wrote: On Sun, Oct 21, 2012 at 12:53 PM, Steven D'Aprano
wrote: Python 2.x legitimately distinguished between floats and complex, e.g.:
py> (-100.0)**0.5
Traceback (most recent call last): File "<stdin>", line 1, in<module> ValueError: negative number cannot be raised to a fractional power
If you wanted a complex result, you can explicitly ask for one:
py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j)
I see that behaviour is changed in Python 3.
Python 2 (future directives aside) also required you to explicitly ask for floating point. That was also changed in Python 3. That doesn't mean that this is necessarily the right thing to do, but it does have precedent. The square root of a negative number is by nature a complex number.
In the set of complex numbers, it is. But in the set of float numbers, a negative number has no square root. I think Steven may be worried about the impact on people who don't know about complex numbers, which is a reasonable concern.
Precisely.
I'm also not sure why we have several variants of the power operator: **, built-in pow(), math.pow().
In Python 3.3, math.pow is the builtin pow. Presumably for backwards compatibility reasons when they were different? If so, it was a LONG time ago: [steve@ando ~]$ python1.5 Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
import math math.pow <built-in function pow>
And yet strangely there's never been a cmath.pow. -- Steven
On Sun, 21 Oct 2012 22:02:17 +1100
Steven D'Aprano
In Python 3.3, math.pow is the builtin pow. Presumably for backwards compatibility reasons when they were different? If so, it was a LONG time ago:
[steve@ando ~]$ python1.5 Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
import math math.pow <built-in function pow>
You are being too optimistic:
pow <built-in function pow> math.pow <built-in function pow> pow is math.pow False
Regards Antoine.
On Sun, Oct 21, 2012 at 10:08 PM, Antoine Pitrou
On Sun, 21 Oct 2012 22:02:17 +1100 Steven D'Aprano
wrote: In Python 3.3, math.pow is the builtin pow. Presumably for backwards compatibility reasons when they were different? If so, it was a LONG time ago:
[steve@ando ~]$ python1.5 Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
import math math.pow <built-in function pow>
You are being too optimistic:
pow <built-in function pow> math.pow <built-in function pow> pow is math.pow False
The one in math doesn't take three args. Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit (Intel)] on win32
math.pow(1,2,3) Traceback (most recent call last): File "
", line 1, in <module> math.pow(1,2,3) TypeError: pow expected 2 arguments, got 3
I conducted a brief survey of a non-technical nature (that is to say, I rambled to my brother and listened to what he said in response), and he's of the opinion that many programmers would be surprised and confused by the sudden appearance of complex numbers in programs that weren't using them. On the flip side, it's kinda like a NaN - you don't get an error immediately, but you'll either see it in your output or get an exception later on. His suggestion was that an expression unexpectedly returning a complex should issue a warning. Warnings aren't really useful in Python as most people have them off, but would there be any way that this sort of thing could be caught by a linter? It'd probably require some fairly deep code analysis to figure out that you're taking the square root of something that could be negative, though. ChrisA
Math.pow() predates ** by many releases. On Sunday, October 21, 2012, Steven D'Aprano wrote:
On 21/10/12 21:11, Antoine Pitrou wrote:
On Sun, 21 Oct 2012 13:38:48 +1100 Chris Angelico
wrote: On Sun, Oct 21, 2012 at 12:53 PM, Steven D'Aprano
wrote: Python 2.x legitimately distinguished between floats and complex, e.g.:
py> (-100.0)**0.5
Traceback (most recent call last): File "<stdin>", line 1, in<module> ValueError: negative number cannot be raised to a fractional power
If you wanted a complex result, you can explicitly ask for one:
py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j)
I see that behaviour is changed in Python 3.
Python 2 (future directives aside) also required you to explicitly ask for floating point. That was also changed in Python 3. That doesn't mean that this is necessarily the right thing to do, but it does have precedent. The square root of a negative number is by nature a complex number.
In the set of complex numbers, it is. But in the set of float numbers, a negative number has no square root. I think Steven may be worried about the impact on people who don't know about complex numbers, which is a reasonable concern.
Precisely.
I'm also not sure why we have several variants of the power operator:
**, built-in pow(), math.pow().
In Python 3.3, math.pow is the builtin pow. Presumably for backwards compatibility reasons when they were different? If so, it was a LONG time ago:
[steve@ando ~]$ python1.5 Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat 4.1.2-52)] on linux2 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
import math
math.pow
<built-in function pow>
And yet strangely there's never been a cmath.pow.
-- Steven ______________________________**_________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/**mailman/listinfo/python-devhttp://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/**mailman/options/python-dev/** guido%40python.orghttp://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (python.org/~guido)
On Sun, Oct 21, 2012 at 11:53 AM, Steven D'Aprano
On 21/10/12 06:28, Tres Seaver wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 10/19/2012 07:35 PM, Greg Ewing wrote:
Antonio Cuni wrote:
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object
i.e., the complex constructor does not check that __complex__ returns an actual complex, while the cmath functions do.
Looks to me like cmath is being excessively finicky here. Why shouldn't a float be usable in *any* context expecting a complex?
Exactly: float is perfectly Liskov-substituable for complex; only applications which do explicit type sniffing can tell the difference, which makes the sniffing bogus.
But float is not *numerically* substitutable for complex, which is why type-sniffing is not bogus at all. If you have an application which assumes numeric quantities represent real values, then you need to distinguish between real-valued and complex-valued arithmetic, and treating floats as implicitly complex is the wrong thing to do.
Since most applications are based on real-values, implicit promotion to complex is in my opinion an anti-feature.
Python 2.x legitimately distinguished between floats and complex, e.g.:
py> (-100.0)**0.5
Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: negative number cannot be raised to a fractional power
If you wanted a complex result, you can explicitly ask for one:
py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j)
I see that behaviour is changed in Python 3. Was this discussed before being changed? I see a single sample docstring buried in PEP 3141 that:
"""a**b; should promote to float or complex when necessary."""
but I can't find any discussion of the consequences of this for the majority of users who would be surprised by the unexpected appearance of a "j" inside their numbers.
PEP 3141 is indeed the driver for these changes, and it's based on the Python 3.x numeric tower consisting of strict supersets: Complex > Real > Rational > Integral If an operation at one level of the tower produces a result in one of the larger supersets, then *that's what it will do* rather than throwing TypeError. int / int promoting to float is one example, as is raising a negative number to a fractional power promoting to complex. The general philosophy is described in http://www.python.org/dev/peps/pep-3141/#implementing-the-arithmetic-operati... It sounds like cmath (or, more specifically, the "D" conversion code) missed out on the associated updates). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Sat, Oct 20, 2012 at 10:57 PM, Nick Coghlan
PEP 3141 is indeed the driver for these changes, and it's based on the Python 3.x numeric tower consisting of strict supersets: Complex > Real > Rational > Integral
Pedant mode: That numeric tower is wrong as it applies to Python -- we have some rational types that can represent some numbers that can't be represented by our complex and """real""" types. >>> int(float(10**100)) == 10**100 False (Also, floats aren't reals and no computer can store any number that is not rational and we should stop pretending they can. (Complex numbers get a free pass because "complex numbers with rational real and imaginary parts" is not a memorable name for a type.)) *Pedant mode deactivated*
If an operation at one level of the tower produces a result in one of the larger supersets, then *that's what it will do* rather than throwing TypeError. int / int promoting to float is one example, as is raising a negative number to a fractional power promoting to complex.
The general philosophy is described in http://www.python.org/dev/peps/pep-3141/#implementing-the-arithmetic-operati...
It sounds like cmath (or, more specifically, the "D" conversion code) missed out on the associated updates).
No, no no. The return type of conversion methods has nothing to do with the interoperability of types in arithmetic. If it did there would've been a systematic purging of old behaviors in these conversion functions, and there evidently hasn't been because float(), cmath, and math all behave "wrong", and those are collectively fairly hard to miss. Since float(), cmath, and math all behave the same, I'd assert that it's complex() (and int()) that is weird and unusual.
class A: ... def __complex__(self): ... return 1 ... def __float__(self): ... return 1 ... complex(A()) (1+0j) float(A()) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __float__ returned non-float (type int) cmath.sqrt(A()) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __complex__ should return a complex object math.sqrt(A()) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: nb_float should return float object
-- Devin
On Sun, Oct 21, 2012 at 5:11 PM, Greg Ewing
Devin Jeanpierre wrote:
(Complex numbers get a free pass because "complex numbers with rational real and imaginary parts" is not a memorable name for a type.)
Complexional?
"Oh is there not one maiden here Whose homely face and bad complexion Have caused all hope to disappear Of ever winning man's affection?" I'm hoping your name was a joke, because it's rather unwieldy. But really, computers are no different from anything else; they have to represent numbers somehow. That pretty much means it has to be rational, as it's awkward trying to do arithmetic with surds floating around everywhere. ChrisA
On Sun, Oct 21, 2012 at 3:06 PM, Devin Jeanpierre
On Sat, Oct 20, 2012 at 10:57 PM, Nick Coghlan
wrote: PEP 3141 is indeed the driver for these changes, and it's based on the Python 3.x numeric tower consisting of strict supersets: Complex > Real > Rational > Integral
Pedant mode: That numeric tower is wrong as it applies to Python -- we have some rational types that can represent some numbers that can't be represented by our complex and """real""" types.
>>> int(float(10**100)) == 10**100 False
(Also, floats aren't reals and no computer can store any number that is not rational and we should stop pretending they can. (Complex numbers get a free pass because "complex numbers with rational real and imaginary parts" is not a memorable name for a type.))
Floats are not rational either, since they allow infinities and NaN as values. Both float and Decimal are approximations to the Real number set, as int and Fraction are approximations to the Integer and Rational sets. In all cases, they cannot express the full scope of the associated numeric concept, because they're limited by their physical representations as bit patterns inside the computer (while that limit is technically available memory for the unbounded representations, the practical limit is lower than that if you want vaguely reasonable running times for arbitrary arithmetic operations). As far as the conversion operators go, I don't recall there being any discussion one way or the other - the question simply never came up, presumably because all code tested already returned complex objects from __complex__. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
I guess I was asking for this. (Sorry for OT conversation.)
On Sun, Oct 21, 2012 at 6:31 AM, Nick Coghlan
(Also, floats aren't reals and no computer can store any number that is not rational and we should stop pretending they can. (Complex numbers get a free pass because "complex numbers with rational real and imaginary parts" is not a memorable name for a type.))
Floats are not rational either, since they allow infinities and NaN as values. Both float and Decimal are approximations to the Real number set, as int and Fraction are approximations to the Integer and Rational sets.
You're missing a step here. Are you claiming that the real numbers have infinities or NaN? Why do floats approximate the reals, while Fraction approximates the rationals? After all, for any real number, a Fraction instance can become arbitrarily close to it (given sufficient memory). The same is not true of a float, which can only get to a relative distance of machine epsilon, and then only when the real number is close to zero. It would seem as if Fractions approximate the reals and rationals, and floats do not approximate anything at all (at least, not arbitrarily closely*). * I think "can be made arbitrarily close to the value in question" is the usual meaning of "approximates" in mathematics, but I'm not sure about numerical computing (maybe it just means "with sufficiently small difference for values we care about"?)
In all cases, they cannot express the full scope of the associated numeric concept, because they're limited by their physical representations as bit patterns inside the computer (while that limit is technically available memory for the unbounded representations, the practical limit is lower than that if you want vaguely reasonable running times for arbitrary arithmetic operations).
There is a deeper difference in how badly we can represent the rationals versus the reals. For an arbitrary rational number, we can represent it precisely on a computer with sufficiently many bytes of memory. That is not true for real numbers: the vast majority of real numbers cannot be represented precisely in any form on a computer, no matter how large it is. And while there are irrational numbers we can represent precisely on a computer, floats don't represent any of them; they only represent a selection of rational numbers, and nan/inf/-inf.
As far as the conversion operators go, I don't recall there being any discussion one way or the other - the question simply never came up, presumably because all code tested already returned complex objects from __complex__.
That's a fair objection to what I said. I guess the discussion is here/now. -- Devin
On 21/10/12 13:57, Nick Coghlan wrote:
On Sun, Oct 21, 2012 at 11:53 AM, Steven D'Aprano
wrote:
Python 2.x legitimately distinguished between floats and complex, e.g.:
py> (-100.0)**0.5
Traceback (most recent call last): File "<stdin>", line 1, in<module> ValueError: negative number cannot be raised to a fractional power
If you wanted a complex result, you can explicitly ask for one:
py> (-100.0+0j)**0.5 (6.123031769111886e-16+10j)
I see that behaviour is changed in Python 3. Was this discussed before being changed? I see a single sample docstring buried in PEP 3141 that:
"""a**b; should promote to float or complex when necessary."""
but I can't find any discussion of the consequences of this for the majority of users who would be surprised by the unexpected appearance of a "j" inside their numbers.
PEP 3141 is indeed the driver for these changes, and it's based on the Python 3.x numeric tower consisting of strict supersets: Complex> Real> Rational> Integral
If an operation at one level of the tower produces a result in one of the larger supersets, then *that's what it will do* rather than throwing TypeError. int / int promoting to float is one example, as is raising a negative number to a fractional power promoting to complex.
The general philosophy is described in http://www.python.org/dev/peps/pep-3141/#implementing-the-arithmetic-operati...
Yes, I've read that. But it seems to me that this change appears to have been driven by type-purity (floats are Liskov-substituable for complex) rather than practical considerations for people who may not know what complex numbers are, or at best may have vague recollections of being told about them in high school, and who now have to deal with the unexpected appearance of complex numbers in calculations which previously gave an exception. When you're dealing with numbers that represent real quantities, getting a complex result is nearly always an error condition. Better to get an exception at the point that occurs, than somewhere distant when the number gets fed to %f formatting, or worse, no error at all, just a silently generating garbage results. -- Steven
Steven D'Aprano wrote:
When you're dealing with numbers that represent real quantities, getting a complex result is nearly always an error condition. Better to get an exception at the point that occurs, than somewhere distant when the number gets fed to %f formatting, or worse, no error at all, just a silently generating garbage results.
Yeah. I don't think Inland Revenue would be very impressed if I tried to tell them I had imaginary tax-to-pay, advantageous though it might be for me. -- Greg
On Mon, Oct 22, 2012 at 11:10 AM, Greg Ewing
Steven D'Aprano wrote:
When you're dealing with numbers that represent real quantities, getting a complex result is nearly always an error condition. Better to get an exception at the point that occurs, than somewhere distant when the number gets fed to %f formatting, or worse, no error at all, just a silently generating garbage results.
Yeah. I don't think Inland Revenue would be very impressed if I tried to tell them I had imaginary tax-to-pay, advantageous though it might be for me.
Unless the IRS instructs you to submit the square root of your income, I doubt that this will ever come up. Amusing though the notion be. There really aren't that many situations where a program will be completely oblivious of complex/imaginary numbers and be able to encounter them... are there? ChrisA
On Mon, Oct 22, 2012 at 02:25:45PM +1100, Chris Angelico wrote:
There really aren't that many situations where a program will be completely oblivious of complex/imaginary numbers and be able to encounter them... are there?
Fortunately the math module does not promote float to complex: py> math.asin(2) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: math domain error (although the error message could be a bit nicer...) so as far as I can tell, the only way you could accidentally get a complex number without expecting one is by exponentiation, either by ** or the builtin pow. This includes square roots. Exponentiation isn't as common as the basic four arithmetic operators, but it is hardly exotic. Any time you have a non-integer power and a negative base, you will get a complex number. So there are two error conditions which used to give an exception but now promote to complex: - taking the root (square root, cube root, etc.) of a number which should be positive, but accidentally becomes negative (perhaps due to round-off error, you get a small -ve number instead of zero); - the base may legitimately be negative, and the exponent is supposed to be a positive integer but accidentally because non-integral. I suppose as someone who is rather fond of complex numbers, I should be pleased about this change, which raises the profile of complex and makes it closer to a "first class" numeric type. But my concern is that people who never dreamed that complex numbers even existed suddenly need to beware of them, and write LBYL code to avoid them, rather than relying on Python raising an exception. In a nutshell, the semantics of ** have changed between 2.x and 3.x, and that's going to have consequences when porting code from 2 to 3. Perhaps 2to3 needs to warn about the use of ** and pow? [wishful thinking] If we had floating point contexts, we could just tell people to stick import float float.context.promote_to_complex = False at the top of their module and they'd be fine. -- Steven
On 10/21/2012 09:35 PM, Steven D'Aprano wrote:
so as far as I can tell, the only way you could accidentally get a complex number without expecting one is by exponentiation, either by ** or the builtin pow. This includes square roots. Exponentiation isn't as common as the basic four arithmetic operators, but it is hardly exotic.
Any time you have a non-integer power and a negative base, you will get a complex number.
No, only via the builtin pow. % python3 Python 3.3.0 (default, Sep 29 2012, 22:42:55) [GCC 4.6.3] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import math >>> pow(-1, 0.5) (6.123031769111886e-17+1j) >>> -1 ** 0.5 -1.0 >>> math.pow(-1, 0.5) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: math domain error >>> //arry/
Chris Angelico writes:
There really aren't that many situations where a program will be completely oblivious of complex/imaginary numbers and be able to encounter them... are there?
Stats. Happens in naive student programs with naive student models. Specifically, in computing variance or SSE, it's often convenient to subtract the square of the mean from the sum of the squares. With ill-conditioned data (specifically, perfectly multicollinear series that arise when you do a linear regression on an accounting identity) I occasionally[1] see a negative variance in submitted output. That implies a complex standard deviation. Unlike Inland Revenue or even the IRS, third-rate MBA students are completely able to take complex results in stride. That's the value of a higher education! ;-) Of course regressing an accounting identity is a (big) mistake anyway, but the students are more likely to argue with a professor than with a computer. You might also see this in various applications in computational geometry. Footnotes: [1] Every couple of years.
participants (19)
-
Antoine Pitrou
-
Antonio Cuni
-
Benjamin Peterson
-
Brett Cannon
-
Chris Angelico
-
Christian Heimes
-
Devin Jeanpierre
-
Georg Brandl
-
Greg Ewing
-
Guido van Rossum
-
Larry Hastings
-
Maciej Fijalkowski
-
Mark Dickinson
-
Nick Coghlan
-
Serhiy Storchaka
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Terry Reedy
-
Tres Seaver