From 6986e9c09fa16967f49d1d1c11aa181cb219d94f Mon Sep 17 00:00:00 2001 From: Xavier Valls Date: Thu, 6 Apr 2017 15:08:31 +0200 Subject: [PATCH 1/3] Extend use cases to iterables Remove dependencies with vector and add some documentation --- math/mathcore/inc/Math/Util.h | 45 +++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/math/mathcore/inc/Math/Util.h b/math/mathcore/inc/Math/Util.h index 48d230880ca34..9df8a2fd8c000 100644 --- a/math/mathcore/inc/Math/Util.h +++ b/math/mathcore/inc/Math/Util.h @@ -18,7 +18,6 @@ #include #include -#include // for defining unused variables in the interfaces // and have still them in the documentation @@ -66,35 +65,57 @@ namespace ROOT { } // end namespace Util + ///\class KahanSum + /// The Kahan compensate summation algorithm significantly reduces the numerical error in the total obtained + /// by adding a sequence of finite precision floating point numbers. + /// This is done by keeping a separate running compensation (a variable to accumulate small errors).\n + /// + /// The intial values of the result and the correction are set to the default value of the type it hass been instantiated with.\n + /// ####Examples: + /// ~~~{.cpp} + /// std::vector numbers = {0.01, 0.001, 0.0001, 0.000001, 0.00000000001}; + /// ROOT::Math::KahanSum k; + /// k.Add(numbers); + /// ~~~ + /// ~~~{.cpp} + /// auto result = ROOT::Math::KahanSum::Accumulate(numbers); + /// ~~~ template class KahanSum { public: + /// Single element accumulated addition. void Add(const T &x) { - auto y = x - correction; - auto t = sum + y; - correction = (t - sum) - y; - sum = t; + auto y = x - fCorrection; + auto t = fSum + y; + fCorrection = (t - fSum) - y; + fSum = t; } - void Add(const std::vector &elements) + /// Iterate over an iterable container of values and accumulate on the exising result + template + void Add(const Container &elements) { + static_assert(!std::is_same::value, "argument is not a container of the same type as the KahanSum class"); for (auto e : elements) this->Add(e); } - static T Accumulate(const std::vector &elements) + /// Iterate over an iterable and return the result of its accumulation + template + static T Accumulate(const Container &elements) { - + static_assert(!std::is_same::value, "argument is not a container of the same type as the KahanSum class"); KahanSum init; init.Add(elements); - return init.sum; + return init.fSum; } - T Result() { return sum; } + /// Return the result + T Result() { return fSum; } private: - T sum{}; - T correction{}; + T fSum{}; + T fCorrection{}; }; } // end namespace Math From 7c92d5483618c84f09cf5acef202790492f23fdc Mon Sep 17 00:00:00 2001 From: Xavier Valls Date: Thu, 6 Apr 2017 17:43:04 +0200 Subject: [PATCH 2/3] Change interface to accept raw pointers as iterators --- math/mathcore/inc/Math/Util.h | 22 +++++++++++----------- math/mathcore/test/testKahan.cxx | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/math/mathcore/inc/Math/Util.h b/math/mathcore/inc/Math/Util.h index 9df8a2fd8c000..5a64e3f251ded 100644 --- a/math/mathcore/inc/Math/Util.h +++ b/math/mathcore/inc/Math/Util.h @@ -92,21 +92,21 @@ namespace ROOT { fSum = t; } - /// Iterate over an iterable container of values and accumulate on the exising result - template - void Add(const Container &elements) - { - static_assert(!std::is_same::value, "argument is not a container of the same type as the KahanSum class"); - for (auto e : elements) this->Add(e); + /// Iterate over a datastructure referenced by a pointer and accumulate on the exising result + template + void Add(const Iterator begin, const Iterator end) + { + static_assert(!std::is_same::value, "Iterator points to an element of the different type than the KahanSum class"); + for( auto it = begin; it!=end; it++ ) this->Add(*it); } - /// Iterate over an iterable and return the result of its accumulation - template - static T Accumulate(const Container &elements) + /// Iterate over a datastructure referenced by a pointer and return the result of its accumulation + template + static T Accumulate(const Iterator begin, const Iterator end) { - static_assert(!std::is_same::value, "argument is not a container of the same type as the KahanSum class"); + static_assert(!std::is_same::value, "Iterator points to an element of the different type than the KahanSum class"); KahanSum init; - init.Add(elements); + init.Add(begin, end); return init.fSum; } diff --git a/math/mathcore/test/testKahan.cxx b/math/mathcore/test/testKahan.cxx index a2f51ffdb8e69..7cc95f6f8f7a8 100644 --- a/math/mathcore/test/testKahan.cxx +++ b/math/mathcore/test/testKahan.cxx @@ -5,8 +5,8 @@ int KahanTest() { std::vector numbers = {0.01, 0.001, 0.0001, 0.000001, 0.00000000001}; ROOT::Math::KahanSum k; - k.Add(numbers); - auto result = ROOT::Math::KahanSum::Accumulate(numbers); + k.Add(numbers.begin(), numbers.end()); + auto result = ROOT::Math::KahanSum::Accumulate(numbers.begin(), numbers.end()); if (k.Result() != result) return 1; return 0; } From 020dcdb860a80cb8045332976c795c2a16b084c4 Mon Sep 17 00:00:00 2001 From: Xavier Valls Date: Thu, 6 Apr 2017 17:54:01 +0200 Subject: [PATCH 3/3] Add option to provide an initial value in KahanSum --- math/mathcore/inc/Math/Util.h | 27 +++++++++++++++++---------- math/mathcore/test/testKahan.cxx | 8 ++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/math/mathcore/inc/Math/Util.h b/math/mathcore/inc/Math/Util.h index 5a64e3f251ded..77dc0f6ab8560 100644 --- a/math/mathcore/inc/Math/Util.h +++ b/math/mathcore/inc/Math/Util.h @@ -70,7 +70,8 @@ namespace ROOT { /// by adding a sequence of finite precision floating point numbers. /// This is done by keeping a separate running compensation (a variable to accumulate small errors).\n /// - /// The intial values of the result and the correction are set to the default value of the type it hass been instantiated with.\n + /// The intial values of the result and the correction are set to the default value of the type it hass been + /// instantiated with.\n /// ####Examples: /// ~~~{.cpp} /// std::vector numbers = {0.01, 0.001, 0.0001, 0.000001, 0.00000000001}; @@ -83,6 +84,9 @@ namespace ROOT { template class KahanSum { public: + /// Constructor accepting a initial value for the summation as parameter + KahanSum(const T &initialValue = T{}) : fSum(initialValue) {} + /// Single element accumulated addition. void Add(const T &x) { @@ -93,19 +97,22 @@ namespace ROOT { } /// Iterate over a datastructure referenced by a pointer and accumulate on the exising result - template + template void Add(const Iterator begin, const Iterator end) - { - static_assert(!std::is_same::value, "Iterator points to an element of the different type than the KahanSum class"); - for( auto it = begin; it!=end; it++ ) this->Add(*it); + { + static_assert(!std::is_same::value, + "Iterator points to an element of the different type than the KahanSum class"); + for (auto it = begin; it != end; it++) this->Add(*it); } - /// Iterate over a datastructure referenced by a pointer and return the result of its accumulation - template - static T Accumulate(const Iterator begin, const Iterator end) + /// Iterate over a datastructure referenced by a pointer and return the result of its accumulation. + /// Can take an initial value as third parameter. + template + static T Accumulate(const Iterator begin, const Iterator end, const T &initialValue = T{}) { - static_assert(!std::is_same::value, "Iterator points to an element of the different type than the KahanSum class"); - KahanSum init; + static_assert(!std::is_same::value, + "Iterator points to an element of the different type than the KahanSum class"); + KahanSum init(initialValue); init.Add(begin, end); return init.fSum; } diff --git a/math/mathcore/test/testKahan.cxx b/math/mathcore/test/testKahan.cxx index 7cc95f6f8f7a8..3521954b24da3 100644 --- a/math/mathcore/test/testKahan.cxx +++ b/math/mathcore/test/testKahan.cxx @@ -8,6 +8,14 @@ int KahanTest() k.Add(numbers.begin(), numbers.end()); auto result = ROOT::Math::KahanSum::Accumulate(numbers.begin(), numbers.end()); if (k.Result() != result) return 1; + + ROOT::Math::KahanSum k2; + ROOT::Math::KahanSum k3(1); + k2.Add(1); + k2.Add(numbers.begin(), numbers.end()); + k3.Add(numbers.begin(), numbers.end()); + if (k2.Result() != k3.Result()) return 2; + return 0; }