Skip to content

Instantly share code, notes, and snippets.

@conqp
Last active July 2, 2021 11:07
Show Gist options
  • Select an option

  • Save conqp/99bdc96633da390ce23af92b155c8dd3 to your computer and use it in GitHub Desktop.

Select an option

Save conqp/99bdc96633da390ce23af92b155c8dd3 to your computer and use it in GitHub Desktop.
Precisely sum up floats while avoiding rising precision loss
#include <algorithm>
using std::for_each;
using std::reverse;
#include <cmath>
using std::modf;
#include <iomanip>
using std::left;
using std::setprecision;
using std::setw;
#include <iostream>
using std::cout;
#include <numeric>
using std::accumulate;
#include <vector>
using std::begin;
using std::end;
using std::vector;
float sumNaive(vector<float> const & floats)
{
return accumulate(begin(floats), end(floats), 0.0f);
}
double sumDouble(vector<float> const & floats)
{
double result = 0;
for (auto f : floats)
result += f;
return result;
}
float sumNormalized(vector<float> const & floats)
{
int integerSum = 0;
float floatSum = 0.0f;
for_each(begin(floats), end(floats), [&integerSum, &floatSum] (float num) {
float integer;
floatSum += modf(num, &integer);
integerSum += static_cast<int>(integer);
});
return integerSum + floatSum;
}
float sumStrictlyNormalized(vector<float> const & floats)
{
int integerSum = 0;
float floatSum = 0.0f;
for_each(begin(floats), end(floats), [&integerSum, &floatSum] (float num) {
float integer;
if (floatSum > 1) {
floatSum = modf(floatSum, &integer);
integerSum += static_cast<int>(integer);
}
floatSum += modf(num, &integer);
integerSum += static_cast<int>(integer);
});
return integerSum + floatSum;
}
template<class SORTED_VECTOR_ITERATOR>
float recursiveSum(SORTED_VECTOR_ITERATOR itBegin, SORTED_VECTOR_ITERATOR itEnd) {
auto d = std::distance(itBegin, itEnd);
if (d == 1)
return *itBegin;
return recursiveSum(itBegin, itBegin + d / 2) + recursiveSum(itBegin + d / 2, itEnd);
}
float preciseSum(vector<float> const & floats)
{
return recursiveSum(begin(floats), end(floats));
}
float getRandomFloat()
{
return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
}
vector<float> getRandomFloats(unsigned int amount)
{
vector<float> result;
for (unsigned int i = 0; i < amount; ++i)
result.push_back(getRandomFloat());
return result;
}
int main()
{
auto floats = getRandomFloats(10'000'000);
cout << setprecision(10) << left;
cout << "### Unsorted ###\n";
cout << setw(21) << "Double: " << sumDouble(floats) << "\n";
cout << setw(21) << "Naive: " << sumNaive(floats) << "\n";
cout << setw(21) << "Normalized: " << sumNormalized(floats) << "\n";
cout << setw(21) << "Strictly normalized: " << sumStrictlyNormalized(floats) << "\n";
cout << setw(21) << "Precise sum: " << preciseSum(floats) << "\n";
sort(begin(floats), end(floats));
cout << "### Sorted ###\n";
cout << setw(21) << "Double: " << sumDouble(floats) << "\n";
cout << setw(21) << "Naive: " << sumNaive(floats) << "\n";
cout << setw(21) << "Normalized: " << sumNormalized(floats) << "\n";
cout << setw(21) << "Strictly normalized: " << sumStrictlyNormalized(floats) << "\n";
cout << setw(21) << "Precise sum: " << preciseSum(floats) << "\n";
reverse(begin(floats), end(floats));
cout << "### Revese sorted ###\n";
cout << setw(21) << "Double: " << sumDouble(floats) << "\n";
cout << setw(21) << "Naive: " << sumNaive(floats) << "\n";
cout << setw(21) << "Normalized: " << sumNormalized(floats) << "\n";
cout << setw(21) << "Strictly normalized: " << sumStrictlyNormalized(floats) << "\n";
cout << setw(21) << "Precise sum: " << preciseSum(floats) << "\n";
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment