Last active
July 2, 2021 11:07
-
-
Save conqp/99bdc96633da390ce23af92b155c8dd3 to your computer and use it in GitHub Desktop.
Precisely sum up floats while avoiding rising precision loss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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