How would I go about optimizing this code?











up vote
1
down vote

favorite












I am writing a function to find the average of an array in which the array is mostly numbers that would overflow if added all at once.



It works by creating a subarray(b in my code) that is half the input(a in my code) array's size(ar_size in my code) and then places the average of 2 values from the input array a[i+0] and a[i+1] with no overlap into b[j].



Once it iterates through the entire input array, it reruns the function with returning the subarray and the input array size until the size equals 2 and then ends the recursion by returning the average of the two values of b[2].



Please pardon the reuse of j.



Also the size of the array is some power of two.



uint64_t* array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size / 2];

uint64_t* j = new uint64_t;

if (ar_size == 2)
{
*j = (a[0] / 2) + (a[1] / 2) + ((a[0] % 2 + a[1] % 2) / 2);

return j;
}

for (int i = 0; i < ar_size; i += 2)
{
b[*j] = (a[i + 0] / 2) + (a[i + 1] / 2) + ((a[i + 0] % 2 + a[i + 1] % 2) / 2);

++*j;
}
delete j;
return array_average(b, ar_size / 2);
}


Also anyone have a better way to average while working with numbers that would cause an overflow to happen?



Here is a revised version:



uint64_t* tools::array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size];
uint64_t* c = new uint64_t[ar_size / 2];

int j;
j = 0;

for (int i = 0; i < ar_size; ++i)
{
b[i] = a[i];
}

if (runs > 0) //This is so i do not delete the original input array I.E not done with it
{
delete a;
}

if (ar_size == 2)
{
uint64_t* y = new uint64_t;

runs = 0;

*y = (b[0] / 2) + (b[1] / 2) + ((b[0] % 2 + b[1] % 2) / 2);

delete b;

return y;
}

for (int i = 0; i < ar_size; i += 2)
{
c[j] = (b[i + 0] / 2) + (b[i + 1] / 2) + ((b[i + 0] % 2 + b[i + 1] % 2) / 2);

++j;
}

delete b;

++runs;

return array_average(c, ar_size / 2);









share|improve this question




















  • 6




    I wouldn't. I'd fix the memory leak it has before trying to rewrite it in a different form, and eliminate the potential undefined behaviour for the caller that comes by returning the address of an automatic variable. In any event, before asking someone else to convert it, have a go yourself. You'll learn more of use that way. And folks here can help if you get in trouble with a more specific concern.
    – Peter
    Nov 7 at 10:48












  • Ftr the leak is from the b array right? @Peter
    – user2430974
    Nov 7 at 11:07












  • I have it working perfectly fine, and ++j is used the increment the b array's position. @molbdnilo
    – user2430974
    Nov 7 at 11:09










  • You've got a tail recursive algorithm. The normal way to covert that into a loop is to recompute the "parameters" each time round the loop. You can start with while(ar_size > 2) around your existing code.
    – Caleth
    Nov 7 at 11:11






  • 1




    You did not understand molbdnilo. You have a line return &j; which returns a pointer to the variable j. That variable lives in the scope of the function and is removed as soon as the function ends, that is with the return. Thus, you return a pointer to a now invalid address. This is undefined behaviour. As per rule with undefined behaviour, it can do anything, including the thing you want the code to do, which is why your tests might still work.
    – Aziuth
    Nov 7 at 11:16















up vote
1
down vote

favorite












I am writing a function to find the average of an array in which the array is mostly numbers that would overflow if added all at once.



It works by creating a subarray(b in my code) that is half the input(a in my code) array's size(ar_size in my code) and then places the average of 2 values from the input array a[i+0] and a[i+1] with no overlap into b[j].



Once it iterates through the entire input array, it reruns the function with returning the subarray and the input array size until the size equals 2 and then ends the recursion by returning the average of the two values of b[2].



Please pardon the reuse of j.



Also the size of the array is some power of two.



uint64_t* array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size / 2];

uint64_t* j = new uint64_t;

if (ar_size == 2)
{
*j = (a[0] / 2) + (a[1] / 2) + ((a[0] % 2 + a[1] % 2) / 2);

return j;
}

for (int i = 0; i < ar_size; i += 2)
{
b[*j] = (a[i + 0] / 2) + (a[i + 1] / 2) + ((a[i + 0] % 2 + a[i + 1] % 2) / 2);

++*j;
}
delete j;
return array_average(b, ar_size / 2);
}


Also anyone have a better way to average while working with numbers that would cause an overflow to happen?



Here is a revised version:



uint64_t* tools::array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size];
uint64_t* c = new uint64_t[ar_size / 2];

int j;
j = 0;

for (int i = 0; i < ar_size; ++i)
{
b[i] = a[i];
}

if (runs > 0) //This is so i do not delete the original input array I.E not done with it
{
delete a;
}

if (ar_size == 2)
{
uint64_t* y = new uint64_t;

runs = 0;

*y = (b[0] / 2) + (b[1] / 2) + ((b[0] % 2 + b[1] % 2) / 2);

delete b;

return y;
}

for (int i = 0; i < ar_size; i += 2)
{
c[j] = (b[i + 0] / 2) + (b[i + 1] / 2) + ((b[i + 0] % 2 + b[i + 1] % 2) / 2);

++j;
}

delete b;

++runs;

return array_average(c, ar_size / 2);









share|improve this question




















  • 6




    I wouldn't. I'd fix the memory leak it has before trying to rewrite it in a different form, and eliminate the potential undefined behaviour for the caller that comes by returning the address of an automatic variable. In any event, before asking someone else to convert it, have a go yourself. You'll learn more of use that way. And folks here can help if you get in trouble with a more specific concern.
    – Peter
    Nov 7 at 10:48












  • Ftr the leak is from the b array right? @Peter
    – user2430974
    Nov 7 at 11:07












  • I have it working perfectly fine, and ++j is used the increment the b array's position. @molbdnilo
    – user2430974
    Nov 7 at 11:09










  • You've got a tail recursive algorithm. The normal way to covert that into a loop is to recompute the "parameters" each time round the loop. You can start with while(ar_size > 2) around your existing code.
    – Caleth
    Nov 7 at 11:11






  • 1




    You did not understand molbdnilo. You have a line return &j; which returns a pointer to the variable j. That variable lives in the scope of the function and is removed as soon as the function ends, that is with the return. Thus, you return a pointer to a now invalid address. This is undefined behaviour. As per rule with undefined behaviour, it can do anything, including the thing you want the code to do, which is why your tests might still work.
    – Aziuth
    Nov 7 at 11:16













up vote
1
down vote

favorite









up vote
1
down vote

favorite











I am writing a function to find the average of an array in which the array is mostly numbers that would overflow if added all at once.



It works by creating a subarray(b in my code) that is half the input(a in my code) array's size(ar_size in my code) and then places the average of 2 values from the input array a[i+0] and a[i+1] with no overlap into b[j].



Once it iterates through the entire input array, it reruns the function with returning the subarray and the input array size until the size equals 2 and then ends the recursion by returning the average of the two values of b[2].



Please pardon the reuse of j.



Also the size of the array is some power of two.



uint64_t* array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size / 2];

uint64_t* j = new uint64_t;

if (ar_size == 2)
{
*j = (a[0] / 2) + (a[1] / 2) + ((a[0] % 2 + a[1] % 2) / 2);

return j;
}

for (int i = 0; i < ar_size; i += 2)
{
b[*j] = (a[i + 0] / 2) + (a[i + 1] / 2) + ((a[i + 0] % 2 + a[i + 1] % 2) / 2);

++*j;
}
delete j;
return array_average(b, ar_size / 2);
}


Also anyone have a better way to average while working with numbers that would cause an overflow to happen?



Here is a revised version:



uint64_t* tools::array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size];
uint64_t* c = new uint64_t[ar_size / 2];

int j;
j = 0;

for (int i = 0; i < ar_size; ++i)
{
b[i] = a[i];
}

if (runs > 0) //This is so i do not delete the original input array I.E not done with it
{
delete a;
}

if (ar_size == 2)
{
uint64_t* y = new uint64_t;

runs = 0;

*y = (b[0] / 2) + (b[1] / 2) + ((b[0] % 2 + b[1] % 2) / 2);

delete b;

return y;
}

for (int i = 0; i < ar_size; i += 2)
{
c[j] = (b[i + 0] / 2) + (b[i + 1] / 2) + ((b[i + 0] % 2 + b[i + 1] % 2) / 2);

++j;
}

delete b;

++runs;

return array_average(c, ar_size / 2);









share|improve this question















I am writing a function to find the average of an array in which the array is mostly numbers that would overflow if added all at once.



It works by creating a subarray(b in my code) that is half the input(a in my code) array's size(ar_size in my code) and then places the average of 2 values from the input array a[i+0] and a[i+1] with no overlap into b[j].



Once it iterates through the entire input array, it reruns the function with returning the subarray and the input array size until the size equals 2 and then ends the recursion by returning the average of the two values of b[2].



Please pardon the reuse of j.



Also the size of the array is some power of two.



uint64_t* array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size / 2];

uint64_t* j = new uint64_t;

if (ar_size == 2)
{
*j = (a[0] / 2) + (a[1] / 2) + ((a[0] % 2 + a[1] % 2) / 2);

return j;
}

