Thread: Library for working with error terms?

  1. #1
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    89

    Question Library for working with error terms?

    Take the number V = 1.2 +/- 0.3 for example. So V spans the interval [0.9, 1.5]. Then V + V -> 2.4 +/- 0.6 -> [1.8, 3] and V * V = 1.53 +/- 0.72 -> [0.81, 2.25], etc.

    I have a rough implementation working already (only supports +, -, *, and / at this point) but really I would rather not have to code this up by hand. Any suggestions for an existing library?

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    694
    The world hangs on a thin thread, and that is the psyche of man. - Carl Jung

  3. #3
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    89
    Quote Originally Posted by john.c View Post
    Thanks! I didn't see any single-header C++ libraries on the list but that does seem to be the search term I was looking for.

    Also regarding the rules of operator overloading. Should they all be moved outside the class or not?

    Code:
    template <typename T>
    class roughly
    {
     public:
    
     T average, extrema;
     
     roughly(const T& average = 0, const T& extrema = 0)
     { 
      set(average, extrema);   
     }
    
     roughly(const roughly& rhs)
     { 
      set(rhs.average, rhs.extrema);
     }
    
     roughly& set(const T& average, const T& extrema = 0)
     {
      roughly& that = *this;
      that.average = average;
      that.extrema = extrema < 0 ? -extrema : extrema;
      return that;
     }
     
     T low() const
     {
      return average - extrema;
     }
    
     T high() const
     {
      return average + extrema;
     }
    
     roughly& operator += (const roughly& rhs)
     {
      average += rhs.average;
      extrema += rhs.extrema; 
      return *this;
     }  
    
     roughly& operator -= (const roughly& rhs)
     {
      average -= rhs.average;
      extrema -= rhs.extrema; 
      return *this;
     } 
    
     roughly& meld(const T& lows, const T& highs)
     {
      average = (lows + highs) / 2;
      extrema = average - lows; 
      return *this;
     }  
    
     roughly& operator *= (const roughly& rhs)
     {
      return meld(low() * rhs.low(), high() * rhs.high());
     } 
    
     roughly& operator /= (const roughly& rhs)
     {
      auto rl = rhs.low();
      auto lows = rl ? low() / rl : 0;
      auto rh = rhs.high();
      auto highs = rh ? high() / rh : 0;
      return meld(lows, highs);
     } 
    };
    
    template <typename T>
    roughly<T>
    inline operator + 
    (
     const roughly<T>& lhs, 
     const roughly<T>& rhs
    )
    {
     return roughly<T>(lhs) += rhs;
    }
    
    template <typename T>
    roughly<T>
    inline operator - 
    (
     const roughly<T>& lhs, 
     const roughly<T>& rhs
    )
    {
     return roughly<T>(lhs) -= rhs;
    }
    
    template <typename T>
    roughly<T>
    inline operator * 
    (
     const roughly<T>& lhs, 
     const roughly<T>& rhs
    )
    {
     return roughly<T>(lhs) *= rhs;
    }
    
    template <typename T>
    roughly<T>
    inline operator / 
    (
     const roughly<T>& lhs, 
     const roughly<T>& rhs
    )
    {
     return roughly<T>(lhs) /= rhs;
    }
    
    template <typename S, typename T>
     S& 
    operator << 
    (
       S& stream, 
       const roughly<T>& data
    )
    {
     return stream << data.average 
     << " [+/-] " << data.extrema; 
    }
    Last edited by Sir Galahad; 4 Days Ago at 08:29 PM.

  4. #4
    Registered User
    Join Date
    Dec 2017
    Posts
    694
    Quote Originally Posted by Sir Galahad View Post
    Thanks! I didn't see any single-header C++ libraries on the list but that does seem to be the search term I was looking for.
    I used my googlefu and typed in "programming library measurement error".

    Quote Originally Posted by Sir Galahad View Post
    Also regarding the rules of operator overloading. Should they all be moved outside the class or not?
    That's definitely a laserlight question (she's a C++ expert). However, what you have looks pretty good to me, with the += (and others like that) in the class and the + and others outside.
    The world hangs on a thin thread, and that is the psyche of man. - Carl Jung

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,234
    Quote Originally Posted by Sir Galahad
    Also regarding the rules of operator overloading. Should they all be moved outside the class or not?
    It's the same rule as any other function: if it needs (or may be critical for efficiency for it to have) access to the internals of the class, then it should be a member or a friend. Otherwise, if it can be (efficiently) implemented without being a member or a friend, then it should be a non-member non-friend.

    Looking over your code, I might suggest (untested even for compile errors):
    Code:
    template <typename T>
    class roughly
    {
    public:
        roughly(const T& average = T(), const T& extrema = T()) :
            average(average), extrema(extrema < T() ? -extrema : extrema) {}
    
        T getAverage() const
        {
            return average;
        }
    
        T getExtrema() const
        {
            return extrema;
        }
    
        roughly& operator += (const roughly& rhs)
        {
            average += rhs.average;
            extrema += rhs.extrema;
            return *this;
        }
    
        roughly& operator -= (const roughly& rhs)
        {
            average -= rhs.average;
            extrema -= rhs.extrema; 
            return *this;
        }
    
        roughly& operator *= (const roughly& rhs)
        {
            return meld(low() * rhs.low(), high() * rhs.high());
        }
    
        roughly& operator /= (const roughly& rhs)
        {
            auto rl = rhs.low();
            auto lows = rl ? low() / rl : T();
            auto rh = rhs.high();
            auto highs = rh ? high() / rh : T();
            return meld(lows, highs);
        }
    private:
        T average, extrema;
    
        T low() const
        {
            return average - extrema;
        }
    
        T high() const
        {
            return average + extrema;
        }
    
        roughly& meld(const T& lows, const T& highs)
        {
            average = (lows + highs) / T(2);
            extrema = average - lows;
            return *this;
        }
    };
    
    template <typename T>
    inline
    roughly<T> operator + (const roughly<T>& lhs, const roughly<T>& rhs)
    {
        return roughly<T>(lhs) += rhs;
    }
    
    template <typename T>
    inline
    roughly<T> operator - (const roughly<T>& lhs, const roughly<T>& rhs)
    {
        return roughly<T>(lhs) -= rhs;
    }
    
    template <typename T>
    inline
    roughly<T> operator * (const roughly<T>& lhs, const roughly<T>& rhs)
    {
        return roughly<T>(lhs) *= rhs;
    }
    
    template <typename T>
    inline
    roughly<T> operator / (const roughly<T>& lhs, const roughly<T>& rhs)
    {
        return roughly<T>(lhs) /= rhs;
    }
    
    template <typename T>
    inline
    std::ostream& operator << (std::ostream& stream, const roughly<T>& data)
    {
        return stream << data.getAverage() << " [+/-] " << data.getExtrema();
    }
    My reasoning:
    • You want to control extrema to be non-negative, so it makes sense to make it private and control how it is set.
    • You might as well make use of the constructor initialiser list.
    • Using T() should give the zero value of type T even for say, a custom bignum class.
    • The implicit copy constructor will do the job efficiently, so there's no need to define it such that it does the extrema check that would have already been done.
    • It looks like low, high, and meld are helper functions, so I have moved them to have private access.
    • I have used T(2) instead of 2 similiarly to get the number 2 of type T. It might not be important, but then if there's say, a bignum class that doesn't support implicit conversions from int, this could turn out to be a boon.
    • I have neither a C++ compiler nor my copy of the C++ standard right now to check, but I believe that if you want to explicitly declare a function template as inline, then the inline keyword comes between the template parameter declarations and the return type.
    • It looks like the operator<< overload is intended for output streams, so rather than having a template parameter S, it makes more sense to explicitly overload for ostream. This could help avoid confusion with say, a future overload of operator<< to perform left shifting.
    Last edited by laserlight; 4 Days Ago at 09:59 PM.
    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

  6. #6
    Registered User
    Join Date
    Dec 2017
    Posts
    694
    @laserlight, I checked it and it compiles just fine.
    And moving the inline keyword to the original position causes an error.
    The world hangs on a thin thread, and that is the psyche of man. - Carl Jung

  7. #7
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    89
    Quote Originally Posted by laserlight View Post
    It's the same rule as any other function: if it needs (or may be critical for efficiency for it to have) access to the internals of the class, then it should be a member or a friend. Otherwise, if it can be (efficiently) implemented without being a member or a friend, then it should be a non-member non-friend.

    Looking over your code, I might suggest (untested even for compile errors):
    Code:
    ...
    My reasoning:
    • You want to control extrema to be non-negative, so it makes sense to make it private and control how it is set.
    • You might as well make use of the constructor initialiser list.
    • Using T() should give the zero value of type T even for say, a custom bignum class.
    • The implicit copy constructor will do the job efficiently, so there's no need to define it such that it does the extrema check that would have already been done.
    • It looks like low, high, and meld are helper functions, so I have moved them to have private access.
    • I have used T(2) instead of 2 similiarly to get the number 2 of type T. It might not be important, but then if there's say, a bignum class that doesn't support implicit conversions from int, this could turn out to be a boon.
    • I have neither a C++ compiler nor my copy of the C++ standard right now to check, but I believe that if you want to explicitly declare a function template as inline, then the inline keyword comes between the template parameter declarations and the return type.
    • It looks like the operator<< overload is intended for output streams, so rather than having a template parameter S, it makes more sense to explicitly overload for ostream. This could help avoid confusion with say, a future overload of operator<< to perform left shifting.
    I was just thinking that I'd heard that operators defined outside of the class were somehow "more flexible" or something?

    Thanks for the other bits of advice by the way. I think I might need a refresher course on this stuff!

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,234
    Quote Originally Posted by Sir Galahad
    I was just thinking that I'd heard that operators defined outside of the class were somehow "more flexible" or something?
    You're probably thinking of how an implicit conversion could work with say, operator+

    If you have operator+ as a member, then you could write x * 2 but not 2 * x. As a non-member, you could write both. But this is not so for operator+= as you want x += 2, but 2 += x wouldn't make the same sense.
    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

  9. #9
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    89
    Quote Originally Posted by laserlight View Post
    You're probably thinking of how an implicit conversion could work with say, operator+

    If you have operator+ as a member, then you could write x * 2 but not 2 * x. As a non-member, you could write both. But this is not so for operator+= as you want x += 2, but 2 += x wouldn't make the same sense.
    Yes, that's it! That's exactly what I was talking about. Okay, so is a friend function the same thing or is there some semantic difference? I'll look it up...

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,234
    A friend function is just a non-member function that has access to private members as if it were a member function.
    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

  11. #11
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    89
    Quote Originally Posted by laserlight View Post
    A friend function is just a non-member function that has access to private members as if it were a member function.
    Perfect. And defining them as friend functions means less verbose template syntax too, so I guess I'll do it that way.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,234
    No, member functions defined in a template class definition tends to be less verbose: you don't need to keep listing the template parameters.

    Why do you need friend functions here?
    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

  13. #13
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    89
    Quote Originally Posted by laserlight View Post
    No, member functions defined in a template class definition tends to be less verbose: you don't need to keep listing the template parameters.

    Why do you need friend functions here?
    I honestly don't know what I was thinking there. Yeah that would not work.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. math.h library not working with curses.h
    By John McPherson in forum C Programming
    Replies: 4
    Last Post: 06-15-2017, 12:39 PM
  2. strtod in standard library not working
    By Vespasian_2 in forum C Programming
    Replies: 11
    Last Post: 05-09-2017, 08:55 AM
  3. uthash library - string key not working
    By baxy in forum C Programming
    Replies: 2
    Last Post: 04-22-2013, 11:00 AM
  4. Replies: 11
    Last Post: 02-04-2012, 09:46 AM
  5. Simply Library Catalog Program not working
    By Bakster in forum C Programming
    Replies: 17
    Last Post: 09-04-2009, 08:06 PM

Tags for this Thread