Thread: Overloading arithmetic operators and commutativity

  1. #1
    Registered User
    Join Date
    Apr 2010
    Posts
    88

    Overloading arithmetic operators and commutativity

    I've been doing work with 3D vectors, and so I decided to overload the +,-, and * operators to make basic algebra operations more intuitive to program with. I've succeeded with accurate results but I have a question about overloading * to denote multipliying a vector by a scalar. Precedence and associativity work for multiplication by a scalar, as they do for the regular * operator.


    Code:
    class Vect{
    
    private:
    
    public:
    
      unsigned dimension;
    
      float x;
      float y;
      float z;
      
    };
    
    Vect operator*(float scalar, Vect the_vect){
    
      int i=0;
      Vect scalar_product;
    	
      scalar_product.x = scalar*(the_vect.x);
      scalar_product.y = scalar*(the_vect.y);
      scalar_product.z = scalar*(the_vect.z);
    
      return scalar_product;  
      
    }
    
    Vect operator*( Vect the_vect, float scalar){
    
      int i=0;
      Vect scalar_product;
    	
      scalar_product.x = scalar*(the_vect.x);
      scalar_product.y = scalar*(the_vect.y);
      scalar_product.z = scalar*(the_vect.z);
    
      return scalar_product;  
      
    }
    The nearly identical declaration, aside from order of the arguments was the only way I could figure out to allow me type either:

    Code:
    vect1 = 7.15*vect1;
    OR
    Code:
    vect1 = vect1*7.15;
    Is there another way to overload the * operator once and to request it to disregard the order of the arguments? This didn't come up with + or - because the arguments for those were both of type Vect, whereas here I've got a float and my Vect type.

    Any thoughts?
    Last edited by Ocifer; 05-28-2011 at 10:33 PM.

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    * doesn't have to be commutative (i.e. in this context it isn't, if you multiply two matrices, right?), so there's no way to force the issue.

  3. #3
    Registered User
    Join Date
    Apr 2010
    Posts
    88
    You are right that two arbitary matrices don't necessarily commute, but that's not quite what I meant.

    I read on a cpp reference page that though we can change what the operators return, and how they behave with our created classes, we cannot change the precedence of their evaluation.

    All I meant was that the operators +,-,* if construed as vector addition, vector subtraction, and the multiplication of a vector by a scalar (real number) -- they all conform to how +, - , * are evaluated under BEDMAS / order of operations.

    As a counter example, I can't think of a non-crack way to make caret ^ work as an exponent operator within C++. Why? Since it is by convention in use for bitwise XOR, meaning it must follow the logic of bitwise XOR.

    A XOR B is equivalent to B XOR A for all A and B. So if we get A ^ B = B ^ A

    Yet this is not necessarily true for two real numbers A and B, if we mean to overload ^ to work as exponentiation as it does in some languages or programs.

    One can come up with an infinite number of pairs of reals (A,B) such that A to power of B is not equivalent to B to the power of A -- that is such that A^B != B^A

    If we are meaning to double up ^ in usage for both XOR and exponentiating reals, for example, we are hard pressed, because we cannot tell C to change the commutativity or associativity of a hard-wired symbol because it messes with the larger algebra.

    I will not be needing to overload for usage of matrices, rather only vectors. In fact with the above code I was able to get * to function such that

    Code:
    7.15*vect1 = vect1*7.15
    as one would expect from algebra rules.

    Given that I do intend to overload * as a commutative operator on a vector and a scalar, does anyone have a more elegant way to achieve what I've done?
    Last edited by Ocifer; 05-28-2011 at 11:43 PM.

  4. #4
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by Ocifer View Post
    You are right that two arbitary matrices don't necessarily commute, but that's not quite what I meant.
    As a matter of fact, you asked for that. Asking for the same function to be called for 2.0*a_vector and for a_vector*3.0 means you want them to commute in this particular case.

    Quote Originally Posted by Ocifer View Post
    Does anyone have a more elegant way to achieve what I've done?
    You still need to provide both operators, but can implement one in terms of the other.

    For example;
    Code:
    Vect operator*(Vect the_vect, float scalar)
    {
         return operator*(scalar, the_vect);
    }
    Note that, generally speaking, it is often better to pass class arguments by const reference than by value. As in
    Code:
    Vect operator*(const Vect &the_vect, float scalar)
    {
         return operator*(scalar, the_vect);
    }
    The benefits of this include avoiding the overhead of creating a temporary copy of vectors passed by the caller.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  5. #5
    Registered User
    Join Date
    Apr 2010
    Posts
    88
    Thanks for the const tip, I've just recently switched to C++.

    To clarify, all I meant was that I wasn't even trying to make * work as matrix multiplication, but rather as multiplication of a vector by a scalar. I'm only working with n-dimensional vectors at the moment.

    N.B. If one were to overload the binary operator * to function as matrix multiplication, it would actually not be desirable to make it call the same routine for A*B and B*A as there is no guarantee that two matrices commute. There is however no counter example to a*M = M*a when a is a real or complex number and M is a matrix. That's all I was getting at. In an extended line, assuming you have some Matrix and Vector classes, say:

    Code:
    Vector V;
    Matrix A, B, Product;
    float scalar = 3.14159;
    
    //imagine A,B, V are initialized
    
    Product = A*B*A*B*A*B; /*Which is not necessarily the same as...*/
    Product = A*A*A*B*B*B;
    
    //vs
    
    Product = scalar*A*scalar*A*scalar*A*scalar /*which would equal*/
    Product = scalar*scalar*scalar*A*A*A;
    + and * and work as basic vector addition and mult. by a scalar (in terms of mathematical vector spaces), and so it's desirable that we make make scalar mult function which allows us to factor the scalar term anywhere. We cannot rearrange our product nicely in the case of arbitrary matrices A and B, as we've no guarantee that they commute. That's all I was I getting at. In the first call, the * would need to overloaded to perform matrix multiplication where left and right multiplication is an issue. Left and right multiplication is not an issue with multiplication by a scalar, in terms of the mathematical result. It only seems to matter to the C++ prototype convention.
    Last edited by Ocifer; 05-29-2011 at 12:20 AM.

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Ocifer View Post
    As a counter example, I can't think of a non-crack way to make caret ^ work as an exponent operator within C++. Why? Since it is by convention in use for bitwise XOR, meaning it must follow the logic of bitwise XOR.

    A XOR B is equivalent to B XOR A for all A and B. So if we get A ^ B = B ^ A

    Yet this is not necessarily true for two real numbers A and B, if we mean to overload ^ to work as exponentiation as it does in some languages or programs.

    One can come up with an infinite number of pairs of reals (A,B) such that A to power of B is not equivalent to B to the power of A -- that is such that A^B != B^A

    If we are meaning to double up ^ in usage for both XOR and exponentiating reals, for example, we are hard pressed, because we cannot tell C to change the commutativity or associativity of a hard-wired symbol because it messes with the larger algebra.
    If any of this was true, you'd have a point. Since it's all a crock, you don't. Once you define a class, you can define operator^(your_class, double) to be completely different from operator^(double, your_class) -- different types and all.

    (EDIT: And actually it's important to keep the two types of commutativity straight, which we haven't really been doing. There's syntactic commutativity, where x*y and y*x must call the same function. This type of syntactic commutativity does not exist at all in C++, anywhere. There's also semantic commutativity, where x*y and y*x give you the same value. That's true for * with arithmetic types, but isn't true for matrices; this is up to you the programmer as you define what the symbols really mean, whether it is true for any given operator. But it is not enforced by the language, or prohibited by the language, in any way (see point 1).)
    Last edited by tabstop; 05-29-2011 at 01:09 AM.

  7. #7
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    One can call the other:
    Code:
    Vect operator*(float scalar, const Vect &the_vect) {
      Vect scalar_product;
    
      scalar_product.x = scalar * the_vect.x;
      scalar_product.y = scalar * the_vect.y;
      scalar_product.z = scalar * the_vect.z;
    
      return scalar_product;
    }
    
    Vect operator*(const Vect &the_vect, float scalar) {
      return scalar * the_vect;
    }
    Which removes duplication. It may be as efficient thanks to compiler optimisation, but I'd perhaps profile to be sure.

    Once you're learned how to make your own, I recommend just downloading and using CML instead.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  8. #8
    Registered User
    Join Date
    Apr 2010
    Posts
    88
    Okay, I think this would be the crucial bit that answers my question. Also well worded, kudos.

    This type of syntactic commutativity does not exist at all in C++, anywhere. There's also semantic commutativity, where x*y and y*x give you the same value. That's true for * with arithmetic types, but isn't true for matrices; this is up to you the programmer as you define what the symbols really mean, whether it is true for any given operator. But it is not enforced by the language, or prohibited by the language, in any way
    Thanks, the last sentence there was what I was looking for. Didn't mean to be argumentative boys and gals; I was getting frustrated as I thought the crux of my question was being overlooked, but this is quite well put. Essentially I was wondering, given that we are dealing with an operation which is semantically commutative, is there a "one line" method to force syntactic commutativity. I was talking about semantic commutativity it seems, stuck a little too much in math world, I also see know what you mean about XOR.

    Thanks for replies guys.
    Last edited by Ocifer; 05-29-2011 at 12:23 PM.

  9. #9
    Registered User
    Join Date
    Apr 2010
    Posts
    88
    Also, what is CML?

  10. #10
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  11. #11
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Are you trying to make it a little bit easier to express vector math in C++, or are you trying to get cute by turning it into a mathematical language in and of itself? If it were me, I'd provide left multiplication by a scalar (scalar*vector) and not even bother with the other one. The programmer who wishes he could write it the other way will just have to "deal". This is C++, not Mathematica.

  12. #12
    Registered User
    Join Date
    Apr 2010
    Posts
    88
    I was trying to make it a bit easier to do vector operations, not re-write the language. The vectors operations are used within a larger context of denoting position and velocity of agents in an IBM model, and so I wanted to make it easier to see what was going on mathematically, instead of many nested functions with prototypes like: vector_add(float * vect1, float * vect2).or something.

    It was also a bit of an exercise/theory question as I'm new to C++ and overloading. I liked the idea a lot, and got curious to try reversing the order. The question was more out of curiosity than anything, as I already have a working implementation in C.

  13. #13
    Registered User
    Join Date
    Feb 2011
    Posts
    6
    I understand it can be annoying to write two functions which are practically identical.
    However, C++ evaluates expressions with operators following the C++ order of operations, and
    the rule for multiplication, just like standard math, requires an expression to be evaluated
    from left to right.
    Therefore, code written:
    Code:
    Vector v;
    Vector w;
    v = 2*w;
    would call the operator*(double, Vector) and NOT operator*(Vector, double).
    It may seem silly and redundant to define both functions but there may be cases
    where two distinct definitions are important. Therefore, both overloads must be
    defined.

    Short answer: There is no keyword or special method to making an operator commutative.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    What I suggest is providing a member operator*= that performs scalar multiplication, e.g.,
    Code:
    Vect& Vect::operator*=(float scalar) {
        x *= scalar;
        y *= scalar;
        z *= scalar;
        return *this;
    }
    Now implementing the overloads of operator* is trivial:
    Code:
    Vect operator*(Vect vect, float scalar) {
        return vect *= scalar;
    }
    
    Vect operator*(float scalar, Vect vect) {
        return vect *= scalar;
    }
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Now implementing the overloads of operator* is trivial
    So much so that you can "CRTP" the crap out of it for later use.

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Overloading operators
    By Niels_M in forum C++ Programming
    Replies: 40
    Last Post: 09-13-2010, 01:23 PM
  2. Replies: 6
    Last Post: 11-11-2009, 02:27 PM
  3. Replies: 25
    Last Post: 11-30-2008, 11:30 PM
  4. bitwise and arithmetic Operators
    By Whiteghost in forum C Programming
    Replies: 4
    Last Post: 12-28-2006, 02:13 PM
  5. Overloading operators...
    By Unregistered in forum C++ Programming
    Replies: 4
    Last Post: 11-21-2001, 08:24 PM