for (int i = 0; i < ar_size; i += 2)
{
b[*j] = (a[i + 0] / 2) + (a[i + 1] / 2) + ((a[i + 0] % 2 + a[i + 1] % 2) / 2);

++*j;
}
delete j;
return array_average(b, ar_size / 2);
}


Also anyone have a better way to average while working with numbers that would cause an overflow to happen?



Here is a revised version:



uint64_t* tools::array_average(uint64_t* a, const int ar_size)
{
uint64_t* b = new uint64_t[ar_size];
uint64_t* c = new uint64_t[ar_size / 2];

int j;
j = 0;

for (int i = 0; i < ar_size; ++i)
{
b[i] = a[i];
}

if (runs > 0) //This is so i do not delete the original input array I.E not done with it
{
delete a;
}

if (ar_size == 2)
{
uint64_t* y = new uint64_t;

runs = 0;

*y = (b[0] / 2) + (b[1] / 2) + ((b[0] % 2 + b[1] % 2) / 2);

delete b;

return y;
}

for (int i = 0; i < ar_size; i += 2)
{
c[j] = (b[i + 0] / 2) + (b[i + 1] / 2) + ((b[i + 0] % 2 + b[i + 1] % 2) / 2);

++j;
}

delete b;

++runs;

return array_average(c, ar_size / 2);






c++ recursion c++14






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 8 at 12:07

























asked Nov 7 at 10:40







user2430974















  • 6




    I wouldn't. I'd fix the memory leak it has before trying to rewrite it in a different form, and eliminate the potential undefined behaviour for the caller that comes by returning the address of an automatic variable. In any event, before asking someone else to convert it, have a go yourself. You'll learn more of use that way. And folks here can help if you get in trouble with a more specific concern.
    – Peter
    Nov 7 at 10:48












  • Ftr the leak is from the b array right? @Peter
    – user2430974
    Nov 7 at 11:07












  • I have it working perfectly fine, and ++j is used the increment the b array's position. @molbdnilo
    – user2430974
    Nov 7 at 11:09










  • You've got a tail recursive algorithm. The normal way to covert that into a loop is to recompute the "parameters" each time round the loop. You can start with while(ar_size > 2) around your existing code.
    – Caleth
    Nov 7 at 11:11






  • 1




    You did not understand molbdnilo. You have a line return &j; which returns a pointer to the variable j. That variable lives in the scope of the function and is removed as soon as the function ends, that is with the return. Thus, you return a pointer to a now invalid address. This is undefined behaviour. As per rule with undefined behaviour, it can do anything, including the thing you want the code to do, which is why your tests might still work.
    – Aziuth
    Nov 7 at 11:16














  • 6




    I wouldn't. I'd fix the memory leak it has before trying to rewrite it in a different form, and eliminate the potential undefined behaviour for the caller that comes by returning the address of an automatic variable. In any event, before asking someone else to convert it, have a go yourself. You'll learn more of use that way. And folks here can help if you get in trouble with a more specific concern.
    – Peter
    Nov 7 at 10:48












  • Ftr the leak is from the b array right? @Peter
    – user2430974
    Nov 7 at 11:07












  • I have it working perfectly fine, and ++j is used the increment the b array's position. @molbdnilo
    – user2430974
    Nov 7 at 11:09










  • You've got a tail recursive algorithm. The normal way to covert that into a loop is to recompute the "parameters" each time round the loop. You can start with while(ar_size > 2) around your existing code.
    – Caleth
    Nov 7 at 11:11






  • 1




    You did not understand molbdnilo. You have a line return &j; which returns a pointer to the variable j. That variable lives in the scope of the function and is removed as soon as the function ends, that is with the return. Thus, you return a pointer to a now invalid address. This is undefined behaviour. As per rule with undefined behaviour, it can do anything, including the thing you want the code to do, which is why your tests might still work.
    – Aziuth
    Nov 7 at 11:16








6




6




I wouldn't. I'd fix the memory leak it has before trying to rewrite it in a different form, and eliminate the potential undefined behaviour for the caller that comes by returning the address of an automatic variable. In any event, before asking someone else to convert it, have a go yourself. You'll learn more of use that way. And folks here can help if you get in trouble with a more specific concern.
– Peter
Nov 7 at 10:48






I wouldn't. I'd fix the memory leak it has before trying to rewrite it in a different form, and eliminate the potential undefined behaviour for the caller that comes by returning the address of an automatic variable. In any event, before asking someone else to convert it, have a go yourself. You'll learn more of use that way. And folks here can help if you get in trouble with a more specific concern.
– Peter
Nov 7 at 10:48














Ftr the leak is from the b array right? @Peter
– user2430974
Nov 7 at 11:07






Ftr the leak is from the b array right? @Peter
– user2430974
Nov 7 at 11:07














I have it working perfectly fine, and ++j is used the increment the b array's position. @molbdnilo
– user2430974
Nov 7 at 11:09




I have it working perfectly fine, and ++j is used the increment the b array's position. @molbdnilo
– user2430974
Nov 7 at 11:09












You've got a tail recursive algorithm. The normal way to covert that into a loop is to recompute the "parameters" each time round the loop. You can start with while(ar_size > 2) around your existing code.
– Caleth
Nov 7 at 11:11




You've got a tail recursive algorithm. The normal way to covert that into a loop is to recompute the "parameters" each time round the loop. You can start with while(ar_size > 2) around your existing code.
– Caleth
Nov 7 at 11:11




1




1




You did not understand molbdnilo. You have a line return &j; which returns a pointer to the variable j. That variable lives in the scope of the function and is removed as soon as the function ends, that is with the return. Thus, you return a pointer to a now invalid address. This is undefined behaviour. As per rule with undefined behaviour, it can do anything, including the thing you want the code to do, which is why your tests might still work.
– Aziuth
Nov 7 at 11:16




You did not understand molbdnilo. You have a line return &j; which returns a pointer to the variable j. That variable lives in the scope of the function and is removed as soon as the function ends, that is with the return. Thus, you return a pointer to a now invalid address. This is undefined behaviour. As per rule with undefined behaviour, it can do anything, including the thing you want the code to do, which is why your tests might still work.
– Aziuth
Nov 7 at 11:16












3 Answers
3






active

oldest

votes

















up vote
3
down vote













First of all, be aware that your average is not the actual average, as you do throw away one halfs. The result of your algorithm on an array that alternates between 0 and 1 would be 0, as 0/2 + 1/2 + (0%2 + 1%2)/2 = 0. Wanted to start with that, because that is a serious weakness of your algorithm.



Also note that if the original size is not a power of 2, some data will get a higher weight.



Aside from that, consider this algorithm: Copy the data. Until the data has only one entry left, put the average of cells 0 and 1 in cell 0, that of 2 and 3 in cell 1, 4 and 5 in 2 and so on. Shrink the data after each such step.



As code:



uint64_t average(std::vector<uint64_t> data)
{
while(data.size() != 1)
{
for(size_t i=0; i<data.size()/2; i++)
{
data[i] = data[2*i]/2 + data[2*i+1]/2 + /* modular stuff */;
}
data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
}
return data[0];
}


Using a proper container here also gets rid of your memory leak, by the way.



Note that this code still has the weakness I talked about. You could extent it by collecting the halves, that is if your modular part is 1, you increase a variable, and when the variable is at two, you add a one in some cell.



Edit: If the input HAS to be a raw array (because you receive it from some external source, for example), use this:



uint64_t average(uint64_t* array, const int array_size)
{
std::vector<uint64_t> data(array, array + array_size);

(rest of the code is identical)


Edit: code above with collecting halves:



inline uint64_t average(const uint64_t& a, const uint64_t& b, uint8_t& left_halves)
{
uint64_t value = a/2 + b/2 + (a%2 + b%2)/2;
if((a%2 + b%2)%2 == 1)
{
left_halves += 1;
}
if(left_halves == 2)
{
value += 1;
left_halves = 0;
}
return value;
}

uint64_t average(std::vector<uint64_t> data)
{
if(data.size() == 0) return 0;

uint8_t left_halves = 0;
while(data.size() != 1)
{
for(size_t i=0; i<data.size()/2; i++)
{
data[i] = average(data[2*i], data[2*i+1], left_halves);
}
data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
}
return data[0];
}


Still has the weakness of increased cell weight if size is not a power of two.






share|improve this answer























  • What if I wanted to not use vectors in this specific situation?
    – user2430974
    Nov 7 at 11:15










  • Then you replace it by a raw array and store it's size in some integer. Note that you then need to delete it to avoid a memory leak. However, std::vector does only use up marginally more memory than a raw array. In almost any situation, you'd want to use a container. If you get the raw array from some part that you do not own, then create a vector out of the input where my code copies the data (data = input_data).
    – Aziuth
    Nov 7 at 11:18












  • Edited my answer to address that.
    – Aziuth
    Nov 7 at 11:23








  • 1




    and "marginally more" here means the difference between 3 pointers and 1 pointer and an int. Also you delete things that you newed, deleteing an array is UB.
    – Caleth
    Nov 7 at 11:25










  • I'd take input_data by value, rather than copy in the body.
    – Caleth
    Nov 7 at 11:28


















up vote
1
down vote













You might use:



constexpr bool is_power_of_2(uint64_t n)
{
return n && !(n & (n - 1));
}

uint64_t array_average(std::vector<uint64_t> v)
{
if (!is_power_of_2(v.size())) {
throw std::runtime_error("invalid size");
}
uint64_t remainder = 0;
while (v.size() != 1) {
for (int i = 0; i != v.size(); i += 2) {
remainder += (a[i] % 2 + a[i + 1] % 2);
b[i / 2] = a[i] / 2 + a[i + 1] / 2;
if (remainder >= 2 && b[i / 2] < -(remainder / 2)) {
b[i / 2] += remainder / 2;
remainder %= 2;
}
}
v.resize(v.size() / 2);
}
return v[0] + remainder / 2;
}





share|improve this answer






























    up vote
    0
    down vote













    There really shouldn't be that much to convert as there are containers, functions and algorithms in the stl that already exist that will do this for you. With out any function examine this short program:



    #include <vector>
    #include <numeric>
    #include <iostream>
    #include <exception>

    int main() {
    try {

    std::vector<uint64_t> values{ 1,2,3,4,5,6,7,8,9,10,11,12 };
    int total = std::accumulate( values.begin(), values.end(), 0 );
    uint64_t average = static_cast<uint64_t>( total ) / values.size();
    std::cout << average << 'n';

    } catch( const std::runtime_error& e ) {
    std::cerr << e.what() << 'n';
    return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
    }


    On my machine windows 7 ultimate 64bit running visual studio 2017 CE compiled with language version set to most recent c++17 or greater. This does give me a compiler warning! Warning: C4244 generated due to conversion and possible loss of data. However there are no compiler errors and it does run and give the expected result. The output here is 6 as expected since integer division is truncated. If I change these lines of code above to this:



    double total = std::accumulate( values.begin(), values.end(),
    static_cast<double>( 0 ) );
    double average = total / values.size();


    It fixes the compiler warnings above by adding the static_cast and it sure enough prints out 6.5 which is the actual value.



    This is all fine and good since the vector is already initialized with values; however, this may not be always the case so let's move this into a function that will take an arbitrary array. It would look something like this:



    uint64_t array_average( std::vector<uint64_t>& values ) {
    // Prevent Division by 0 and early return
    // as to not call `std::accumulate`
    if ( !values.empty() ) {
    // check if only 1 entry if so just return it
    if ( values.size() == 1 ) {
    return values[0];
    } else { // otherwise do the calculation.
    return std::accumulate( values.begin(), values.end(),
    static_cast<uint64_t>( 0 ) ) / values.size();
    }
    }
    // Empty Container
    throw std::runtime_error( "Can not take average of an empty container" );
    }


    This function is nice and all, we can do better by improving this by making it a little more generic that will work with any arithmetic type!



    template<typename T>
    T array_average( std::vector<T>& values ) {
    if( std::is_arithmetic<T>::value ) {
    if( !values.empty() ) {
    if( values.size() == 1 ) {
    return values[0];
    } else {
    return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
    }
    } else {
    throw std::runtime_error( "Can not take average of an empty container" );
    }
    } else {
    throw std::runtime_error( "T is not of an arithmetic type" );
    }
    }


    At first glance this looks okay. This will compile and run if you use this with types that are arithmetic. However, if we use it with a type that isn't this will fail to compile. For example:



    #include <vector>
    #include <numeric>
    #include <iostream>
    #include <exception>
    #include <type_traits>

    class Fruit {
    protected:
    std::string name_;
    public:
    std::string operator()() const {
    return name_;
    }
    std::string name() const { return name_; }

    Fruit operator+( const Fruit& other ) {
    this->name_ += " " + other.name();
    return *this;
    }
    };

    class Apple : public Fruit {
    public:
    Apple() { this->name_ = "Apple"; }

    };

    class Banana : public Fruit {
    public:
    Banana() { this->name_ = "Banana"; }
    };

    class Pear : public Fruit {
    public:
    Pear() { this->name_ = "Pear"; }
    };

    std::ostream& operator<<( std::ostream& os, const Fruit& fruit ) {
    os << fruit.name() << " ";
    return os;
    }

    template<typename T>
    T array_average( std::vector<T>& values ); // Using the definition above

    int main() {
    try {
    std::vector<uint64_t> values { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
    std::vector<double> values2 { 2.0, 3.5, 4.5, 6.7, 8.9 };
    std::vector<Fruit> fruits { Apple(), Banana(), Pear() };

    std::cout << array_average( values ) << 'n'; // compiles runs and prints 6
    std::cout << array_average( values2 ) << 'n'; // compiles runs and prints 5.12
    std::cout << array_average( fruits ) << 'n'; // fails to compile.

    } catch( const std::runtime_error& e ) {
    std::cerr << e.what() << 'n';
    return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
    }


    This fails to compile because the static_cast can not convert int to T with T = Fruit MSVC compiler error C2440



    We can fix this by changing a single line of code in our function template if your compiler supports it:



    We can change if( std::is_arithmetic<T>::value ) to if constexpr( std::is_arithmetic<T>::value ) and our function will now look like this:



    template<typename T>
    T array_average( const std::vector<T>& values ) {
    if constexpr( std::is_arithmetic<T>::value ) {
    if( !values.empty() ) {
    if( values.size() == 1 ) {
    return values[0];
    } else {
    return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
    }
    } else {
    throw std::runtime_error( "Can not take average of an empty container" );
    }
    } else {
    throw std::runtime_error( "T is not of an arithmetic type" );
    }
    }


    You can run the same program above and it will fully compile even when you are using types that are not arithmetic.



    int main() {
    //....
    std::cout << array_average( fruits ) << 'n'; // Now compiles
    //...
    }


    However when you run this code it will generate a Runtime Error and depending on how your IDE and debugger is setup you may need to put a break point within the catch statement where the return EXIT_FAILURE is to see the message printed to the screen, otherwise the application may just exit without any notification at all.



    If you don't want runtime errors you can substitute and produce compiler time errors by using static_assert instead of throwing a runtime error. This can be a handy little function, but it isn't 100% without some minor limitations and gotchas, but to find out more information about this function you can check the Question that I had asked when I was writing the implementation to this function that can be found here and you can read the comments there that will give you more insight to some of the limitations that this function provides.



    One of the current limitations with this function would be this: let's say we have a container that has a bunch of complex numbers (3i + 2), (4i - 6), (7i + 3) well you can still take the average of these as it is a valid thing, but the above function will not consider this to be arithmetic in it's current state.



    To resolve this issue what can be done is this: instead of using std::is_arithmetic<t> you could write your own policy and traits that this function should accept. I'll leave that part as an exercise for you.



    As you can see a majority of the work is already being done for us with the standard library. We used accumulate and divided by the containers size and we were done, the rest of the time involved was making sure it accepts proper types, if it's to be thread safe and or exception safe etc.



    Finally we did not have to worry about cumbersome for loops on arrays and making sure the loops didn't exceed the size of the array. We did not have to call new and worry about when and where to call delete in order to not have any memory leaks. ASFAIK I do not think that std::accumulate will overflow on supporting containers, but don't quote me on this one. It may depend on the types that are in the container and that there is a static_cast involved. Even with some of these caveats in many cases it is still better to use containers than managing your own raw memory, and to use the algorithms and functions that are designed to work on them. They make things a lot simpler and easier to manage and even debug.






    share|improve this answer























    • I think you missed the point of his problem. Your first block will surely not work if you have an array that stores the maximum of uint64_t one thousand times, while the idea of his algorithm would. Your second block which uses a double will obviously cause a loss of information in such a case, for the fact that a double can't hold more information than a uint64_t. I strongly assume that the guy works with very large numbers that come close to the maximum of uint64_t, and/or with very large arrays, and I also assume that the result is desired to be as precise as possible.
      – Aziuth
      Nov 7 at 22:45












    • You would be correct in assuming @Aziuth. With what I'm working with the arrays are ~1GB (134217728 items at 64bits) in size and even the smallest value would cause an overflow quite quickly.
      – user2430974
      Nov 8 at 0:19












    • It appears that the question was reworded since the time of me writing my answer and now that I have read it again with the current updates it is a little more clear of what is being asked. At first it appeared that the OP wanted to convert the algorithm or function into something simpler. Now it makes sense that you are expecting overflow and to that my answer is now irrelevant and what the OP is looking for is something on the lines of a huge integer library function or implementation with efficiency at its core design.
      – Francis Cugler
      Nov 8 at 1:19













    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53187827%2fhow-would-i-go-about-optimizing-this-code%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown
























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    3
    down vote













    First of all, be aware that your average is not the actual average, as you do throw away one halfs. The result of your algorithm on an array that alternates between 0 and 1 would be 0, as 0/2 + 1/2 + (0%2 + 1%2)/2 = 0. Wanted to start with that, because that is a serious weakness of your algorithm.



    Also note that if the original size is not a power of 2, some data will get a higher weight.



    Aside from that, consider this algorithm: Copy the data. Until the data has only one entry left, put the average of cells 0 and 1 in cell 0, that of 2 and 3 in cell 1, 4 and 5 in 2 and so on. Shrink the data after each such step.



    As code:



    uint64_t average(std::vector<uint64_t> data)
    {
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = data[2*i]/2 + data[2*i+1]/2 + /* modular stuff */;
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Using a proper container here also gets rid of your memory leak, by the way.



    Note that this code still has the weakness I talked about. You could extent it by collecting the halves, that is if your modular part is 1, you increase a variable, and when the variable is at two, you add a one in some cell.



    Edit: If the input HAS to be a raw array (because you receive it from some external source, for example), use this:



    uint64_t average(uint64_t* array, const int array_size)
    {
    std::vector<uint64_t> data(array, array + array_size);

    (rest of the code is identical)


    Edit: code above with collecting halves:



    inline uint64_t average(const uint64_t& a, const uint64_t& b, uint8_t& left_halves)
    {
    uint64_t value = a/2 + b/2 + (a%2 + b%2)/2;
    if((a%2 + b%2)%2 == 1)
    {
    left_halves += 1;
    }
    if(left_halves == 2)
    {
    value += 1;
    left_halves = 0;
    }
    return value;
    }

    uint64_t average(std::vector<uint64_t> data)
    {
    if(data.size() == 0) return 0;

    uint8_t left_halves = 0;
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = average(data[2*i], data[2*i+1], left_halves);
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Still has the weakness of increased cell weight if size is not a power of two.






    share|improve this answer























    • What if I wanted to not use vectors in this specific situation?
      – user2430974
      Nov 7 at 11:15










    • Then you replace it by a raw array and store it's size in some integer. Note that you then need to delete it to avoid a memory leak. However, std::vector does only use up marginally more memory than a raw array. In almost any situation, you'd want to use a container. If you get the raw array from some part that you do not own, then create a vector out of the input where my code copies the data (data = input_data).
      – Aziuth
      Nov 7 at 11:18












    • Edited my answer to address that.
      – Aziuth
      Nov 7 at 11:23








    • 1




      and "marginally more" here means the difference between 3 pointers and 1 pointer and an int. Also you delete things that you newed, deleteing an array is UB.
      – Caleth
      Nov 7 at 11:25










    • I'd take input_data by value, rather than copy in the body.
      – Caleth
      Nov 7 at 11:28















    up vote
    3
    down vote













    First of all, be aware that your average is not the actual average, as you do throw away one halfs. The result of your algorithm on an array that alternates between 0 and 1 would be 0, as 0/2 + 1/2 + (0%2 + 1%2)/2 = 0. Wanted to start with that, because that is a serious weakness of your algorithm.



    Also note that if the original size is not a power of 2, some data will get a higher weight.



    Aside from that, consider this algorithm: Copy the data. Until the data has only one entry left, put the average of cells 0 and 1 in cell 0, that of 2 and 3 in cell 1, 4 and 5 in 2 and so on. Shrink the data after each such step.



    As code:



    uint64_t average(std::vector<uint64_t> data)
    {
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = data[2*i]/2 + data[2*i+1]/2 + /* modular stuff */;
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Using a proper container here also gets rid of your memory leak, by the way.



    Note that this code still has the weakness I talked about. You could extent it by collecting the halves, that is if your modular part is 1, you increase a variable, and when the variable is at two, you add a one in some cell.



    Edit: If the input HAS to be a raw array (because you receive it from some external source, for example), use this:



    uint64_t average(uint64_t* array, const int array_size)
    {
    std::vector<uint64_t> data(array, array + array_size);

    (rest of the code is identical)


    Edit: code above with collecting halves:



    inline uint64_t average(const uint64_t& a, const uint64_t& b, uint8_t& left_halves)
    {
    uint64_t value = a/2 + b/2 + (a%2 + b%2)/2;
    if((a%2 + b%2)%2 == 1)
    {
    left_halves += 1;
    }
    if(left_halves == 2)
    {
    value += 1;
    left_halves = 0;
    }
    return value;
    }

    uint64_t average(std::vector<uint64_t> data)
    {
    if(data.size() == 0) return 0;

    uint8_t left_halves = 0;
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = average(data[2*i], data[2*i+1], left_halves);
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Still has the weakness of increased cell weight if size is not a power of two.






    share|improve this answer























    • What if I wanted to not use vectors in this specific situation?
      – user2430974
      Nov 7 at 11:15










    • Then you replace it by a raw array and store it's size in some integer. Note that you then need to delete it to avoid a memory leak. However, std::vector does only use up marginally more memory than a raw array. In almost any situation, you'd want to use a container. If you get the raw array from some part that you do not own, then create a vector out of the input where my code copies the data (data = input_data).
      – Aziuth
      Nov 7 at 11:18












    • Edited my answer to address that.
      – Aziuth
      Nov 7 at 11:23








    • 1




      and "marginally more" here means the difference between 3 pointers and 1 pointer and an int. Also you delete things that you newed, deleteing an array is UB.
      – Caleth
      Nov 7 at 11:25










    • I'd take input_data by value, rather than copy in the body.
      – Caleth
      Nov 7 at 11:28













    up vote
    3
    down vote










    up vote
    3
    down vote









    First of all, be aware that your average is not the actual average, as you do throw away one halfs. The result of your algorithm on an array that alternates between 0 and 1 would be 0, as 0/2 + 1/2 + (0%2 + 1%2)/2 = 0. Wanted to start with that, because that is a serious weakness of your algorithm.



    Also note that if the original size is not a power of 2, some data will get a higher weight.



    Aside from that, consider this algorithm: Copy the data. Until the data has only one entry left, put the average of cells 0 and 1 in cell 0, that of 2 and 3 in cell 1, 4 and 5 in 2 and so on. Shrink the data after each such step.



    As code:



    uint64_t average(std::vector<uint64_t> data)
    {
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = data[2*i]/2 + data[2*i+1]/2 + /* modular stuff */;
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Using a proper container here also gets rid of your memory leak, by the way.



    Note that this code still has the weakness I talked about. You could extent it by collecting the halves, that is if your modular part is 1, you increase a variable, and when the variable is at two, you add a one in some cell.



    Edit: If the input HAS to be a raw array (because you receive it from some external source, for example), use this:



    uint64_t average(uint64_t* array, const int array_size)
    {
    std::vector<uint64_t> data(array, array + array_size);

    (rest of the code is identical)


    Edit: code above with collecting halves:



    inline uint64_t average(const uint64_t& a, const uint64_t& b, uint8_t& left_halves)
    {
    uint64_t value = a/2 + b/2 + (a%2 + b%2)/2;
    if((a%2 + b%2)%2 == 1)
    {
    left_halves += 1;
    }
    if(left_halves == 2)
    {
    value += 1;
    left_halves = 0;
    }
    return value;
    }

    uint64_t average(std::vector<uint64_t> data)
    {
    if(data.size() == 0) return 0;

    uint8_t left_halves = 0;
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = average(data[2*i], data[2*i+1], left_halves);
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Still has the weakness of increased cell weight if size is not a power of two.






    share|improve this answer














    First of all, be aware that your average is not the actual average, as you do throw away one halfs. The result of your algorithm on an array that alternates between 0 and 1 would be 0, as 0/2 + 1/2 + (0%2 + 1%2)/2 = 0. Wanted to start with that, because that is a serious weakness of your algorithm.



    Also note that if the original size is not a power of 2, some data will get a higher weight.



    Aside from that, consider this algorithm: Copy the data. Until the data has only one entry left, put the average of cells 0 and 1 in cell 0, that of 2 and 3 in cell 1, 4 and 5 in 2 and so on. Shrink the data after each such step.



    As code:



    uint64_t average(std::vector<uint64_t> data)
    {
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = data[2*i]/2 + data[2*i+1]/2 + /* modular stuff */;
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Using a proper container here also gets rid of your memory leak, by the way.



    Note that this code still has the weakness I talked about. You could extent it by collecting the halves, that is if your modular part is 1, you increase a variable, and when the variable is at two, you add a one in some cell.



    Edit: If the input HAS to be a raw array (because you receive it from some external source, for example), use this:



    uint64_t average(uint64_t* array, const int array_size)
    {
    std::vector<uint64_t> data(array, array + array_size);

    (rest of the code is identical)


    Edit: code above with collecting halves:



    inline uint64_t average(const uint64_t& a, const uint64_t& b, uint8_t& left_halves)
    {
    uint64_t value = a/2 + b/2 + (a%2 + b%2)/2;
    if((a%2 + b%2)%2 == 1)
    {
    left_halves += 1;
    }
    if(left_halves == 2)
    {
    value += 1;
    left_halves = 0;
    }
    return value;
    }

    uint64_t average(std::vector<uint64_t> data)
    {
    if(data.size() == 0) return 0;

    uint8_t left_halves = 0;
    while(data.size() != 1)
    {
    for(size_t i=0; i<data.size()/2; i++)
    {
    data[i] = average(data[2*i], data[2*i+1], left_halves);
    }
    data.resize(data.size()/2 + data.size()%2); //last part is required if the size is not an even number
    }
    return data[0];
    }


    Still has the weakness of increased cell weight if size is not a power of two.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 7 at 11:51

























    answered Nov 7 at 11:06









    Aziuth

    1,595817




    1,595817












    • What if I wanted to not use vectors in this specific situation?
      – user2430974
      Nov 7 at 11:15










    • Then you replace it by a raw array and store it's size in some integer. Note that you then need to delete it to avoid a memory leak. However, std::vector does only use up marginally more memory than a raw array. In almost any situation, you'd want to use a container. If you get the raw array from some part that you do not own, then create a vector out of the input where my code copies the data (data = input_data).
      – Aziuth
      Nov 7 at 11:18












    • Edited my answer to address that.
      – Aziuth
      Nov 7 at 11:23








    • 1




      and "marginally more" here means the difference between 3 pointers and 1 pointer and an int. Also you delete things that you newed, deleteing an array is UB.
      – Caleth
      Nov 7 at 11:25










    • I'd take input_data by value, rather than copy in the body.
      – Caleth
      Nov 7 at 11:28


















    • What if I wanted to not use vectors in this specific situation?
      – user2430974
      Nov 7 at 11:15










    • Then you replace it by a raw array and store it's size in some integer. Note that you then need to delete it to avoid a memory leak. However, std::vector does only use up marginally more memory than a raw array. In almost any situation, you'd want to use a container. If you get the raw array from some part that you do not own, then create a vector out of the input where my code copies the data (data = input_data).
      – Aziuth
      Nov 7 at 11:18












    • Edited my answer to address that.
      – Aziuth
      Nov 7 at 11:23








    • 1




      and "marginally more" here means the difference between 3 pointers and 1 pointer and an int. Also you delete things that you newed, deleteing an array is UB.
      – Caleth
      Nov 7 at 11:25










    • I'd take input_data by value, rather than copy in the body.
      – Caleth
      Nov 7 at 11:28
















    What if I wanted to not use vectors in this specific situation?
    – user2430974
    Nov 7 at 11:15




    What if I wanted to not use vectors in this specific situation?
    – user2430974
    Nov 7 at 11:15












    Then you replace it by a raw array and store it's size in some integer. Note that you then need to delete it to avoid a memory leak. However, std::vector does only use up marginally more memory than a raw array. In almost any situation, you'd want to use a container. If you get the raw array from some part that you do not own, then create a vector out of the input where my code copies the data (data = input_data).
    – Aziuth
    Nov 7 at 11:18






    Then you replace it by a raw array and store it's size in some integer. Note that you then need to delete it to avoid a memory leak. However, std::vector does only use up marginally more memory than a raw array. In almost any situation, you'd want to use a container. If you get the raw array from some part that you do not own, then create a vector out of the input where my code copies the data (data = input_data).
    – Aziuth
    Nov 7 at 11:18














    Edited my answer to address that.
    – Aziuth
    Nov 7 at 11:23






    Edited my answer to address that.
    – Aziuth
    Nov 7 at 11:23






    1




    1




    and "marginally more" here means the difference between 3 pointers and 1 pointer and an int. Also you delete things that you newed, deleteing an array is UB.
    – Caleth
    Nov 7 at 11:25




    and "marginally more" here means the difference between 3 pointers and 1 pointer and an int. Also you delete things that you newed, deleteing an array is UB.
    – Caleth
    Nov 7 at 11:25












    I'd take input_data by value, rather than copy in the body.
    – Caleth
    Nov 7 at 11:28




    I'd take input_data by value, rather than copy in the body.
    – Caleth
    Nov 7 at 11:28












    up vote
    1
    down vote













    You might use:



    constexpr bool is_power_of_2(uint64_t n)
    {
    return n && !(n & (n - 1));
    }

    uint64_t array_average(std::vector<uint64_t> v)
    {
    if (!is_power_of_2(v.size())) {
    throw std::runtime_error("invalid size");
    }
    uint64_t remainder = 0;
    while (v.size() != 1) {
    for (int i = 0; i != v.size(); i += 2) {
    remainder += (a[i] % 2 + a[i + 1] % 2);
    b[i / 2] = a[i] / 2 + a[i + 1] / 2;
    if (remainder >= 2 && b[i / 2] < -(remainder / 2)) {
    b[i / 2] += remainder / 2;
    remainder %= 2;
    }
    }
    v.resize(v.size() / 2);
    }
    return v[0] + remainder / 2;
    }





    share|improve this answer



























      up vote
      1
      down vote













      You might use:



      constexpr bool is_power_of_2(uint64_t n)
      {
      return n && !(n & (n - 1));
      }

      uint64_t array_average(std::vector<uint64_t> v)
      {
      if (!is_power_of_2(v.size())) {
      throw std::runtime_error("invalid size");
      }
      uint64_t remainder = 0;
      while (v.size() != 1) {
      for (int i = 0; i != v.size(); i += 2) {
      remainder += (a[i] % 2 + a[i + 1] % 2);
      b[i / 2] = a[i] / 2 + a[i + 1] / 2;
      if (remainder >= 2 && b[i / 2] < -(remainder / 2)) {
      b[i / 2] += remainder / 2;
      remainder %= 2;
      }
      }
      v.resize(v.size() / 2);
      }
      return v[0] + remainder / 2;
      }





      share|improve this answer

























        up vote
        1
        down vote










        up vote
        1
        down vote









        You might use:



        constexpr bool is_power_of_2(uint64_t n)
        {
        return n && !(n & (n - 1));
        }

        uint64_t array_average(std::vector<uint64_t> v)
        {
        if (!is_power_of_2(v.size())) {
        throw std::runtime_error("invalid size");
        }
        uint64_t remainder = 0;
        while (v.size() != 1) {
        for (int i = 0; i != v.size(); i += 2) {
        remainder += (a[i] % 2 + a[i + 1] % 2);
        b[i / 2] = a[i] / 2 + a[i + 1] / 2;
        if (remainder >= 2 && b[i / 2] < -(remainder / 2)) {
        b[i / 2] += remainder / 2;
        remainder %= 2;
        }
        }
        v.resize(v.size() / 2);
        }
        return v[0] + remainder / 2;
        }





        share|improve this answer














        You might use:



        constexpr bool is_power_of_2(uint64_t n)
        {
        return n && !(n & (n - 1));
        }

        uint64_t array_average(std::vector<uint64_t> v)
        {
        if (!is_power_of_2(v.size())) {
        throw std::runtime_error("invalid size");
        }
        uint64_t remainder = 0;
        while (v.size() != 1) {
        for (int i = 0; i != v.size(); i += 2) {
        remainder += (a[i] % 2 + a[i + 1] % 2);
        b[i / 2] = a[i] / 2 + a[i + 1] / 2;
        if (remainder >= 2 && b[i / 2] < -(remainder / 2)) {
        b[i / 2] += remainder / 2;
        remainder %= 2;
        }
        }
        v.resize(v.size() / 2);
        }
        return v[0] + remainder / 2;
        }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 7 at 11:27

























        answered Nov 7 at 11:11









        Jarod42

        111k1299179




        111k1299179






















            up vote
            0
            down vote













            There really shouldn't be that much to convert as there are containers, functions and algorithms in the stl that already exist that will do this for you. With out any function examine this short program:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>

            int main() {
            try {

            std::vector<uint64_t> values{ 1,2,3,4,5,6,7,8,9,10,11,12 };
            int total = std::accumulate( values.begin(), values.end(), 0 );
            uint64_t average = static_cast<uint64_t>( total ) / values.size();
            std::cout << average << 'n';

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            On my machine windows 7 ultimate 64bit running visual studio 2017 CE compiled with language version set to most recent c++17 or greater. This does give me a compiler warning! Warning: C4244 generated due to conversion and possible loss of data. However there are no compiler errors and it does run and give the expected result. The output here is 6 as expected since integer division is truncated. If I change these lines of code above to this:



            double total = std::accumulate( values.begin(), values.end(),
            static_cast<double>( 0 ) );
            double average = total / values.size();


            It fixes the compiler warnings above by adding the static_cast and it sure enough prints out 6.5 which is the actual value.



            This is all fine and good since the vector is already initialized with values; however, this may not be always the case so let's move this into a function that will take an arbitrary array. It would look something like this:



            uint64_t array_average( std::vector<uint64_t>& values ) {
            // Prevent Division by 0 and early return
            // as to not call `std::accumulate`
            if ( !values.empty() ) {
            // check if only 1 entry if so just return it
            if ( values.size() == 1 ) {
            return values[0];
            } else { // otherwise do the calculation.
            return std::accumulate( values.begin(), values.end(),
            static_cast<uint64_t>( 0 ) ) / values.size();
            }
            }
            // Empty Container
            throw std::runtime_error( "Can not take average of an empty container" );
            }


            This function is nice and all, we can do better by improving this by making it a little more generic that will work with any arithmetic type!



            template<typename T>
            T array_average( std::vector<T>& values ) {
            if( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            At first glance this looks okay. This will compile and run if you use this with types that are arithmetic. However, if we use it with a type that isn't this will fail to compile. For example:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>
            #include <type_traits>

            class Fruit {
            protected:
            std::string name_;
            public:
            std::string operator()() const {
            return name_;
            }
            std::string name() const { return name_; }

            Fruit operator+( const Fruit& other ) {
            this->name_ += " " + other.name();
            return *this;
            }
            };

            class Apple : public Fruit {
            public:
            Apple() { this->name_ = "Apple"; }

            };

            class Banana : public Fruit {
            public:
            Banana() { this->name_ = "Banana"; }
            };

            class Pear : public Fruit {
            public:
            Pear() { this->name_ = "Pear"; }
            };

            std::ostream& operator<<( std::ostream& os, const Fruit& fruit ) {
            os << fruit.name() << " ";
            return os;
            }

            template<typename T>
            T array_average( std::vector<T>& values ); // Using the definition above

            int main() {
            try {
            std::vector<uint64_t> values { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
            std::vector<double> values2 { 2.0, 3.5, 4.5, 6.7, 8.9 };
            std::vector<Fruit> fruits { Apple(), Banana(), Pear() };

            std::cout << array_average( values ) << 'n'; // compiles runs and prints 6
            std::cout << array_average( values2 ) << 'n'; // compiles runs and prints 5.12
            std::cout << array_average( fruits ) << 'n'; // fails to compile.

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            This fails to compile because the static_cast can not convert int to T with T = Fruit MSVC compiler error C2440



            We can fix this by changing a single line of code in our function template if your compiler supports it:



            We can change if( std::is_arithmetic<T>::value ) to if constexpr( std::is_arithmetic<T>::value ) and our function will now look like this:



            template<typename T>
            T array_average( const std::vector<T>& values ) {
            if constexpr( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            You can run the same program above and it will fully compile even when you are using types that are not arithmetic.



            int main() {
            //....
            std::cout << array_average( fruits ) << 'n'; // Now compiles
            //...
            }


            However when you run this code it will generate a Runtime Error and depending on how your IDE and debugger is setup you may need to put a break point within the catch statement where the return EXIT_FAILURE is to see the message printed to the screen, otherwise the application may just exit without any notification at all.



            If you don't want runtime errors you can substitute and produce compiler time errors by using static_assert instead of throwing a runtime error. This can be a handy little function, but it isn't 100% without some minor limitations and gotchas, but to find out more information about this function you can check the Question that I had asked when I was writing the implementation to this function that can be found here and you can read the comments there that will give you more insight to some of the limitations that this function provides.



            One of the current limitations with this function would be this: let's say we have a container that has a bunch of complex numbers (3i + 2), (4i - 6), (7i + 3) well you can still take the average of these as it is a valid thing, but the above function will not consider this to be arithmetic in it's current state.



            To resolve this issue what can be done is this: instead of using std::is_arithmetic<t> you could write your own policy and traits that this function should accept. I'll leave that part as an exercise for you.



            As you can see a majority of the work is already being done for us with the standard library. We used accumulate and divided by the containers size and we were done, the rest of the time involved was making sure it accepts proper types, if it's to be thread safe and or exception safe etc.



            Finally we did not have to worry about cumbersome for loops on arrays and making sure the loops didn't exceed the size of the array. We did not have to call new and worry about when and where to call delete in order to not have any memory leaks. ASFAIK I do not think that std::accumulate will overflow on supporting containers, but don't quote me on this one. It may depend on the types that are in the container and that there is a static_cast involved. Even with some of these caveats in many cases it is still better to use containers than managing your own raw memory, and to use the algorithms and functions that are designed to work on them. They make things a lot simpler and easier to manage and even debug.






            share|improve this answer























            • I think you missed the point of his problem. Your first block will surely not work if you have an array that stores the maximum of uint64_t one thousand times, while the idea of his algorithm would. Your second block which uses a double will obviously cause a loss of information in such a case, for the fact that a double can't hold more information than a uint64_t. I strongly assume that the guy works with very large numbers that come close to the maximum of uint64_t, and/or with very large arrays, and I also assume that the result is desired to be as precise as possible.
              – Aziuth
              Nov 7 at 22:45












            • You would be correct in assuming @Aziuth. With what I'm working with the arrays are ~1GB (134217728 items at 64bits) in size and even the smallest value would cause an overflow quite quickly.
              – user2430974
              Nov 8 at 0:19












            • It appears that the question was reworded since the time of me writing my answer and now that I have read it again with the current updates it is a little more clear of what is being asked. At first it appeared that the OP wanted to convert the algorithm or function into something simpler. Now it makes sense that you are expecting overflow and to that my answer is now irrelevant and what the OP is looking for is something on the lines of a huge integer library function or implementation with efficiency at its core design.
              – Francis Cugler
              Nov 8 at 1:19

















            up vote
            0
            down vote













            There really shouldn't be that much to convert as there are containers, functions and algorithms in the stl that already exist that will do this for you. With out any function examine this short program:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>

            int main() {
            try {

            std::vector<uint64_t> values{ 1,2,3,4,5,6,7,8,9,10,11,12 };
            int total = std::accumulate( values.begin(), values.end(), 0 );
            uint64_t average = static_cast<uint64_t>( total ) / values.size();
            std::cout << average << 'n';

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            On my machine windows 7 ultimate 64bit running visual studio 2017 CE compiled with language version set to most recent c++17 or greater. This does give me a compiler warning! Warning: C4244 generated due to conversion and possible loss of data. However there are no compiler errors and it does run and give the expected result. The output here is 6 as expected since integer division is truncated. If I change these lines of code above to this:



            double total = std::accumulate( values.begin(), values.end(),
            static_cast<double>( 0 ) );
            double average = total / values.size();


            It fixes the compiler warnings above by adding the static_cast and it sure enough prints out 6.5 which is the actual value.



            This is all fine and good since the vector is already initialized with values; however, this may not be always the case so let's move this into a function that will take an arbitrary array. It would look something like this:



            uint64_t array_average( std::vector<uint64_t>& values ) {
            // Prevent Division by 0 and early return
            // as to not call `std::accumulate`
            if ( !values.empty() ) {
            // check if only 1 entry if so just return it
            if ( values.size() == 1 ) {
            return values[0];
            } else { // otherwise do the calculation.
            return std::accumulate( values.begin(), values.end(),
            static_cast<uint64_t>( 0 ) ) / values.size();
            }
            }
            // Empty Container
            throw std::runtime_error( "Can not take average of an empty container" );
            }


            This function is nice and all, we can do better by improving this by making it a little more generic that will work with any arithmetic type!



            template<typename T>
            T array_average( std::vector<T>& values ) {
            if( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            At first glance this looks okay. This will compile and run if you use this with types that are arithmetic. However, if we use it with a type that isn't this will fail to compile. For example:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>
            #include <type_traits>

            class Fruit {
            protected:
            std::string name_;
            public:
            std::string operator()() const {
            return name_;
            }
            std::string name() const { return name_; }

            Fruit operator+( const Fruit& other ) {
            this->name_ += " " + other.name();
            return *this;
            }
            };

            class Apple : public Fruit {
            public:
            Apple() { this->name_ = "Apple"; }

            };

            class Banana : public Fruit {
            public:
            Banana() { this->name_ = "Banana"; }
            };

            class Pear : public Fruit {
            public:
            Pear() { this->name_ = "Pear"; }
            };

            std::ostream& operator<<( std::ostream& os, const Fruit& fruit ) {
            os << fruit.name() << " ";
            return os;
            }

            template<typename T>
            T array_average( std::vector<T>& values ); // Using the definition above

            int main() {
            try {
            std::vector<uint64_t> values { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
            std::vector<double> values2 { 2.0, 3.5, 4.5, 6.7, 8.9 };
            std::vector<Fruit> fruits { Apple(), Banana(), Pear() };

            std::cout << array_average( values ) << 'n'; // compiles runs and prints 6
            std::cout << array_average( values2 ) << 'n'; // compiles runs and prints 5.12
            std::cout << array_average( fruits ) << 'n'; // fails to compile.

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            This fails to compile because the static_cast can not convert int to T with T = Fruit MSVC compiler error C2440



            We can fix this by changing a single line of code in our function template if your compiler supports it:



            We can change if( std::is_arithmetic<T>::value ) to if constexpr( std::is_arithmetic<T>::value ) and our function will now look like this:



            template<typename T>
            T array_average( const std::vector<T>& values ) {
            if constexpr( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            You can run the same program above and it will fully compile even when you are using types that are not arithmetic.



            int main() {
            //....
            std::cout << array_average( fruits ) << 'n'; // Now compiles
            //...
            }


            However when you run this code it will generate a Runtime Error and depending on how your IDE and debugger is setup you may need to put a break point within the catch statement where the return EXIT_FAILURE is to see the message printed to the screen, otherwise the application may just exit without any notification at all.



            If you don't want runtime errors you can substitute and produce compiler time errors by using static_assert instead of throwing a runtime error. This can be a handy little function, but it isn't 100% without some minor limitations and gotchas, but to find out more information about this function you can check the Question that I had asked when I was writing the implementation to this function that can be found here and you can read the comments there that will give you more insight to some of the limitations that this function provides.



            One of the current limitations with this function would be this: let's say we have a container that has a bunch of complex numbers (3i + 2), (4i - 6), (7i + 3) well you can still take the average of these as it is a valid thing, but the above function will not consider this to be arithmetic in it's current state.



            To resolve this issue what can be done is this: instead of using std::is_arithmetic<t> you could write your own policy and traits that this function should accept. I'll leave that part as an exercise for you.



            As you can see a majority of the work is already being done for us with the standard library. We used accumulate and divided by the containers size and we were done, the rest of the time involved was making sure it accepts proper types, if it's to be thread safe and or exception safe etc.



            Finally we did not have to worry about cumbersome for loops on arrays and making sure the loops didn't exceed the size of the array. We did not have to call new and worry about when and where to call delete in order to not have any memory leaks. ASFAIK I do not think that std::accumulate will overflow on supporting containers, but don't quote me on this one. It may depend on the types that are in the container and that there is a static_cast involved. Even with some of these caveats in many cases it is still better to use containers than managing your own raw memory, and to use the algorithms and functions that are designed to work on them. They make things a lot simpler and easier to manage and even debug.






            share|improve this answer























            • I think you missed the point of his problem. Your first block will surely not work if you have an array that stores the maximum of uint64_t one thousand times, while the idea of his algorithm would. Your second block which uses a double will obviously cause a loss of information in such a case, for the fact that a double can't hold more information than a uint64_t. I strongly assume that the guy works with very large numbers that come close to the maximum of uint64_t, and/or with very large arrays, and I also assume that the result is desired to be as precise as possible.
              – Aziuth
              Nov 7 at 22:45












            • You would be correct in assuming @Aziuth. With what I'm working with the arrays are ~1GB (134217728 items at 64bits) in size and even the smallest value would cause an overflow quite quickly.
              – user2430974
              Nov 8 at 0:19












            • It appears that the question was reworded since the time of me writing my answer and now that I have read it again with the current updates it is a little more clear of what is being asked. At first it appeared that the OP wanted to convert the algorithm or function into something simpler. Now it makes sense that you are expecting overflow and to that my answer is now irrelevant and what the OP is looking for is something on the lines of a huge integer library function or implementation with efficiency at its core design.
              – Francis Cugler
              Nov 8 at 1:19















            up vote
            0
            down vote










            up vote
            0
            down vote









            There really shouldn't be that much to convert as there are containers, functions and algorithms in the stl that already exist that will do this for you. With out any function examine this short program:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>

            int main() {
            try {

            std::vector<uint64_t> values{ 1,2,3,4,5,6,7,8,9,10,11,12 };
            int total = std::accumulate( values.begin(), values.end(), 0 );
            uint64_t average = static_cast<uint64_t>( total ) / values.size();
            std::cout << average << 'n';

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            On my machine windows 7 ultimate 64bit running visual studio 2017 CE compiled with language version set to most recent c++17 or greater. This does give me a compiler warning! Warning: C4244 generated due to conversion and possible loss of data. However there are no compiler errors and it does run and give the expected result. The output here is 6 as expected since integer division is truncated. If I change these lines of code above to this:



            double total = std::accumulate( values.begin(), values.end(),
            static_cast<double>( 0 ) );
            double average = total / values.size();


            It fixes the compiler warnings above by adding the static_cast and it sure enough prints out 6.5 which is the actual value.



            This is all fine and good since the vector is already initialized with values; however, this may not be always the case so let's move this into a function that will take an arbitrary array. It would look something like this:



            uint64_t array_average( std::vector<uint64_t>& values ) {
            // Prevent Division by 0 and early return
            // as to not call `std::accumulate`
            if ( !values.empty() ) {
            // check if only 1 entry if so just return it
            if ( values.size() == 1 ) {
            return values[0];
            } else { // otherwise do the calculation.
            return std::accumulate( values.begin(), values.end(),
            static_cast<uint64_t>( 0 ) ) / values.size();
            }
            }
            // Empty Container
            throw std::runtime_error( "Can not take average of an empty container" );
            }


            This function is nice and all, we can do better by improving this by making it a little more generic that will work with any arithmetic type!



            template<typename T>
            T array_average( std::vector<T>& values ) {
            if( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            At first glance this looks okay. This will compile and run if you use this with types that are arithmetic. However, if we use it with a type that isn't this will fail to compile. For example:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>
            #include <type_traits>

            class Fruit {
            protected:
            std::string name_;
            public:
            std::string operator()() const {
            return name_;
            }
            std::string name() const { return name_; }

            Fruit operator+( const Fruit& other ) {
            this->name_ += " " + other.name();
            return *this;
            }
            };

            class Apple : public Fruit {
            public:
            Apple() { this->name_ = "Apple"; }

            };

            class Banana : public Fruit {
            public:
            Banana() { this->name_ = "Banana"; }
            };

            class Pear : public Fruit {
            public:
            Pear() { this->name_ = "Pear"; }
            };

            std::ostream& operator<<( std::ostream& os, const Fruit& fruit ) {
            os << fruit.name() << " ";
            return os;
            }

            template<typename T>
            T array_average( std::vector<T>& values ); // Using the definition above

            int main() {
            try {
            std::vector<uint64_t> values { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
            std::vector<double> values2 { 2.0, 3.5, 4.5, 6.7, 8.9 };
            std::vector<Fruit> fruits { Apple(), Banana(), Pear() };

            std::cout << array_average( values ) << 'n'; // compiles runs and prints 6
            std::cout << array_average( values2 ) << 'n'; // compiles runs and prints 5.12
            std::cout << array_average( fruits ) << 'n'; // fails to compile.

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            This fails to compile because the static_cast can not convert int to T with T = Fruit MSVC compiler error C2440



            We can fix this by changing a single line of code in our function template if your compiler supports it:



            We can change if( std::is_arithmetic<T>::value ) to if constexpr( std::is_arithmetic<T>::value ) and our function will now look like this:



            template<typename T>
            T array_average( const std::vector<T>& values ) {
            if constexpr( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            You can run the same program above and it will fully compile even when you are using types that are not arithmetic.



            int main() {
            //....
            std::cout << array_average( fruits ) << 'n'; // Now compiles
            //...
            }


            However when you run this code it will generate a Runtime Error and depending on how your IDE and debugger is setup you may need to put a break point within the catch statement where the return EXIT_FAILURE is to see the message printed to the screen, otherwise the application may just exit without any notification at all.



            If you don't want runtime errors you can substitute and produce compiler time errors by using static_assert instead of throwing a runtime error. This can be a handy little function, but it isn't 100% without some minor limitations and gotchas, but to find out more information about this function you can check the Question that I had asked when I was writing the implementation to this function that can be found here and you can read the comments there that will give you more insight to some of the limitations that this function provides.



            One of the current limitations with this function would be this: let's say we have a container that has a bunch of complex numbers (3i + 2), (4i - 6), (7i + 3) well you can still take the average of these as it is a valid thing, but the above function will not consider this to be arithmetic in it's current state.



            To resolve this issue what can be done is this: instead of using std::is_arithmetic<t> you could write your own policy and traits that this function should accept. I'll leave that part as an exercise for you.



            As you can see a majority of the work is already being done for us with the standard library. We used accumulate and divided by the containers size and we were done, the rest of the time involved was making sure it accepts proper types, if it's to be thread safe and or exception safe etc.



            Finally we did not have to worry about cumbersome for loops on arrays and making sure the loops didn't exceed the size of the array. We did not have to call new and worry about when and where to call delete in order to not have any memory leaks. ASFAIK I do not think that std::accumulate will overflow on supporting containers, but don't quote me on this one. It may depend on the types that are in the container and that there is a static_cast involved. Even with some of these caveats in many cases it is still better to use containers than managing your own raw memory, and to use the algorithms and functions that are designed to work on them. They make things a lot simpler and easier to manage and even debug.






            share|improve this answer














            There really shouldn't be that much to convert as there are containers, functions and algorithms in the stl that already exist that will do this for you. With out any function examine this short program:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>

            int main() {
            try {

            std::vector<uint64_t> values{ 1,2,3,4,5,6,7,8,9,10,11,12 };
            int total = std::accumulate( values.begin(), values.end(), 0 );
            uint64_t average = static_cast<uint64_t>( total ) / values.size();
            std::cout << average << 'n';

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            On my machine windows 7 ultimate 64bit running visual studio 2017 CE compiled with language version set to most recent c++17 or greater. This does give me a compiler warning! Warning: C4244 generated due to conversion and possible loss of data. However there are no compiler errors and it does run and give the expected result. The output here is 6 as expected since integer division is truncated. If I change these lines of code above to this:



            double total = std::accumulate( values.begin(), values.end(),
            static_cast<double>( 0 ) );
            double average = total / values.size();


            It fixes the compiler warnings above by adding the static_cast and it sure enough prints out 6.5 which is the actual value.



            This is all fine and good since the vector is already initialized with values; however, this may not be always the case so let's move this into a function that will take an arbitrary array. It would look something like this:



            uint64_t array_average( std::vector<uint64_t>& values ) {
            // Prevent Division by 0 and early return
            // as to not call `std::accumulate`
            if ( !values.empty() ) {
            // check if only 1 entry if so just return it
            if ( values.size() == 1 ) {
            return values[0];
            } else { // otherwise do the calculation.
            return std::accumulate( values.begin(), values.end(),
            static_cast<uint64_t>( 0 ) ) / values.size();
            }
            }
            // Empty Container
            throw std::runtime_error( "Can not take average of an empty container" );
            }


            This function is nice and all, we can do better by improving this by making it a little more generic that will work with any arithmetic type!



            template<typename T>
            T array_average( std::vector<T>& values ) {
            if( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            At first glance this looks okay. This will compile and run if you use this with types that are arithmetic. However, if we use it with a type that isn't this will fail to compile. For example:



            #include <vector>
            #include <numeric>
            #include <iostream>
            #include <exception>
            #include <type_traits>

            class Fruit {
            protected:
            std::string name_;
            public:
            std::string operator()() const {
            return name_;
            }
            std::string name() const { return name_; }

            Fruit operator+( const Fruit& other ) {
            this->name_ += " " + other.name();
            return *this;
            }
            };

            class Apple : public Fruit {
            public:
            Apple() { this->name_ = "Apple"; }

            };

            class Banana : public Fruit {
            public:
            Banana() { this->name_ = "Banana"; }
            };

            class Pear : public Fruit {
            public:
            Pear() { this->name_ = "Pear"; }
            };

            std::ostream& operator<<( std::ostream& os, const Fruit& fruit ) {
            os << fruit.name() << " ";
            return os;
            }

            template<typename T>
            T array_average( std::vector<T>& values ); // Using the definition above

            int main() {
            try {
            std::vector<uint64_t> values { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
            std::vector<double> values2 { 2.0, 3.5, 4.5, 6.7, 8.9 };
            std::vector<Fruit> fruits { Apple(), Banana(), Pear() };

            std::cout << array_average( values ) << 'n'; // compiles runs and prints 6
            std::cout << array_average( values2 ) << 'n'; // compiles runs and prints 5.12
            std::cout << array_average( fruits ) << 'n'; // fails to compile.

            } catch( const std::runtime_error& e ) {
            std::cerr << e.what() << 'n';
            return EXIT_FAILURE;
            }
            return EXIT_SUCCESS;
            }


            This fails to compile because the static_cast can not convert int to T with T = Fruit MSVC compiler error C2440



            We can fix this by changing a single line of code in our function template if your compiler supports it:



            We can change if( std::is_arithmetic<T>::value ) to if constexpr( std::is_arithmetic<T>::value ) and our function will now look like this:



            template<typename T>
            T array_average( const std::vector<T>& values ) {
            if constexpr( std::is_arithmetic<T>::value ) {
            if( !values.empty() ) {
            if( values.size() == 1 ) {
            return values[0];
            } else {
            return std::accumulate( values.begin(), values.end(), static_cast<T>( 0 ) ) / values.size();
            }
            } else {
            throw std::runtime_error( "Can not take average of an empty container" );
            }
            } else {
            throw std::runtime_error( "T is not of an arithmetic type" );
            }
            }


            You can run the same program above and it will fully compile even when you are using types that are not arithmetic.



            int main() {
            //....
            std::cout << array_average( fruits ) << 'n'; // Now compiles
            //...
            }


            However when you run this code it will generate a Runtime Error and depending on how your IDE and debugger is setup you may need to put a break point within the catch statement where the return EXIT_FAILURE is to see the message printed to the screen, otherwise the application may just exit without any notification at all.



            If you don't want runtime errors you can substitute and produce compiler time errors by using static_assert instead of throwing a runtime error. This can be a handy little function, but it isn't 100% without some minor limitations and gotchas, but to find out more information about this function you can check the Question that I had asked when I was writing the implementation to this function that can be found here and you can read the comments there that will give you more insight to some of the limitations that this function provides.



            One of the current limitations with this function would be this: let's say we have a container that has a bunch of complex numbers (3i + 2), (4i - 6), (7i + 3) well you can still take the average of these as it is a valid thing, but the above function will not consider this to be arithmetic in it's current state.



            To resolve this issue what can be done is this: instead of using std::is_arithmetic<t> you could write your own policy and traits that this function should accept. I'll leave that part as an exercise for you.



            As you can see a majority of the work is already being done for us with the standard library. We used accumulate and divided by the containers size and we were done, the rest of the time involved was making sure it accepts proper types, if it's to be thread safe and or exception safe etc.



            Finally we did not have to worry about cumbersome for loops on arrays and making sure the loops didn't exceed the size of the array. We did not have to call new and worry about when and where to call delete in order to not have any memory leaks. ASFAIK I do not think that std::accumulate will overflow on supporting containers, but don't quote me on this one. It may depend on the types that are in the container and that there is a static_cast involved. Even with some of these caveats in many cases it is still better to use containers than managing your own raw memory, and to use the algorithms and functions that are designed to work on them. They make things a lot simpler and easier to manage and even debug.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 7 at 15:20

























            answered Nov 7 at 14:47









            Francis Cugler

            4,10311227




            4,10311227












            • I think you missed the point of his problem. Your first block will surely not work if you have an array that stores the maximum of uint64_t one thousand times, while the idea of his algorithm would. Your second block which uses a double will obviously cause a loss of information in such a case, for the fact that a double can't hold more information than a uint64_t. I strongly assume that the guy works with very large numbers that come close to the maximum of uint64_t, and/or with very large arrays, and I also assume that the result is desired to be as precise as possible.
              – Aziuth
              Nov 7 at 22:45












            • You would be correct in assuming @Aziuth. With what I'm working with the arrays are ~1GB (134217728 items at 64bits) in size and even the smallest value would cause an overflow quite quickly.
              – user2430974
              Nov 8 at 0:19












            • It appears that the question was reworded since the time of me writing my answer and now that I have read it again with the current updates it is a little more clear of what is being asked. At first it appeared that the OP wanted to convert the algorithm or function into something simpler. Now it makes sense that you are expecting overflow and to that my answer is now irrelevant and what the OP is looking for is something on the lines of a huge integer library function or implementation with efficiency at its core design.
              – Francis Cugler
              Nov 8 at 1:19




















            • I think you missed the point of his problem. Your first block will surely not work if you have an array that stores the maximum of uint64_t one thousand times, while the idea of his algorithm would. Your second block which uses a double will obviously cause a loss of information in such a case, for the fact that a double can't hold more information than a uint64_t. I strongly assume that the guy works with very large numbers that come close to the maximum of uint64_t, and/or with very large arrays, and I also assume that the result is desired to be as precise as possible.
              – Aziuth
              Nov 7 at 22:45












            • You would be correct in assuming @Aziuth. With what I'm working with the arrays are ~1GB (134217728 items at 64bits) in size and even the smallest value would cause an overflow quite quickly.
              – user2430974
              Nov 8 at 0:19












            • It appears that the question was reworded since the time of me writing my answer and now that I have read it again with the current updates it is a little more clear of what is being asked. At first it appeared that the OP wanted to convert the algorithm or function into something simpler. Now it makes sense that you are expecting overflow and to that my answer is now irrelevant and what the OP is looking for is something on the lines of a huge integer library function or implementation with efficiency at its core design.
              – Francis Cugler
              Nov 8 at 1:19


















            I think you missed the point of his problem. Your first block will surely not work if you have an array that stores the maximum of uint64_t one thousand times, while the idea of his algorithm would. Your second block which uses a double will obviously cause a loss of information in such a case, for the fact that a double can't hold more information than a uint64_t. I strongly assume that the guy works with very large numbers that come close to the maximum of uint64_t, and/or with very large arrays, and I also assume that the result is desired to be as precise as possible.
            – Aziuth
            Nov 7 at 22:45






            I think you missed the point of his problem. Your first block will surely not work if you have an array that stores the maximum of uint64_t one thousand times, while the idea of his algorithm would. Your second block which uses a double will obviously cause a loss of information in such a case, for the fact that a double can't hold more information than a uint64_t. I strongly assume that the guy works with very large numbers that come close to the maximum of uint64_t, and/or with very large arrays, and I also assume that the result is desired to be as precise as possible.
            – Aziuth
            Nov 7 at 22:45














            You would be correct in assuming @Aziuth. With what I'm working with the arrays are ~1GB (134217728 items at 64bits) in size and even the smallest value would cause an overflow quite quickly.
            – user2430974
            Nov 8 at 0:19






            You would be correct in assuming @Aziuth. With what I'm working with the arrays are ~1GB (134217728 items at 64bits) in size and even the smallest value would cause an overflow quite quickly.
            – user2430974
            Nov 8 at 0:19














            It appears that the question was reworded since the time of me writing my answer and now that I have read it again with the current updates it is a little more clear of what is being asked. At first it appeared that the OP wanted to convert the algorithm or function into something simpler. Now it makes sense that you are expecting overflow and to that my answer is now irrelevant and what the OP is looking for is something on the lines of a huge integer library function or implementation with efficiency at its core design.
            – Francis Cugler
            Nov 8 at 1:19






            It appears that the question was reworded since the time of me writing my answer and now that I have read it again with the current updates it is a little more clear of what is being asked. At first it appeared that the OP wanted to convert the algorithm or function into something simpler. Now it makes sense that you are expecting overflow and to that my answer is now irrelevant and what the OP is looking for is something on the lines of a huge integer library function or implementation with efficiency at its core design.
            – Francis Cugler
            Nov 8 at 1:19




















             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53187827%2fhow-would-i-go-about-optimizing-this-code%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            Hercules Kyvelos

            Tangent Lines Diagram Along Smooth Curve

            Yusuf al-Mu'taman ibn Hud