New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better document math.copysign behavior. #56420
Comments
I expected return int if I gave x as integer to copysign. I encounterd two problems. one:
>>> import math
>>> a = [0, 1, 2, 3]
>>> i = 1
>>> i_copysign = math.copysign(i, -1)
>>> a[i_copysign]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not float
two:
>>> n = 10 ** 20
>>> math.copysign(n + 1, 1) == n + 1
False |
"Except when explicitly noted otherwise, all return values are floats." On the other hand, copysign says "return x with the sign of y", which certainly sounds like it is preserving x, not creating a new float. So at the least there is a doc issue, I think. |
As with all the math docs, 'x' refers to the value, not the object with the value. However, "Return abs(x) with the sign of y" is, to me, clearer and more accurate. Both doc string and doc chapter should get any modification. |
abs() behavior show below.
>>> type(abs(-1))
<class 'int'>
>>> type(abs(-1.0))
<class 'float'> we should fix this problem if write I'd like to try this problem if need fix. |
How about something like: "Return a float with the magnitude of x but the sign of y."? The behaviour of math.copysign with respect to non-float inputs matches that of almost all the other math module functions: integer arguments are first converted to floats, and then the underlying libm function applied. I'm not convinced that changing the behaviour of copysign to produce integer results for integer argument would be a good idea. |
It occurred to me, also, that as currently written, copysign 'should' return the type of the first arg. In C89, and I suspect in Python 1.0, all math functions return double (Python float). Like Mark, I am more inclined to change the doc than the code.
def icopysign(j,k):
if (j > 0) and (k < 0) or (j < 0) and (k > 0):
return -j
return j
for j,k,i in ((1,1,1), (1,-1,-1), (-1,-1,-1), (-1, 1, 1),
(1,0,1), (-1,0,-1)):
assert icopysign(j,k) == i, (j,k,i) This would certainly be up for debate if we changed the code, but there should be no difference in outputs for same value inputs. (This principle is the reason for / and //.) So lets leave copysign as a function defined on floats with inputs coerced to float if needed. Anyone who needs it for ints can define it for (-1,0) according to their need. |
I made the patch. math.copysign(1, -0.)
returns 1. I hope to return -1. Please help me. |
umedoblock: David, Mark, and I agree that this should be a doc issue, and so I suggested a DOC patch. So I do not know why you are screwing around with the code, or what you are trying to do with it, or why you are messing around with the version headers or why you think this is a 3.2 only issue. |
sorry. but this patch contain new fail... math.copysign(-1., 0.)
returns 1. |
I attached DOC patch. I misunderstand ? I use just Python3.2. I didn't use Python 2.x, 3.0 and 3.1 in my programming life. |
Third party refers to things other than Pythonx.y code For instance, distutils2/distribute was for some years developed separately from the main codebase and was recently merged into the 3.3 repository. While separate, its issues were '3rd party'. That has nothing to do with this. A patch against 3.2 is fine. It will almost certainly apply unchanged to both 3.3 and 2.7 since this part of the doc may have never changed since written. The patch looks fine so far. I see that you kept the line just under 80 characters. Now, can you expand it to also change the docstring for this function in the mathmodule.c file? I am not exactly sure where it is in the file, relative to the function code itself. As I remember, it is not as convenient as in Python files. copysign(x, y)
Return x with the sign of y. When revising the "Return ..." part to match the doc, I think we should include the "On a platform ..." sentence also. If Mark disagrees, it would be easily removed. Notice that the indent is 1 or 2 more spaces, so the existing line would become too long. 'a platform' could be changed to 'platforms'. I personally like that better anyway. |
Sure, sounds good. One of the main things that makes copysign useful is that it distinguishes between -0.0 and 0.0. |
I'm late, sorry. I attached the patch for math.rst and mathmodule.c. |
Taken from http://www.slac.stanford.edu/comp/unix/package/rtems/doc/html/libm/libm.info.copysign.html i'd suggest to extend Return a float with the magnitude of x to Return a float with the magnitude (absolute value) of x It could probably help people less math-savvy in understand what's going to happen :) Maybe also (only in rest doc) might be nice to describe what happens in case the arguments are NaN, f.e.: >>> import math
>>> x = float('nan')
>>> math.copysign(1., x)
1.0
>>> math.copysign(-1., x)
1.0
>>> math.copysign(x, -1)
nan
>>> math.copysign(x, x)
nan umedoblock: would you like to expand the patch with these notes (unless someone objects :)). |
sandro: OK, I attached the new patch. |
well, what I actually meant is to describe the behavior in case one (or both) of the arguments is NaN (so not cut&pasting the code), while the example was just provided as a quick reference. |
I agree with adding '(absolute value)'. I think the following covers the NaN behavior. "NaN acts as a positive value that cannot be negated." This should be added to both doc and docstring. I do not think we generally specify the nan behavior for each function, but it usually follows general rules. The copysign(x,nan) behavior is not obvious as nan, like int 0, does not really have a sign. One might expect copysign(-1.0,nan) to be -1. |
New changeset 3ad7725b5013 by Andrew Kuchling in branch '3.3': |
Applied. I added two sentences describing the NaN behaviour. |
The paragraph about NaNs isn't correct. For example: >>> from math import copysign
>>> copysign(1.0, float('nan'))
1.0
>>> copysign(1.0, -float('nan'))
-1.0 Though it doesn't really make sense to talk about 'positive' or 'negative' NaNs, a NaN still has a sign bit, and that sign bit is used in the copysign operation. |
More examples, showing that >>> result1 = copysign(float('nan'), 1.0)
>>> result2 = copysign(float('nan'), -1.0)
>>> copysign(1.0, result1) # result1 is a NaN whose sign bit is cleared.
1.0
>>> copysign(1.0, result2) # result2 is a NaN whose sign bit is set.
-1.0 |
OK; I'll just drop the 'If y is NaN' sentence. |
Actually ISTM the 'if x is NaN' sentence can also go; it seems to just copy the sign bit no matter what x and y are. >>> from math import *
>>> nan = float('nan')
>>> neg = -nan
>>> copysign(+1, copysign(nan, neg))
-1.0 |
Yep, that's exactly what happens. The sign bit from y just gets blindly transferred to x, with no regard to any meaning of x or y. So it works exactly the same way for zeros, NaNs, infinities, or whatever. |
New changeset b01f4ed077fa by Andrew Kuchling in branch '3.3': |
Thanks! (And apologies for the nitpicking.) |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: