After review

This commit is contained in:
dkavolis
2020-11-02 13:28:13 +00:00
committed by Mateusz Pusz
parent 23dd21465d
commit 1ef2bf9f28
5 changed files with 76 additions and 31 deletions

View File

@@ -23,40 +23,41 @@
#pragma once #pragma once
#include <gsl/gsl_assert> #include <gsl/gsl_assert>
#include <units/bits/math_concepts.h>
#include <units/bits/pow.h> #include <units/bits/pow.h>
#include <units/bits/ratio_maths.h> #include <units/bits/ratio_maths.h>
namespace units::detail { namespace units::detail {
struct decimal_fp { struct decimal_fp {
double significand; double significant;
std::intmax_t mantissa; std::intmax_t exponent;
}; };
[[nodiscard]] constexpr decimal_fp to_decimal(double v) noexcept [[nodiscard]] constexpr decimal_fp to_decimal(double v) noexcept
{ {
if (v == 0) { if (v == 0) {
return {.significand = 0.0, .mantissa = 0}; return {.significant = 0.0, .exponent = 0};
} }
double significand = abs(v); double significant = abs(v);
std::intmax_t mantissa = 0; std::intmax_t exponent = 0;
while (significand < 1) { while (significant < 1) {
significand *= 10.0; significant *= 10.0;
--mantissa; --exponent;
} }
while (significand >= 10) { while (significant >= 10) {
significand /= 10.0; significant /= 10.0;
++mantissa; ++exponent;
} }
if (v < 0) { if (v < 0) {
significand = -significand; significant = -significant;
} }
return {.significand = significand, .mantissa = mantissa}; return {.significant = significant, .exponent = exponent};
} }
/* approximate natural log as https://math.stackexchange.com/a/977836 /* approximate natural log as https://math.stackexchange.com/a/977836
@@ -66,8 +67,8 @@ struct decimal_fp {
{ {
Expects(v > 0); Expects(v > 0);
// lookup table to speed up convergence for all significand values // lookup table to speed up convergence for all significant values
// significand values of 7 and greater benefit mostly as they now converge in 5 terms compared to O(10)-O(100) // significant values of 7 and greater benefit mostly as they now converge in 5 terms compared to O(10)-O(100)
// required without the table // required without the table
// //
// using python: // using python:
@@ -177,14 +178,14 @@ struct decimal_fp {
}; };
decimal_fp x = to_decimal(v); decimal_fp x = to_decimal(v);
// dividing the significand by nearest lower value in [1.0, 1.1, 1.2, ..., 9.9] will greatly improve convergence // dividing the significant by nearest lower value in [1.0, 1.1, 1.2, ..., 9.9] will greatly improve convergence
x.significand *= 10; x.significant *= 10;
const auto isignificand = static_cast<std::size_t>(x.significand); const auto isignificant = static_cast<std::size_t>(x.significant);
x.significand /= static_cast<double>(isignificand); x.significant /= static_cast<double>(isignificant);
const double result = static_cast<double>(x.mantissa - 1) * log_table[9] + log_table[isignificand - 1]; const double result = static_cast<double>(x.exponent - 1) * log_table[9] + log_table[isignificant - 1];
// 1.0 <= significand < 1.1 converges rapidly // 1.0 <= significant < 1.1 converges rapidly
const double y = (x.significand - 1) / (x.significand + 1); const double y = (x.significant - 1) / (x.significant + 1);
const double y_squared = y * y; const double y_squared = y * y;
double sum = 0; double sum = 0;
// 5 terms are needed for convergence to machine precision in the worst case scenario // 5 terms are needed for convergence to machine precision in the worst case scenario
@@ -201,7 +202,8 @@ struct decimal_fp {
larger Factor values improve convergence for all values but reduce the precision larger Factor values improve convergence for all values but reduce the precision
*/ */
template<std::size_t N, std::intmax_t Factor = 256> template<std::size_t N, std::intmax_t Factor = 256>
[[nodiscard]] constexpr double constexpr_exp(double v) noexcept requires requires { Factor > 0; } requires gt_zero<Factor>
[[nodiscard]] constexpr double constexpr_exp(double v) noexcept
{ {
if constexpr (N == 0) { if constexpr (N == 0) {
return 1.0; return 1.0;

View File

@@ -0,0 +1,35 @@
// The MIT License (MIT)
//
// Copyright (c) 2018 Mateusz Pusz
//
// 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, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#pragma once
#include <cstdint>
namespace units::detail {
template<std::intmax_t N>
concept gt_zero = (N > 0);
template<std::intmax_t N>
concept non_zero = (N != 0);
} // namespace units::detail

View File

@@ -24,6 +24,7 @@
#include <gsl/gsl_assert> #include <gsl/gsl_assert>
#include <units/bits/constexpr_math.h> #include <units/bits/constexpr_math.h>
#include <units/bits/math_concepts.h>
#include <units/bits/pow.h> #include <units/bits/pow.h>
#include <units/bits/ratio_maths.h> #include <units/bits/ratio_maths.h>
#include <cmath> #include <cmath>
@@ -31,7 +32,8 @@
namespace units::detail { namespace units::detail {
template<std::intmax_t N, typename F> template<std::intmax_t N, typename F>
[[nodiscard]] constexpr std::intmax_t iroot_impl(std::intmax_t v, F const& pow_function) noexcept requires requires { N > 0; } requires gt_zero<N>
[[nodiscard]] constexpr std::intmax_t iroot_impl(std::intmax_t v, F const& pow_function) noexcept
{ {
if constexpr (N == 1) { if constexpr (N == 1) {
return v; return v;
@@ -61,13 +63,15 @@ template<std::intmax_t N, typename F>
// Factor = 32 needs quite a few more terms to converge // Factor = 32 needs quite a few more terms to converge
// https://godbolt.org/z/odWq1o // https://godbolt.org/z/odWq1o
template<std::intmax_t N, std::size_t ExpOrder = 12, std::intmax_t Factor = 64> template<std::intmax_t N, std::size_t ExpOrder = 12, std::intmax_t Factor = 64>
[[nodiscard]] constexpr std::intmax_t iroot_compile(std::intmax_t v) noexcept requires requires { N > 0; } requires gt_zero<N>
[[nodiscard]] constexpr std::intmax_t iroot_compile(std::intmax_t v) noexcept
{ {
return iroot_impl<N>(v, [](double x, double exponent) { return constexpr_pow<ExpOrder, Factor>(x, exponent); }); return iroot_impl<N>(v, [](double x, double exponent) { return constexpr_pow<ExpOrder, Factor>(x, exponent); });
} }
template<std::intmax_t N> template<std::intmax_t N>
[[nodiscard]] std::intmax_t iroot_runtime(std::intmax_t v) noexcept requires requires { N > 0; } requires gt_zero<N>
[[nodiscard]] std::intmax_t iroot_runtime(std::intmax_t v) noexcept
{ {
return iroot_impl<N>(v, [](double x, double exponent) { return iroot_impl<N>(v, [](double x, double exponent) {
if constexpr (N == 2) { if constexpr (N == 2) {
@@ -81,7 +85,8 @@ template<std::intmax_t N>
} }
template<std::intmax_t N> template<std::intmax_t N>
[[nodiscard]] constexpr std::intmax_t iroot(std::intmax_t v) noexcept requires requires { N > 0; } requires gt_zero<N>
[[nodiscard]] constexpr std::intmax_t iroot(std::intmax_t v) noexcept
{ {
// compile time version is much slower, use faster version at runtime // compile time version is much slower, use faster version at runtime
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {

View File

@@ -40,8 +40,8 @@ namespace units {
* @return Quantity The result of computation * @return Quantity The result of computation
*/ */
template<std::intmax_t Num, std::intmax_t Den = 1, Quantity Q> template<std::intmax_t Num, std::intmax_t Den = 1, Quantity Q>
[[nodiscard]] inline auto pow(const Q& q) noexcept requires detail::non_zero<Den>
requires requires { std::pow(q.count(), 1.0); Den != 0; } [[nodiscard]] inline auto pow(const Q& q) noexcept requires requires { std::pow(q.count(), 1.0); }
{ {
using rep = TYPENAME Q::rep; using rep = TYPENAME Q::rep;
if constexpr (Num == 0) { if constexpr (Num == 0) {

View File

@@ -23,6 +23,7 @@
#pragma once #pragma once
#include <units/bits/external/hacks.h> #include <units/bits/external/hacks.h>
#include <units/bits/math_concepts.h>
#include <units/bits/ratio_maths.h> #include <units/bits/ratio_maths.h>
#include <units/bits/pow.h> #include <units/bits/pow.h>
#include <units/bits/root.h> #include <units/bits/root.h>
@@ -107,7 +108,8 @@ namespace detail {
} }
template<std::intmax_t N> template<std::intmax_t N>
[[nodiscard]] constexpr ratio root(const ratio& r) requires requires { N > 0; } requires gt_zero<N>
[[nodiscard]] constexpr ratio root(const ratio& r)
{ {
if constexpr (N == 1) { if constexpr (N == 1) {
return r; return r;
@@ -124,7 +126,8 @@ template<std::intmax_t N>
} // namespace detail } // namespace detail
template<std::intmax_t Num, std::intmax_t Den = 1> template<std::intmax_t Num, std::intmax_t Den = 1>
[[nodiscard]] constexpr ratio pow(const ratio& r) requires requires { Den != 0; } requires detail::non_zero<Den>
[[nodiscard]] constexpr ratio pow(const ratio& r)
{ {
if constexpr (Num == 0) { if constexpr (Num == 0) {
return ratio(1); return ratio(1);