Skip to content
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

make x^y for integer x,y return a float? #745

Closed
StefanKarpinski opened this issue Apr 20, 2012 · 18 comments
Closed

make x^y for integer x,y return a float? #745

StefanKarpinski opened this issue Apr 20, 2012 · 18 comments
Assignees
Labels
breaking This change will break code
Milestone

Comments

@StefanKarpinski
Copy link
Member

We need to decide if x^y should return an int or a float when x and y are ints. Current behavior may be problematic and/or surprising. Since there are very few integer values of x and y where x^y can be computed exactly with integers, maybe it should default to floating point. In which case we may want to introduce ipow(x,y) as a shorter name for power_by_squaring.

@ghost ghost assigned StefanKarpinski Apr 20, 2012
@AntonioSaragga
Copy link

I wonder if we may have float arithmetic operations by default (this is the standard behavior in Matlab and R.). If we want integer arithmetic operations then we'll need to state it explicitly

Present behavior (Julia Windows)

2^500 = 0
2.0^500 = 3.273390607896142e150
123456 * 123456 = -1938485248
123456.0 * 123456 = 1.5241383936e10
float(123456) * 123456 = 1.5241383936e10

Proposed behavior (Julia Windows)

2^500 = 3.273390607896142e150
2.0^500 = 3.273390607896142e150
123456 * 123456 = 1.5241383936e10
123456.0 * 123456 = 1.5241383936e10
int32(123456)_123456 = 1.5241383936e10
int32(123456)_int32(123456) = -1938485248

@StefanKarpinski
Copy link
Member Author

You do state it explicitly by using integer types.

@JeffreySarnoff
Copy link
Contributor

if, when x and y are ints, (x^y) returns type T by default, then (x*y) [and (x+y), (x-y), (x/y), ...] should return type T by default.
That internal consistency is necessary to any fluid manner of realizing reliably transportable software; and the consistency facilitates software that plays well with less effortful consideration of others.

It would be better to have the default be int x int --> float and introduce a complete set of recognizably similar operators to designate int x int --> int than to mix default return types for operations (and functions) 'of a kind'.

The alternative would be having the default as int x int --> int for operations (and functions) 'of a kind', and using some floatifying notation when int x int --> float is desired.

.

@StefanKarpinski
Copy link
Member Author

Making integer arithmetic return a float is nuts because it means, among other things, that you can't write a simple loop using integer arithmetic. Adding two integers should and will produce an integer. x^y is very different from operations like x+y, x-y and x*y because the power function isn't really arithmetic. Similarly, x/y is a different beast and returns a float even when the arguments are ints.

@JeffreySarnoff
Copy link
Contributor

" Making integer arithmetic return a float is nuts " -- true that

'x^y' is not always very different from, e.g. 'x_y' (for integers, x_x is
x^2, and the two operations are explicitly interchanged in e.g. the horner
form of lower-order polynomials having integer coeffs).

For me, it is nicer and saves development time, when numeric ops with
integer inputs respond with an integer result by default, and when not
possible, with an exception, or maybe a testable symbol for an unspecified
linteger > maxint.

On Thu, Apr 26, 2012 at 4:23 PM, Stefan Karpinski <
reply@reply.github.com

wrote:

Making integer arithmetic return a float is nuts because it means, among
other things, that you can't write a simple loop using integer arithmetic.
Adding two integers should and will produce an integer. x^y is very
different from operations like x+y, x-y and x*y because the power
function isn't really arithmetic. Similarly, x/y is a different beast and
returns a float even when the arguments are ints.


Reply to this email directly or view it on GitHub:
#745 (comment)

@StefanKarpinski
Copy link
Member Author

That is, in fact, how things work at the moment except that int^int overflows and gives zero for large values. The reason that x^y is fundamentally non-arithmetic is that the power, y, is not constant. Thus, the implementation requires a loop and depends on one of the inputs in a non-polynomial (i.e. non-arithmetic) manner. Arguably, x^y is more like log(x) or exp(y). The biggest issue I have with making x^y return a float for integer values is in fact that for constants like 2, we want x^2 to basically just be a shorthand for x*x, which such a change would break.

