Thread: [q-lang-users] Exponentiation operator
Brought to you by:
agraef
From: Albert G. <Dr....@t-...> - 2006-06-16 18:22:20
|
The built-in ^ operator currently returns a floating point value in all cases. That makes sense, because it is supposed to work with negative and fractional exponents too, so this seems to be the only way to make ^ work consistently. For the case of integer bases and positive integer exponent there's the pow function which returns an Int value. Now I'm reconsidering this. Maybe it would be better if ^ actually returned an Int where this is possible, and a Float otherwise? (This wouldn't be possible in a language with static typing like Haskell, of course, but Q doesn't have this problem.) A similar issue arises with the definition of ^ for Rational. Should Rational ^ Int be a Rational? Or a Float, like Rational ^ Rational and Rational ^ Float are? Comments appreciated. :) Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Albert G. <Dr....@t-...> - 2006-06-16 19:07:09
|
A related question is whether rational arithmetic should return an Int instead of a Rational when the denominator of the result is 1. Rob, what is your take on this? In your current implementation it will always return a Rational, right? Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Rob H. <hub...@gm...> - 2006-06-16 19:22:19
|
Albert Graef wrote: > A related question is whether rational arithmetic should return an Int > instead of a Rational when the denominator of the result is 1. Rob, what > is your take on this? In your current implementation it will always > return a Rational, right? > > Albert > > Yes, currently a Rational is returned even if the value could be float. All the functions return a "normalised" value. Thus a simple way to implement the other behaviour is to change "normalisation" to reduce to Int when possible. There's a chance that some of my algorithms will need to be tweaked afterwards, as sometimes I assume in some private functions that a value is in the form (rat N D). Thus perhaps there should be separate "normalisers" for producing intermediate values, and for returning values to a the public interface. I think that the behaviour ought to follow the style of what you do with (^). There are pros and cons with either approach. On the one hand, always having a Rational gives you certainty of type. On the other hand, having a simpler type returned may be an advantage, especially when that's an intermediate value. Rob. |
From: Albert G. <Dr....@t-...> - 2006-06-16 21:04:36
|
Rob Hubbard wrote: > All the functions return a "normalised" value. Thus a simple way to > implement the other behaviour is to change "normalisation" to reduce to > Int when possible. You will find that those cases are already treated separately (for optimization purposes) in the prototype stdlib rational.q that I sent you in private mail. So the necessary changes are in fact trivial. > There's a chance that some of my algorithms will need > to be tweaked afterwards, as sometimes I assume in some private > functions that a value is in the form (rat N D). Those parts of your module will end up in the separate ratutils.q module and thus need to be rewritten anyway, because ratutils won't have access to the rat constructor any more (which is private to rational.q). I've already checked this, the necessary changes should be rather minimal. BTW, even if we don't decide for the "auto-simplification", I'd like to propose that the numerator, denominator and num_den functions should accept Int arguments, too, then _all_ operations in the rational base module would work on Rational and Int alike. This would also be analogous to complex.q, where 're' and 'im' are also defined on Real. > I think that the behaviour ought to follow the style of what you do with > (^). Ok, so that would mean that we never promote the result to a type less general that the argument types. Fair enough. That's what effectively happens in Haskell, too, AFAICS. John, you argued in favour of "auto-simplification", do you think you can also live without it? Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: Rob H. <hub...@gm...> - 2006-06-16 19:13:50
|
I would prefer either: (1) Always a float, or (2) A value of a type as simple as possible (up to the simplicity of the arguments perhaps): thus Int if possible, otherwise Rational if possible, otherwise Real if possible, otherwise (perhaps) Complex (e.g. (-1)^(1/2) would be complex). That is, I think either (1) no attempt at all should be made to return an exact value in a suitable type; a Float should be returned where possible (i.e. where defined and Real), or (2) a thorough attempt should be made. With (2), I would like to see (1 over 100)^(1 over 2) return (1 over 10). I would also expect the algorithm for (2) know that (N over 1) is an integer. On the other hand, I would not expect the algorithm to recognise that e.g. 0.5 = 1/2. It's fair for the algorithm to assume that a value expressed in a given inexact type can not be expressed exactly as a simpler type. [Perhaps there should be operators to do both.] When both (1) and (2) produce values, those values should be "equal" (in some sense; the problem is of course that you can't rely on testing equality of floats). That's quite a tall order as X^(1/N) has N complex values, so great care would need to be taken in choosing the "principal value". One possibility for the algorithm is to (a) first find a complex value, then (b) attempt a series of simplifications somehow. That doesn't really answer the question does it?? Rob. |
From: Albert G. <Dr....@t-...> - 2006-06-16 20:33:47
|
Rob Hubbard wrote: > (2) A value of a type as simple as possible (up to the simplicity of the > arguments perhaps): thus Int if possible, otherwise Rational if > possible, otherwise Real if possible, otherwise (perhaps) Complex (e.g. > (-1)^(1/2) would be complex). I suppose you mean "Float" rather than "Real" here. ("Real" is similar to "Num" in that it's an abstract type with no objects of its own. It's just a common umbrella for Int, Float and (soon) Rational.) > That is, I think either > (1) no attempt at all should be made to return an exact value in a > suitable type; a Float should be returned where possible (i.e. where > defined and Real), or > (2) a thorough attempt should be made. I foresee that (2), while desirable, would mean a lot of complication. In particular, we need to be able to check whether a given root of a bigint is again a bigint, _without_ converting to an inexact representation somewhere in that decision procedure. Thus I'm leaning towards solution (1). Q is not supposed to be a computer algebra system after all, and the pow function is there if an exact solution is needed, in cases where its existence is obvious. The rest should be the burden of the application programmer. :) So that means that exponentiation yields a Float result if it exists, and a Complex result otherwise. In fact this is the way it is implemented right now, but this would then also apply to the Rational type. > That doesn't really answer the question does it?? It does, since it helped to remind me that innocent-looking changes sometimes have dramatic consequences. ;-) Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |
From: John C. <co...@cc...> - 2006-06-16 19:23:18
|
Albert Graef scripsit: > Now I'm reconsidering this. Maybe it would be better if ^ actually > returned an Int where this is possible, and a Float otherwise? (This > wouldn't be possible in a language with static typing like Haskell, of > course, but Q doesn't have this problem.) A similar issue arises with > the definition of ^ for Rational. Should Rational ^ Int be a Rational? > Or a Float, like Rational ^ Rational and Rational ^ Float are? I'd split ^ into two operators, one to return a Float (perhaps ^) always, and one to return an exact result where possible, a Float where necessary (perhaps **, perhaps pow). > A related question is whether rational arithmetic should return an Int > instead of a Rational when the denominator of the result is 1. I think it should, yes. And likewise when the numerator is 0. I also think, for the same reasons, that a complex operator should return a Real when the imaginary part is 0 (but not 0.0), for reasons stated before. -- Knowledge studies others / Wisdom is self-known; John Cowan Muscle masters brothers / Self-mastery is bone; co...@cc... Content need never borrow / Ambition wanders blind; http://ccil.org/~cowan Vitality cleaves to the marrow / Leaving death behind. --Tao 33 (Bynner) |
From: Rob H. <hub...@gm...> - 2006-06-16 20:42:32
|
John Cowan wrote: > Albert Graef scripsit: > >> A related question is whether rational arithmetic should return an Int >> instead of a Rational when the denominator of the result is 1. >> > > I think it should, yes. And likewise when the numerator is 0. > The Rationals are normalised (that is, they are reduced to have a positive denominator, and coprime numerator and denominator). So, when the numerator is 0, the denominator is 1. Thus the denominator is 1 exactly when the overall value is integral. |
From: John C. <co...@cc...> - 2006-06-16 21:43:13
|
Albert Graef scripsit: > Ok, so that would mean that we never promote the result to a type less > general that the argument types. Fair enough. That's what effectively > happens in Haskell, too, AFAICS. > > John, you argued in favour of "auto-simplification", do you think you > can also live without it? I can live with it, I think, as long as all operators do appropriate argument promotion. In particular, the numeric comparisons should work properly in all cases except ordering involving complex numbers. I now think that ^ should always return a float (transcendental exponentiation) whereas pow should accept any exact left argument and integer right argument and return an integer or rational result. -- John Cowan <co...@cc...> http://www.ccil.org/~cowan But no living man am I! You look upon a woman. Eowyn I am, Eomund's daughter. You stand between me and my lord and kin. Begone, if you be not deathless. For living or dark undead, I will smite you if you touch him. |
From: Albert G. <Dr....@t-...> - 2006-06-16 22:02:34
|
John Cowan wrote: > I can live with it, I think, as long as all operators do appropriate > argument promotion. In particular, the numeric comparisons should work properly > in all cases except ordering involving complex numbers. Yes, that's the case already in the current release, and will still work in Q 7.2 when rational.q will be part of the standard library. > I now think that ^ should always return a float (transcendental exponentiation) > whereas pow should accept any exact left argument and integer right argument > and return an integer or rational result. Right, that's how it's going to be (I think that Rob's implementation already does this). Ok, so it looks like this is resolved. Back to coding. :) Thanks for the replies, Albert -- Dr. Albert Gr"af Dept. of Music-Informatics, University of Mainz, Germany Email: Dr....@t-..., ag...@mu... WWW: http://www.musikinformatik.uni-mainz.de/ag |