I bumped into an oddity today: 6.0 // 0.001 != math.floor(6.0 / 0.001) In looking at Objects/floatobject.c, I was surprised to find that float_floor_division() is implemented in terms of float_divmod(). Does anyone know why it takes such a circuitous path? I had expected something simpler and faster: return PyFloat_FromDouble(floor(a/b)); Raymond
Probably because I tend not to know what I'm doing when numerics are concerned. :-( On 1/19/07, Raymond Hettinger <raymond.hettinger@verizon.net> wrote:
I bumped into an oddity today:
6.0 // 0.001 != math.floor(6.0 / 0.001)
In looking at Objects/floatobject.c, I was surprised to find that float_floor_division() is implemented in terms of float_divmod(). Does anyone know why it takes such a circuitous path? I had expected something simpler and faster:
return PyFloat_FromDouble(floor(a/b));
Raymond
_______________________________________________ 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/guido%40python.org
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
[Raymond Hettinger]
I bumped into an oddity today:
6.0 // 0.001 != math.floor(6.0 / 0.001)
In looking at Objects/floatobject.c, I was surprised to find that float_floor_division() is implemented in terms of float_divmod(). Does anyone know why it takes such a circuitous path? I had expected something simpler and faster:
return PyFloat_FromDouble(floor(a/b));
To preserve, so far as is possible with floats, that (*) a == (a//b)*b + a%b In this case the binary double closest to decimal 0.001 is 0.001000000000000000020816681711721685132943093776702880859375 which is a little bit larger than 1/1000. Therefore the mathematical value of a/b is a little bit smaller than 6/(1/1000) = 6000, and the true floor of the mathematically correct result is 5999. a % b is always computed exactly (mathematical result == machine result) under Python's definition whenever a and b have the same sign (under C99's and the `decimal` module's definition it's always exact regardless of signs), and getting the exact value for a%b implies the exact value for a//b must also be returned (when possible) in order to preserve identity (*) above. IOW, since
6.0 % 0.001 0.00099999999999987512
it would be inconsistent to return 6000 for 6.0 // 0.001:
6.0 - 6000 * 0.001 # this isn't close to the value of a%b 0.0 6.0 - 5999 * 0.001 # but this is close 0.00099999999999944578
Note that two rounding errors occur in computing a - N*b via binary doubles. If there were no rounding errors, we'd have 6 % b == 6.0 - 5999 * b exactly where b = 0.001000000000000000020816681711721685132943093776702880859375 is the value actually stored in the machine for 0.001:
import decimal decimal.getcontext().prec = 1000 # way more than needed import math # Set b to exact decimal value of binary double closest to 0.001. m, e = math.frexp(0.001) b = decimal.Decimal(int(m*2**53)) / decimal.Decimal(1 << (53-e)) # Then 6%b is exactly equal to 6 - 5999*b 6 % b == 6 - 5999*b True # Confirm that all decimal calculations were exact. decimal.getcontext().flags[decimal.Inexact] False # Confirm that floor(6/b) is 5999. int(6/b) 5999 print 6/b 5999.99999999999987509990972966989180234686226...
All that said, (*) doesn't make a lot of sense for floats to begin with (for that matter, Python's definition of mod alone doesn't make much sense for floats when the signs differ -- e.g.,
-1 % 1e100 1e+100 decimal.Decimal(-1) % decimal.Decimal("1e100") Decimal("-1")
).
Hi Tim, On Fri, Jan 19, 2007 at 08:33:23PM -0500, Tim Peters wrote:
decimal.Decimal(-1) % decimal.Decimal("1e100") Decimal("-1")
BTW - isn't that case in contradiction with the general Python rule that if b > 0, then a % b should return a number between 0 included and b excluded? We try hard to do that for ints, longs and floats. The fact that it works differently with Decimal could be unexpected. A bientot, Armin
[Tim Peters]
...
decimal.Decimal(-1) % decimal.Decimal("1e100") Decimal("-1")
[Armin Rigo]
BTW - isn't that case in contradiction with the general Python rule that if b > 0, then a % b should return a number between 0 included and b excluded?
Sure.
We try hard to do that for ints, longs and floats.
But fail in this case for floats:
-1 % 1e100 < 1e100 False
Can't win. The infinite-precision result is the mathematical F(1e100)-1, where F(1e100) is the binary float closest to 10**100, but F(1e100)-1 isn't representable as a float -- it rounds back up to F(1e100):
-1 % 1e100 == 1e100 True
There simply is no /representable/ float value in [0, 10**100) congruent to -1 modulo 10**100 (or modulo F(1e100)), so it's impossible to return a non-surprising (to everyone) result in that range. 0 and 1e100 are in some sense "the best" answers in that range, both off by only 1 part in F(1e100), the smallest possible error among representable floats in that range. -1/1e100 certainly isn't 0, so -1 // 1e100 == -1.0 is required. Picking -1 % 1e100 == 1e100 then follows, to try to preserve that a = (a//b)*b + a%b as closely as is possible. Ints and longs never have problems here, because the exact % result is always exactly representable. That isn't true of floats (whether binary or decimal), but under a different definition of "mod" the mathematically exact result is always exactly representable: a%b takes the sign of `a` rather than the sign of `b`. C's fmod (Python's math.fmod), and the proposed standard for decimal arithmetic implemented by the `decimal` module, use that meaning for "mod" instead.
math.fmod(-1, 1e100) -1.0
The fact that it works differently with Decimal could be unexpected.
Yup. See "can't win" above :-( Another good definition of "mod" for floats is to return the representative of smallest absolute value; i.e., satisfy abs(a%b) <= abs(b) / 2 The mathematically exact value for that is also exactly representable (BTW, the proposed standard for decimal arithmetic calls this "remainder-near", as opposed to "remainder"). It's just a fact that different definitions of mod are most useful most often depending on data type. Python's is good for integers and often sucks for floats. The C99 and `decimal` definition(s) is/are good for floats and often suck(s) for integers. Trying to pretend that integers are a subset of floats can't always work ;-)
On 1/21/07, Tim Peters <tim.peters@gmail.com> wrote:
It's just a fact that different definitions of mod are most useful most often depending on data type. Python's is good for integers and often sucks for floats. The C99 and `decimal` definition(s) is/are good for floats and often suck(s) for integers. Trying to pretend that integers are a subset of floats can't always work ;-)
That really sucks, especially since the whole point of making int division return a float was to make the integers embedded in the floats... I think the best solution would be to remove the definition of % (and then also for divmod()) for floats altogether, deferring to math.fmod() instead. The ints aren't really embedded in Decimal, so we don't have to do that there (but we could). The floats currently aren't embedded in complex, since f.real and f.imag don't work for floats (and math.sqrt(-1.0) doesn't return 1.0j, and many other anomalies). That was a conscious decision because complex numbers are useless for most folks. But is it still the right decision, given what we're trying to do with int and float? -- --Guido van Rossum (home page: http://www.python.org/~guido/)
... [Tim]
It's just a fact that different definitions of mod are most useful most often depending on data type. Python's is good for integers and often sucks for floats. The C99 and `decimal` definition(s) is/are good for floats and often suck(s) for integers. Trying to pretend that integers are a subset of floats can't always work ;-)
[Guido]
That really sucks, especially since the whole point of making int division return a float was to make the integers embedded in the floats... I think the best solution would be to remove the definition of % (and then also for divmod()) for floats altogether, deferring to math.fmod() instead.
In Python 2? I thought this was already (semi)settled for Py3K -- back last May, on the Py3K list, in a near-repetition of the current thread: [Tim] I'd be happiest if P3K floats didn't support __mod__ or __divmod__ at all. Floating mod is so rare it doesn't need syntactic support, and the try-to-be-like-integer __mod__ and __divmod__ floats support now can deliver surprises to all users incautious enough to use them. [Guido] OK, this makes sense. I've added this to PEP 3100.
The ints aren't really embedded in Decimal, so we don't have to do that there (but we could).
Floor division is an odd beast for floats, and I don't know of a good use for it. As Raymond's original example in this thread showed, it's not always the case that math.floor(x/y) == x // y The rounding error in computing x/y can cause floor() to see an exact integer coming in even when the true value of x/y is a little smaller than that integer (of course it's also possible for x/y to overflow). This is independent of fp base (it's "just as true" for decimal floats as for binary floats). The `decimal` module also has two distinct flavors of "mod", neither of which match Python's integer-mod definition:
decimal.Decimal(7).__mod__(10) Decimal("7") decimal.Decimal(7).remainder_near(10) Decimal("-3") decimal.Decimal(-7).__mod__(10) Decimal("-7") decimal.Decimal(-7).remainder_near(10) Decimal("3")
But, again, I think floating mod is so rare it doesn't need syntactic support, and especially not when there's more than one implementation -- and `decimal` is a floating type (in Cowlishaw's REXX, this was the /only/ numeric type, so there was more pressure there to give it a succinct spelling).
The floats currently aren't embedded in complex, since f.real and f.imag don't work for floats (and math.sqrt(-1.0) doesn't return 1.0j, and many other anomalies). That was a conscious decision because complex numbers are useless for most folks. But is it still the right decision, given what we're trying to do with int and float?
Sounds like a "purity" thing. The "pure form" of the original /intent/ was probably just that nobody should get a complex result from non-complex inputs (they should see an exception instead) unless they use a `cmath` function to prove they know what they're doing up front. I still think that has pragmatic value, since things like sqrt(-1) and asin(1.05) really are signs of logic errors for most programs. But under that view, there's nothing surprising about, e.g., (math.pi).imag returning 0.0 -- or (3).imag returning 0.0 either. That sounds fine to me (if someone cares enough to implement it). Unclear what very_big_int.real should do (it can lose information to rounding if the int > 2**53, or even overflow -- maybe it should just return the int!).
On 1/21/07, Tim Peters <tim.peters@gmail.com> wrote:
[Tim]
It's just a fact that different definitions of mod are most useful most often depending on data type. Python's is good for integers and often sucks for floats. The C99 and `decimal` definition(s) is/are good for floats and often suck(s) for integers. Trying to pretend that integers are a subset of floats can't always work ;-)
[Guido]
That really sucks, especially since the whole point of making int division return a float was to make the integers embedded in the floats... I think the best solution would be to remove the definition of % (and then also for divmod()) for floats altogether, deferring to math.fmod() instead.
In Python 2?
No way!
I thought this was already (semi)settled for Py3K -- back last May, on the Py3K list, in a near-repetition of the current thread:
[Tim] I'd be happiest if P3K floats didn't support __mod__ or __divmod__ at all. Floating mod is so rare it doesn't need syntactic support, and the try-to-be-like-integer __mod__ and __divmod__ floats support now can deliver surprises to all users incautious enough to use them.
[Guido] OK, this makes sense. I've added this to PEP 3100.
Oops, you're right. I'm suffering from memory loss. :-(
The ints aren't really embedded in Decimal, so we don't have to do that there (but we could).
Floor division is an odd beast for floats, and I don't know of a good use for it. As Raymond's original example in this thread showed, it's not always the case that
math.floor(x/y) == x // y
The rounding error in computing x/y can cause floor() to see an exact integer coming in even when the true value of x/y is a little smaller than that integer (of course it's also possible for x/y to overflow). This is independent of fp base (it's "just as true" for decimal floats as for binary floats).
The `decimal` module also has two distinct flavors of "mod", neither of which match Python's integer-mod definition:
decimal.Decimal(7).__mod__(10) Decimal("7") decimal.Decimal(7).remainder_near(10) Decimal("-3") decimal.Decimal(-7).__mod__(10) Decimal("-7") decimal.Decimal(-7).remainder_near(10) Decimal("3")
But, again, I think floating mod is so rare it doesn't need syntactic support, and especially not when there's more than one implementation -- and `decimal` is a floating type (in Cowlishaw's REXX, this was the /only/ numeric type, so there was more pressure there to give it a succinct spelling).
So you are proposing that Decimal also rip out the % and // operators and __divmod__? WFM, but I don't know what Decimal users say (I'm not one).
The floats currently aren't embedded in complex, since f.real and f.imag don't work for floats (and math.sqrt(-1.0) doesn't return 1.0j, and many other anomalies). That was a conscious decision because complex numbers are useless for most folks. But is it still the right decision, given what we're trying to do with int and float?
Sounds like a "purity" thing. The "pure form" of the original /intent/ was probably just that nobody should get a complex result from non-complex inputs (they should see an exception instead) unless they use a `cmath` function to prove they know what they're doing up front.
I still think that has pragmatic value, since things like sqrt(-1) and asin(1.05) really are signs of logic errors for most programs.
OK, that's good.
But under that view, there's nothing surprising about, e.g., (math.pi).imag returning 0.0 -- or (3).imag returning 0.0 either. That sounds fine to me (if someone cares enough to implement it). Unclear what very_big_int.real should do (it can lose information to rounding if the int > 2**53, or even overflow -- maybe it should just return the int!).
For ints and floats, real could just return self, and imag could return a 0 of the same type as self. I guess the conjugate() function could also just return self (although I see that conjugate() for a complex with a zero imaginary part returns something whose imaginary part is -0; is that intentional? I'd rather not have to do that when the input is an int or float, what do you think?) -- --Guido van Rossum (home page: http://www.python.org/~guido/)
[Guido]
... So you are proposing that Decimal also rip out the % and // operators and __divmod__? WFM, but I don't know what Decimal users say (I'm not one).
Yes: it's just as much a floating type as HW binary floats, and all the same issues come up. For example, decimal floats are just as prone to the floor division surprise Raymond started this thread with; e.g.,
a Decimal("2.172839486617283948661728393E+29") b Decimal("1234567890123456789012345678") a / b Decimal("176.0000000000000000000000000") a/b == 176 True a // b Decimal("175")
That is, floor division of `a` by `b` isn't necessarily the same as the floor of `a` divided by `b` for decimal floats either, and for exactly the same reason as when using binary floats: a/b can suffer a rounding error due to finite precision, but floor division computes the floor of the quotient "as if" infinite precision were available. At least using `decimal` it's easy to /explain/ just by boosting the precision:
decimal.getcontext().prec *= 2 a / b Decimal("175.99999999999999999999999997731999979587999814250798308")
This shows quite clearly why a/b rounded up to exactly 176 when working with half this precision. There's also that the decimal __mod__ implementation is like math.fmod for binary floats, not like Python's int/long __mod__. Having just one builtin meaning for numeric `%` as an infix operator is a good thing, and the int/long meaning is both by far the most common use but only "works" for types with exactly representable results (ints and longs built in; rationals if someone adds them; ditto constructive reals; ... -- but not floats).
... For ints and floats, real could just return self, and imag could return a 0 of the same type as self.
Cool! Works for me.
I guess the conjugate() function could also just return self (although I see that conjugate() for a complex with a zero imaginary part returns something whose imaginary part is -0; is that intentional?
That's wrong, if true: it should return something with the opposite sign on the imaginary part, whether or not that equals 0 (+0. and -0. both "equal 0"). This is harder to check than it should be because it appears there's a bug in the complex constructor (at least in Python 2.5): complex(1., 0.0) and complex(1., -0.0) both appear to create a complex with a +0 imaginary part:
def is_minus_0(x): ... import math ... return x == 0.0 and math.atan2(x, x) != 0 is_minus_0(+0.0) # just showing that "it works" False is_minus_0(-0.0) # ditto True is_minus_0(complex(1, 0.0).imag) False is_minus_0(complex(1, -0.0).imag) # should be True False
OTOH, the complex constructor does respect the sign of the real part:
is_minus_0(complex(0.0, 0.0).real) False is_minus_0(complex(-0.0, 0.0).real) True
complex_new() ends with: cr.real -= ci.imag; cr.imag += ci.real; and I have no idea what that thinks it's doing. Surely this isn't intended?!:
complex(complex(1.0, 2.0), complex(10.0, 20.0)) (-19+12j)
WTF? In any case, that's also what's destroying the sign of the imaginary part in complex(1.0, -0.0). Knowing that a -0 imaginary part can't be constructed in the obvious way:
is_minus_0(complex(0, 0).conjugate().imag) True
So conjugate() does flip the sign on a +0 imaginary part, and:
is_minus_0(complex(0, 0).conjugate().conjugate().imag) False
so it also flips the sign on a -0 imaginary part. That's all as it should be. Hmm. You meant to ask something different, but I actually answered that too ;-)
I'd rather not have to do that when the input is an int or float, what do you think?)
Returning `self` is fine with me for those, although, e.g., it does mean that (3).conjugate().imag and (complex(3)).conjugate().imag are distinguishable with enough pain. I don't care. I think of integers and floats as "not having" an imaginary part more than as having a 0 imaginary part (but happy to invent a 0 imaginary part if someone asks for one).
On Tuesday 23 January 2007 07:01, Tim Peters wrote:
complex_new() ends with:
cr.real -= ci.imag; cr.imag += ci.real;
and I have no idea what that thinks it's doing. Surely this isn't intended?!:
complex(complex(1.0, 2.0), complex(10.0, 20.0))
(-19+12j)
WTF? In any case, that's also what's destroying the sign of the imaginary part in complex(1.0, -0.0).
It seems pretty clear what it thinks it's doing -- namely, defining complex(a,b) = a + ib even when a,b are complex. And half of why it does that is clear: you want complex(a)=a when a is complex. Why b should be allowed to be complex too, though, it's hard to imagine. -- g
On 1/24/07, Gareth McCaughan <gareth.mccaughan@pobox.com> wrote:
complex(complex(1.0, 2.0), complex(10.0, 20.0))
(-19+12j)
WTF? In any case, that's also what's destroying the sign of the imaginary part in complex(1.0, -0.0).
It seems pretty clear what it thinks it's doing -- namely, defining complex(a,b) = a + ib even when a,b are complex. And half of why it does that is clear: you want complex(a)=a when a is complex. Why b should be allowed to be complex too, though, it's hard to imagine.
I think that's the right thing to do, because that is mathematically correct. j is just an imaginary number with a property that j*j = -1. So (a+bj) + (c+dj)j = (a-d) + (b+c)j. Complex numbers are not just magic pairs with two numbers and have actual mathematical rules.
On Wednesday 24 January 2007 08:39, Alexey Borzenkov wrote: [me, about complex():]
It seems pretty clear what it thinks it's doing -- namely, defining complex(a,b) = a + ib even when a,b are complex. And half of why it does that is clear: you want complex(a)=a when a is complex. Why b should be allowed to be complex too, though, it's hard to imagine.
[Alexey:]
I think that's the right thing to do, because that is mathematically correct. j is just an imaginary number with a property that j*j = -1. So (a+bj) + (c+dj)j = (a-d) + (b+c)j.
Yes, thanks, I know what j is, and I know how to multiply complex numbers. (All of which you could have deduced from reading what I wrote, as it happens.) The question is whether it makes sense to define complex(a,b) = a+ib for all a,b or whether the two-argument form is always in practice going to be used with real numbers[1]. If it is, which seems pretty plausible to me, then changing complex() to complain when passed two complex numbers would (1) notify users sooner when they have errors in their programs, (2) simplify the code, and (3) avoid the arguably broken behaviour Tim was remarking on, where complex(-0.0).real is +0 instead of -0. [1] For the avoidance of ambiguity: "real" is not synonymous with "double-precision floating-point".
Complex numbers are not just magic pairs with two numbers and have actual mathematical rules.
Gosh, and there I was thinking that complex numbers were magic pairs where you just make stuff up at random. Silly me. -- Gareth McCaughan PhD in pure mathematics, University of Cambridge
On 1/24/07, Gareth McCaughan <gareth.mccaughan@pobox.com> wrote:
[Alexey:]
I think that's the right thing to do, because that is mathematically correct. j is just an imaginary number with a property that j*j = -1. So
(a+bj) + (c+dj)j = (a-d) + (b+c)j.
Yes, thanks, I know what j is, and I know how to multiply complex numbers. (All of which you could have deduced from reading what I wrote, as it happens.) The question is whether it makes sense to define complex(a,b) = a+ib for all a,b or whether the two-argument form is always in practice going to be used with real numbers[1]. If it is, which seems pretty plausible to me, then changing complex() to complain when passed two complex numbers would (1) notify users sooner when they have errors in their programs, (2) simplify the code, and (3) avoid the arguably broken behaviour Tim was remarking on, where complex(-0.0).real is +0 instead of -0.
Haven't looked in source code for complex constructor yet, but I think that if it changes sign of -0.0 then it just does something wrong and needs fixing without change in behaviour. Maybe it could check if numbers it got on input are real or complex and proceed accordingly so that it never gets to computing -0.0-(+0.0), i.e. when second argument is not a complex number it could just add it to imaginary part of first argument, but skip substracting inexistant 0.0 from first argument's real part. Change of behaviour like ignoring imaginary part of second argument seems bad to me, and that's my only point. Besides, documentation (at least for Python 2.4) clearly states that second argument can be a complex number: *complex*( [real[, imag]]) Create a complex number with the value *real + imag*j* or convert a string or number to a complex number. If the first parameter is a string, it will be interpreted as a complex number and the function must be called without a second parameter. The second parameter can never be a string. Each argument may be *any numeric type* (*including complex*). If imag is omitted, it defaults to zero and the function serves as a numeric conversion function like int(), long() and float(). If both arguments are omitted, returns 0j.
On Wednesday 24 January 2007 10:20, Alexey Borzenkov wrote:
I think that's the right thing to do, because that is mathematically correct. j is just an imaginary number with a property that j*j = -1. So
(a+bj) + (c+dj)j = (a-d) + (b+c)j.
Yes, thanks, I know what j is, and I know how to multiply complex numbers. (All of which you could have deduced from reading what I wrote, as it happens.) The question is whether it makes sense to define complex(a,b) = a+ib for all a,b or whether the two-argument form is always in practice going to be used with real numbers[1]. If it is, which seems pretty plausible to me, then changing complex() to complain when passed two complex numbers would (1) notify users sooner when they have errors in their programs, (2) simplify the code, and (3) avoid the arguably broken behaviour Tim was remarking on, where complex(-0.0).real is +0 instead of -0.
Haven't looked in source code for complex constructor yet, but I think that if it changes sign of -0.0 then it just does something wrong and needs fixing without change in behaviour. Maybe it could check if numbers it got on input are real or complex and proceed accordingly so that it never gets to computing -0.0-(+0.0), i.e. when second argument is not a complex number it could just add it to imaginary part of first argument, but skip substracting inexistant 0.0 from first argument's real part. Change of behaviour like ignoring imaginary part of second argument seems bad to me, and that's my only point. Besides, documentation (at least for Python 2.4) clearly states that second argument can be a complex number:
I'm not suggesting that it should ignore the imaginary part of the second argument, and I don't think anyone else is either. What might make sense is for passing a complex second argument to raise an exception. (I don't particularly object to the present behaviour either.) The fact that the documentation states explicitly that the second argument can be a complex number is probably sufficient reason for not changing the behaviour, at least before 3.0. -- g
"Guido van Rossum" <guido@python.org> wrote:
That really sucks, especially since the whole point of making int division return a float was to make the integers embedded in the floats... I think the best solution would be to remove the definition of % (and then also for divmod()) for floats altogether, deferring to math.fmod() instead.
Please, NO!!! At least not without changing the specification of the math module. The problem with it is that it is specified to be a mapping of the underlying C library, complete with its <censored> error handling. fmod isn't bad, as <math.h> goes, BUT: God alone knows what happens with fmod(x,0.0), let alone fmod(x,-0.0). C99 says that it is implementation-defined whether a domain error occurs or the function returns zero, but domain errors are defined behaviour only in C90 (and not in C99!) It is properly defined only if Annex F is in effect (with all the consequences that implies). Note that I am not saying that syntactic support is needed, because Fortran gets along perfectly well with this as a function. All I am saying is that we want a defined function with decent error handling! Returning a NaN is fine on systems with proper NaN support, which is why C99 Annex F fmod is OK.
For ints and floats, real could just return self, and imag could return a 0 of the same type as self. I guess the conjugate() function could also just return self (although I see that conjugate() for a complex with a zero imaginary part returns something whose imaginary part is -0; is that intentional? I'd rather not have to do that when the input is an int or float, what do you think?)
I don't see the problem in doing that - WHEN implicit conversion to a smaller domain, losing information, raises an exception. The errors caused by needing a 'cast' (including Fortran INT, DBLE and (ugh) COMPLEX, here) causing not just conversion but information loss have caused major trouble for as long as I have been around. Regards, Nick Maclaren, University of Cambridge Computing Service, New Museums Site, Pembroke Street, Cambridge CB2 3QH, England. Email: nmm1@cam.ac.uk Tel.: +44 1223 334761 Fax: +44 1223 334679
[Guido]
That really sucks, especially since the whole point of making int division return a float was to make the integers embedded in the floats... I think the best solution would be to remove the definition of % (and then also for divmod()) for floats altogether, deferring to math.fmod() instead.
[Nick Maclaren]
Please, NO!!!
At least not without changing the specification of the math module. The problem with it is that it is specified to be a mapping of the underlying C library, complete with its <censored> error handling. fmod isn't bad, as <math.h> goes, BUT:
God alone knows what happens with fmod(x,0.0), let alone fmod(x,-0.0). C99 says that it is implementation-defined whether a domain error occurs or the function returns zero, but domain errors are defined behaviour only in C90 (and not in C99!) It is properly defined only if Annex F is in effect (with all the consequences that implies).
Note that I am not saying that syntactic support is needed, because Fortran gets along perfectly well with this as a function. All I am saying is that we want a defined function with decent error handling! Returning a NaN is fine on systems with proper NaN support, which is why C99 Annex F fmod is OK.
math.fmod is 15 years old -- whether or not someone likes it has nothing to do with whether Python should stop trying to use the current integer-derived meaning of % for floats. On occasion we've added additional error checking around functions inherited from C. But adding code to return a NaN has never been done. If you want special error checking added to the math.fmod wrapper, it would be easiest to "sell" by far to request that it raise ZeroDivisionError (as integer mod does) for a modulus of 0, or ValueError (Python's historic mapping of libm EDOM, and what Python's fmod(1, 0) already does on some platforms). The `decimal` module raises InvalidOperation in this case, but that exception is specific to the `decimal` module for now.
For ints and floats, real could just return self, and imag could return a 0 of the same type as self. I guess the conjugate() function could also just return self (although I see that conjugate() for a complex with a zero imaginary part returns something whose imaginary part is -0; is that intentional? I'd rather not have to do that when the input is an int or float, what do you think?)
I don't see the problem in doing that - WHEN implicit conversion to a smaller domain, losing information, raises an exception.
Take it as a pragmatic fact that it wouldn't. Besides, e.g., the conjugate of 10**50000 is exactly 10**50000 mathematically. Why raise an exception just because it can't be represented as a float? The exact result is easily supplied with a few lines of "obviously correct" implementation code (incref `self` and return it).
Guido van Rossum wrote:
The ints aren't really embedded in Decimal, so we don't have to do that there (but we could).
-0. If we can't achieve it without disturbing the rest of Python, I'll try as much as possible to keep what the Spec proposes. Regards, -- . Facundo . Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/
[Guido]
The ints aren't really embedded in Decimal, so we don't have to do that there (but we could).
[Facundo Batista]
-0.
If we can't achieve it without disturbing the rest of Python, I'll try as much as possible to keep what the Spec proposes.
Which "Spec"? For example, floor division isn't mentioned at all in IBM's proposed decimal standard, or in PEP 327, or in the Python Library Reference section on `decimal`. It's an artifact of trying to extend Python's integer mod definition to floats, and for reasons explained in this thread (for the 27th time ;-)), that definition doesn't make good sense for floats. The IBM spec defines `remainder` and `remainder-near` for floats, and those do make good sense for floats. But they're /different/ definitions than Python uses for integer mod. Do note that this discussion is only about Python 3. Under the view that it was a (minor, but real) design error to /try/ extending Python's integer mod definition to floats, if __mod__, and __divmod__ and __floordiv__ go away for binary floats in P3K they should certainly go away for decimal floats in P3K too. And that's about syntax, not functionality: the IBM spec's "remainder" and "remainder-near" still need to be there, it's "just" that a user would have to get at "remainder" in a more long-winded way (analogous to that a P3K user would have to spell out "math.fmod" to get at a mod function for binary floats).
Tim> For example, floor division isn't mentioned at all in IBM's Tim> proposed decimal standard, or in PEP 327, or in the Python Library Tim> Reference section on `decimal`. It's an artifact of trying to Tim> extend Python's integer mod definition to floats, and for reasons Tim> explained in this thread (for the 27th time ;-)), that definition Tim> doesn't make good sense for floats.... Tim> Do note that this discussion is only about Python 3.... Since Python 3 is about cleaning things up (at least in part) and Tim is telling us this is going to clean things up in this area, I'm +1 on this. Besides, I've found it's generally best to agree with Tim. ;-) Skip
Tim Peters wrote:
Which "Spec"? For example, floor division isn't mentioned at all in IBM's proposed decimal standard, or in PEP 327, or in the Python
Oops, you're right. My fault, sorry.
Library Reference section on `decimal`. It's an artifact of trying to extend Python's integer mod definition to floats, and for reasons explained in this thread (for the 27th time ;-)), that definition doesn't make good sense for floats. The IBM spec defines `remainder` and `remainder-near` for floats, and those do make good sense for floats. But they're /different/ definitions than Python uses for integer mod.
I knew the definition of IBM, but assumed (inmersed in my ignorance) that "remainder" was the way that standard float worked for __mod__. Now I know that it's not like that, :)
Do note that this discussion is only about Python 3. Under the view that it was a (minor, but real) design error to /try/ extending Python's integer mod definition to floats, if __mod__, and __divmod__ and __floordiv__ go away for binary floats in P3K they should certainly go away for decimal floats in P3K too. And that's about syntax, not functionality: the IBM spec's "remainder" and "remainder-near" still need to be there, it's "just" that a user would have to get at "remainder" in a more long-winded way (analogous to
We'll have to deprecate that functionality, with proper warnings (take not I'm not following the thread that discuss the migration path to 3k). And we'll have to add the method "remainder" to decimal objects (right now we have only "remainder_near" in decimal objects, and both "remainder" and "remainder_near" in context). Regards, -- . Facundo . Blog: http://www.taniquetil.com.ar/plog/ PyAr: http://www.python.org/ar/
Hi Tim, On Sun, Jan 21, 2007 at 09:08:18PM -0500, Tim Peters wrote:
BTW - isn't that case in contradiction with the general Python rule that if b > 0, then a % b should return a number between 0 included and b excluded?
Sure.
You're not addressing my point, though, so I was probably not clear enough. My surprize was not that a % b was equal to b in this case, and so not strictly smaller than b (because I expect such things when working with floats). My surprize was that Decimals don't even try to be between 0 and b, and sometimes give a result that is far outside the range. This surprized me because I expected floats and Decimals to try to give "approximately equal" results... A bientot, Armin
[Armin]
BTW - isn't that case in contradiction with the general Python rule that if b > 0, then a % b should return a number between 0 included and b excluded?
[Tim]
Sure.
[Armin]
You're not addressing my point, though, so I was probably not clear enough.
"Sure" is the answer to all possible points :-)
My surprize was not that a % b was equal to b in this case, and so not strictly smaller than b (because I expect such things when working with floats).
That's unnecessarily pessimistic: there's no problem defining modular reduction for floats that's exact.
My surprize was that Decimals don't even try to be between 0 and b, and sometimes give a result that is far outside the range. This surprized me because I expected floats and Decimals to try to give "approximately equal" results...
Sure, you do expect that, and sure, they don't. Now what? The `decimal` module is an implementation of IBM's proposed standard for decimal arithmetic: http://www2.hursley.ibm.com/decimal/ It requires two mod-like operations, neither of which behave like Python's "number theoretic" mod. Nobody involved cared to extend the proposed standard by adding a third mod-like operation. For some reason `decimal` implemented __mod__ as the proposed standard's "remainder" operation. That's the immediate source of your surprise. IMO `decimal` should not have implemented __mod__ at all, as Python's number-theoretic mod is not part of the proposed standard, is a poor basis for a floating-point mod regardess, and it was a mistake to implement decimal % decimal in a way so visibly different from float % float and integer % integer: it confuses the meaning of "%". That's your complaint, right? My preferred "solution" is to remove __mod__, __divmod__, and __floordiv__ from all flavors of floats (both binary and decimal) in P3K. That addresses your concern in that "decimal % decimal" would raise an exception in P3K (it wouldn't be implemented). A user who wanted some form of decimal float modular reduction would need to read the docs, decide which of the two supported such functions they want, and explicitly ask for it. Similarly, a user who wanted some from of binary float modular reduction would need to invoke math.fmod() explicitly -- or, possibly, if someone contributes the code needed to implement C99's additional "remainder" function cross-platform, that too: IBM's spec same-as C ---------- --------- remainder fmod (C89 & C99) remainder-near remainder (C99 & IEEE-754) It's not a coincidence that all standards addressing modular reduction for floats converge on essentially those two definitions. I can't be made to feel guilty about this :-) For P2 I don't propose any changes -- and in which case my only response is just "sure - yes - right - decimal's __mod__ in fact does not act like Python's integer __mod__ in mixed-sign cases -- and neither does decimal's __floordiv__ or decimal's __divmod__ act like their integer counterparts in mixed-sign cases".
Hi Tim, On Tue, Jan 23, 2007 at 05:14:29PM -0500, Tim Peters wrote:
For some reason `decimal` implemented __mod__ as the proposed standard's "remainder" operation. That's the immediate source of your surprise. IMO `decimal` should not have implemented __mod__ at all, as Python's number-theoretic mod is not part of the proposed standard, is a poor basis for a floating-point mod regardess, and it was a mistake to implement decimal % decimal in a way so visibly different from float % float and integer % integer: it confuses the meaning of "%". That's your complaint, right?
Thanks for the clarification. Yes, it makes sense that __mod__, __divmod__ and __floordiv__ on float and decimal would eventually follow the same path as for complex (where they make even less sense and already raise a DeprecationWarning). A bientot, Armin.
The only thing I would miss about this is that I am used to write certain timing loops that like to sync on whole seconds, by taking time.time() % 1.0 which nicely gives me the milliseconds in the current second. E.g. while True: do_something_expensive_once_a_second_on_the_second() now = time.time() time.sleep(1.0 - (now % 1.0)) I guess I could use (now - int(now)) in a pinch, assuming int() continues to truncate. --Guido On 1/25/07, Armin Rigo <arigo@tunes.org> wrote:
Hi Tim,
On Tue, Jan 23, 2007 at 05:14:29PM -0500, Tim Peters wrote:
For some reason `decimal` implemented __mod__ as the proposed standard's "remainder" operation. That's the immediate source of your surprise. IMO `decimal` should not have implemented __mod__ at all, as Python's number-theoretic mod is not part of the proposed standard, is a poor basis for a floating-point mod regardess, and it was a mistake to implement decimal % decimal in a way so visibly different from float % float and integer % integer: it confuses the meaning of "%". That's your complaint, right?
Thanks for the clarification. Yes, it makes sense that __mod__, __divmod__ and __floordiv__ on float and decimal would eventually follow the same path as for complex (where they make even less sense and already raise a DeprecationWarning).
A bientot,
Armin. _______________________________________________ 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/guido%40python.org
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
[Guido]
The only thing I would miss about this is that I am used to write certain timing loops that like to sync on whole seconds, by taking time.time() % 1.0 which nicely gives me the milliseconds in the current second. E.g.
while True: do_something_expensive_once_a_second_on_the_second() now = time.time() time.sleep(1.0 - (now % 1.0))
Indeed, the only use of floating % in the standard library I recall is the ancient return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0 from the original Wichman-Hill random() generator. Maybe we could introduce "%" as a unary prefix operator, where %x means "the fractional part of x" ;-) Are you opposed to importing `math`? The infix spelling had important speed benefits in random.py (random() was the time-critical function in that module), but the use above couldn't care less. time.sleep(1.0 - math.fmod(now, 1.0)) would do the same, except would be easier to reason about because it's trivially guaranteed that 0.0 <= math.fmod(x, 1.0) < 1.0 for any finite float x >= 0.0. The same may or may not be true of % (I would have to think about that, and craft a proof one way or the other -- if it is true, it would have to invoke something special about the modulus 1.0, as the inequality doesn't hold for % for some other modulus values). Better, you could use the more obvious: time.sleep(math.ceil(now) - now) That "says" as directly as possible that you want the number of seconds needed to reach the next integer value (or 0, if `now` is already an integer).
I guess I could use (now - int(now)) in a pinch,
That would need to be time.sleep(1.0 - (now - int(now))) I'd use the `ceil` spelling myself, even today -- but I don't suffer "it's better if it's syntax or __builtin__" disease ;-)
assuming int() continues to truncate.
Has anyone suggested to change that? I'm not aware of any complaints or problems due to int() truncating. There have been requests to add new kinds of round-to-integer functions, but in addition to int().
On 1/25/07, Tim Peters <tim.peters@gmail.com> wrote:
[Guido]
The only thing I would miss about this is that I am used to write certain timing loops that like to sync on whole seconds, by taking time.time() % 1.0 which nicely gives me the milliseconds in the current second. E.g.
while True: do_something_expensive_once_a_second_on_the_second() now = time.time() time.sleep(1.0 - (now % 1.0))
Indeed, the only use of floating % in the standard library I recall is the ancient
return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0
from the original Wichman-Hill random() generator. Maybe we could introduce "%" as a unary prefix operator, where %x means "the fractional part of x" ;-)
Are you opposed to importing `math`? The infix spelling had important speed benefits in random.py (random() was the time-critical function in that module), but the use above couldn't care less.
time.sleep(1.0 - math.fmod(now, 1.0))
would do the same, except would be easier to reason about because it's trivially guaranteed that 0.0 <= math.fmod(x, 1.0) < 1.0 for any finite float x >= 0.0. The same may or may not be true of % (I would have to think about that, and craft a proof one way or the other -- if it is true, it would have to invoke something special about the modulus 1.0, as the inequality doesn't hold for % for some other modulus values).
I don't care about the speed, but having to import math (which I otherwise almost never need) is a distraction, and (perhaps more so) I can never remember whether it's modf() or fmod() that I want.
Better, you could use the more obvious:
time.sleep(math.ceil(now) - now)
That "says" as directly as possible that you want the number of seconds needed to reach the next integer value (or 0, if `now` is already an integer).
Yeah, once math is imported the possibilities are endless. :-)
I guess I could use (now - int(now)) in a pinch,
That would need to be
time.sleep(1.0 - (now - int(now)))
I'd use the `ceil` spelling myself, even today -- but I don't suffer "it's better if it's syntax or __builtin__" disease ;-)
assuming int() continues to truncate.
Has anyone suggested to change that? I'm not aware of any complaints or problems due to int() truncating. There have been requests to add new kinds of round-to-integer functions, but in addition to int().
I thought those semantics were kind of poorly specified. But maybe that was long ago (when int() just did whatever (int) did in C) and it's part of the language now. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
[Guido]
... I don't care about the speed, but having to import math (which I otherwise almost never need) is a distraction, and (perhaps more so) I can never remember whether it's modf() or fmod() that I want.
fractional part of x == fmod(x, 1.0) == modf(x)[0], so you could use either. Since modf returns a tuple and fmod returns a float, you'll get an exception quickly if you pick the wrong one :-) The name "modf" certainly sucks. ...
assuming int() continues to truncate.
Has anyone suggested to change that? I'm not aware of any complaints or problems due to int() truncating. There have been requests to add new kinds of round-to-integer functions, but in addition to int().
I thought those semantics were kind of poorly specified. But maybe that was long ago (when int() just did whatever (int) did in C) and it's part of the language now.
"(int)float_or_double" truncates in C (even in K&R C) /provided that/ the true result is representable as an int. Else behavior is undefined (may return -1, may cause a HW fault, ...). So Python uses C's modf() for float->int now, which is always defined for finite floats, and also truncates.
[Armin]
Thanks for the clarification. Yes, it makes sense that __mod__, __divmod__ and __floordiv__ on float and decimal would eventually follow the same path as for complex (where they make even less sense and already raise a DeprecationWarning).
This truly has nothing to do with complex. All meanings for "mod" (whether in Python, IEEE-754, C89, C99, or IBM's proposed decimal standard) are instances of this mathematical schema: [1] x%y = x - R(x/y)*y by definition, where R(z) is a specific way of rounding z to an exact mathematical ("infinite precision") integer. For int, long, and float, Python uses R=floor (and again it's important to note that these are mathematical statements, not statements about computer arithmetic). For ints, longs, and (mathematical) reals, that's the usual "number-theoretic" definition of mod, as, e.g., given by Knuth: mod(x, y) = x - floor(x/y)*y It's the only definition of all those mentioned here that guarantees the result is non-negative when the modulus (y) is positive, and that's a very nice property for integers. It's /also/ the only definition off all those mentioned here where the exact mathematical result may /not/ be exactly representable as a computer float when x and y are computer floats. It's that last point that makes it a poor definition for working with computer floats: for any other plausible way of defining "mod", the exact result /is/ exactly representable as a computer float. That makes reasoning much easier, just as you don't have to think twice about seeing abs() or unary minus applied to a float. No information is lost, and you can rely on expected invariants like [2] 0 <= abs(x%y) < abs(y) if y != 0 and finite provided one of the non- R=floor definitions of mod is used for computer floats. For complex, Python uses R(z) = floor(real_part_of(z)). AFAICT, Python just made that up out of thin air. There are no known use cases, and it's bizarre. For example, [2] isn't even approximately reliable: >>> x = 5000 + 100j >>> y = 1j >>> x % y (5000+0j) >>> print abs(x%y), abs(y) 5000.0 1.0 In short, while Python "does something" for complex % complex, what it does seems more-than-less arbitrary. That's why it was deprecated years ago, and nobody complained. But for computer floats, there are ways to instantiate R in [1] that work fine, returning a result that is truly (exactly) congruent to x modulo y, even though x, y and the result are all computer floats. Two of those ways: The C89 fmod = C99 fmod = C99 integral "%" = IBM spec "remainder" picks R(z) = round z to the closest integer in the direction of 0. The C99 "remainder" = IBM spec "remainder-near" = IEEE-754 REM picks R(z) = round z to the nearest integer, or if z is exactly halfway between integers to the nearest even integer. This one has the nice property (for floats!) that [2] can be strengthened to: 0 <= abs(x%y) <= abs(y)/2 That's often useful for argument reduction of periodic functions, which is an important use case for a floating-point mod. You typically don't care about the sign of the result in that case, but do want the absolute value as small as possible. That's probably why this was the only definition standardized by 754. In contrast, I don't know of any use for complex %.
participants (9)
-
Alexey Borzenkov
-
Armin Rigo
-
Facundo Batista
-
Gareth McCaughan
-
Guido van Rossum
-
Nick Maclaren
-
Raymond Hettinger
-
skip@pobox.com
-
Tim Peters