@JeffreySarnoff
Copy link
Contributor

+1 vote for respecting the equivalence.

Why does zero result when 'x^y' overflows for integer x,y?

On Thu, Apr 26, 2012 at 5:57 PM, Stefan Karpinski <
reply@reply.github.com

wrote:

That is, in fact, how things work at the moment except that int^int
overflows and gives zero for large values. The reason that x^y is
fundamentally non-arithmetic is that the power, y, is not constant. Thus,
the implementation requires a loop and depends on one of the inputs in a
non-polynomial (i.e. arithmetic) manner. Arguably, x^y is more like
log(x) or exp(y). The biggest issue I have with making x^y return a
float for integer values is in fact that for constants like 2, we want
x^2 to basically just be a shorthand for x*x, which such a change would
break.


Reply to this email directly or view it on GitHub:
#745 (comment)

@StefanKarpinski
Copy link
Member Author

Because that's how integer arithmetic works on x86 (at least that's how the code that LLVM generates works).

@JeffBezanson
Copy link
Member

I agree with Stefan that + and / are not the same --- integers are closed under one and not the other.
But I think respecting the equivalence of integer ^ (nonnegative integer) to repeated multiplication is more important than most other concerns.

We should consider making integer ^ (negative integer) an error. And the same for sqrt(negative) or negative^noninteger. Some of these currently give NaN, which is not exactly useful. Throwing a domain error instead at least tells you where the problem arose.

@StefanKarpinski
Copy link
Member Author

I suspect that making sqrt(negative float) throw an error may be a performance issue. The question that instigated this whole discussion was not what to do about 2^-64, which works fine now, but rather what to do about 2^64 which gives zero. There is some overlap between these issues, but I have far less problem with a type instability that depends only on the sign of y than on the values of both x and y.

@JeffBezanson
Copy link
Member

2^64 gives the correct answer mod 2^64, consistent with all our integer arithmetic. Arguably, when we can't give an appropriate answer there should be a domain error. But, giving a float for integer^negative is at least better than giving a NaN. I'm pretty sure the only reason NaN exists is that people don't want to agree on exception behavior at the systems programming level (oddly, it exists at the hardware level, then goes away in C, then comes back in HLLs). In an environment with exceptions it seems you have to throw instead of returning NaN when possible.

@JeffBezanson
Copy link
Member

Historically, matlab only had doubles, and added true integer data types much later. We want integers to be integers from the beginning, so an integer literal is of an integer type. I understand the "command line comfort" of wanting to get the "right answer" for any typed expression, but at the end of the day this is really just a minor nuisance.

The portability question is a totally different one. It might be good to make Int 64 bits on all platforms, but we give up a significant amount of performance on 32-bit to do so. The slowdown is not enough to be the end of the world, but enough to worry about. It's a difficult tradeoff.

@Keno
Copy link
Member

Keno commented Apr 27, 2012

I'm still in favor off having overflow throw an exception, but being able to disable the check through a command line flag

@AntonioSaragga
Copy link

Yes I totally agree. It would be much better if optionally overflow could throw an exception (since getting Inf it appears has its own drawbacks).

@JeffBezanson
Copy link
Member

Yes, having the ability to check for any kind of sketchy numerical thing is a good feature. The only question is what to enable by default, and we will make tradeoffs to balance performance and safety.

At some point I'll check the cost of converting NaN to errors. We can add intrinsics for that and it will probably be worth it. Finding out exactly where a NaN (or overflow) arose could be really useful. And nobody wants to deal with errno and feclearexcept and fetestexcept. Ew.

@StefanKarpinski
Copy link
Member Author

Resolved: make (x::Int)^(y::Int) throw a domain error when x and y will cause overflow and when y < 0.

@mschauer
Copy link
Contributor

For some reason this popped up on Github and reading "throw a domain error when x and y will cause overflow" just the remark: That is not the case currently and I keep running into this, because the notation 10^n is used to enter constants which when run on systems with Int==Int32 instead of Int=Int64 silently overflow.

@mschauer
Copy link
Contributor

I filed an issue #5573

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking This change will break code
Projects
None yet
Development

No branches or pull requests

6 participants