// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE // // @license: http://www.opensource.org/licenses/mit-license.php // @author: see AUTHORS file #pragma once #include #include #include namespace xlnt { namespace detail { /// /// Takes in any number and outputs a string form of that number which will /// serialise and deserialise without loss of precision /// template std::string serialize_number_to_string(Number num) { // more digits and excel won't match constexpr int Excel_Digit_Precision = 15; //sf std::stringstream ss; ss.precision(Excel_Digit_Precision); ss << num; return ss.str(); } /// /// constexpr abs /// template constexpr Number abs(Number val) { if (val < Number{0}) { return -val; } return val; }; /// /// constexpr max /// template constexpr Number max(Number lval, Number rval) { return (lval < rval) ? rval : lval; } /// /// constexpr min /// template constexpr Number min(Number lval, Number rval) { return (lval < rval) ? lval : rval; } /// /// Floating point equality requires a bit of fuzzing due to the imprecise nature of fp calculation /// References: /// - Several blogs/articles were referenced with the following being the most useful /// -- https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ /// -- http://realtimecollisiondetection.net/blog/?p=89 /// - Testing Frameworks {Catch2, Boost, Google}, primarily for selecting the default scale factor /// -- None of these even remotely agree /// template // parameter types (deduced) bool float_equals(const LNumber &lhs, const RNumber &rhs, int epsilon_scale = 20) // scale the "fuzzy" equality. Higher value gives a more tolerant comparison { static_assert(std::is_floating_point::value || std::is_floating_point::value, "Using this function with two integers is just wasting time. Use =="); static_assert(std::is_floating_point::value, "Cannot extract epsilon from a number that isn't a floating point type"); // NANs always compare false with themselves if (std::isnan(lhs) || std::isnan(rhs)) { return false; } // a type that lhs and rhs can agree on using common_t = typename std::common_type::type; // epsilon type defaults to float because even if both args are a higher precision type // either or both could have been promoted by prior operations // if a higher precision is required, the template type can be changed constexpr common_t epsilon = std::numeric_limits::epsilon(); // the "epsilon" then needs to be scaled into the comparison range // epsilon for numeric_limits is valid when abs(x) <1.0, scaling only needs to be upwards // in particular, this prevents a lhs of 0 from requiring an exact comparison // additionally, a scale factor is applied. common_t scaled_fuzz = epsilon_scale * epsilon * max(xlnt::detail::abs(lhs), common_t{1}); return ((lhs + scaled_fuzz) >= rhs) && ((rhs + scaled_fuzz) >= lhs); } } // namespace detail } // namespace xlnt