Merge branch 'master' into feature/faster-CI

This commit is contained in:
Yves Delley
2024-11-12 18:20:20 +01:00
committed by GitHub
21 changed files with 2426 additions and 1754 deletions

View File

@ -386,7 +386,7 @@ the same kind.
We can try to do this by ourselves, but it is tough. Probably no one, for sure we are
not, is an expert in all the fields of ISO/IEC 80000 applicability.
We need the help of subject matter experts who will help us build those trees for their domains
and then verify that everything works as expected.
@ -401,8 +401,8 @@ Some quantities are more complicated than others. For example, _power_ has:
- var (e.g., _reactive power_),
- complex quantities expressed in VA (volt-ampere) (e.g., _complex power_).
How should we model this? Maybe those should be two independent trees of quantities, each having
a different unit?
How should we model this? Maybe those should be two or three independent trees of quantities, each
having its own unit?
```mermaid
flowchart TD
@ -411,14 +411,50 @@ flowchart TD
power --- electromagnetism_power["<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>"]
power --- active_power["<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>"]
apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i><br>[VA]"]
nonactive_power["<b>nonactive_power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[VA]"]
nonactive_power --- reactive_power["<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]"]
complex_power["<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i><br>[VA]"]
complex_power --- apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i>"]
```
This will mean that we will not be able to add or compare _active power_, _reactive power_, and
_apparent power_, which probably makes a lot of sense. However, it also means that the following
will fail to compile:
```cpp
quantity apparent = isq::apparent_power(100 * VA);
quantity active = isq::active_power(60 * W);
quantity<isq::nonactive_power[VA]> q = sqrt(pow<2>(apparent) - pow<2>(active)); // Compile-time error
```
Also the following will not work:
```cpp
quantity active = isq::active_power(60 * W);
quantity reactive = isq::reactive_power(40 * var);
quantity<isq::apparent_power[VA]> q = sqrt(pow<2>(active) + pow<2>(reactive)); // Compile-time error
```
If we want the above to work maybe we need to implement the tree as follows?
```mermaid
flowchart TD
power["<b>power</b><br><i>(mass * length<sup>2</sup> / time<sup>3</sup>)</i><br>[W]"]
power --- mechanical_power["<b>mechanical_power</b><br><i>(scalar_product(force, velocity))</i>"]
power --- electromagnetism_power["<b>electromagnetism_power</b> | <b>instantaneous_power</b><br><i>(instantaneous_voltage * instantaneous_electric_current)</i>"]
power --- apparent_power["<b>apparent_power</b><br><i>(voltage * electric_current)<br>(mod(complex_power))</i><br>[VA]"]
apparent_power --- active_power["<b>active_power</b><br><i>(1 / period * instantaneous_power * time)<br>(re(complex_power))</i>"]
apparent_power --- nonactive_power["<b>nonactive_power</b><br><i>(sqrt(apparent_power<sup>2</sup> - active_power<sup>2</sup>))</i><br>"]
nonactive_power --- reactive_power["<b>reactive_power</b><br><i>(im(complex_power))</i><br>[var]"]
apparent_power --- complex_power["<b>complex_power</b><br>{complex}<br><i>(voltage_phasor * electric_current_phasor)<br>(active_power + j * reactive_power)</i>"]
```
This will mean that we will not be able to add or compare _active power_ with _apparent power_,
which probably makes a lot of sense. Again, ISQ does not provide a direct answer here.
However, the above allows direct addition and comparison of _active power_ and _nonactive power_,
and also will not complain if someone will try to use watt (W) as a unit of _apparent power_ or
_reactive power_.
Again, ISQ does not provide a direct answer here.
## More base quantities?
@ -490,5 +526,5 @@ a daily basis.
I hope you enjoyed following this series and learned more about the International System
of Quantities. Please try it out in your domain and share feedback with us. We always love to
hear about the projects in which our library is being used and about use cases it helps
hear about the projects in which our library is being used and about use cases it helps
address.

View File

@ -16,19 +16,11 @@ work in practice.
--8<-- "example/si_constants.cpp:28:40"
```
As always, we start with the inclusion of all the needed header files. After that, for
the simplicity of this example, we
[hack the character of quantities](../framework_basics/character_of_a_quantity.md#hacking-the-character)
to be able to express vector quantities with simple scalar types.
```cpp title="si_constants.cpp" linenums="14"
--8<-- "example/si_constants.cpp:42:44"
```
As always, we start with the inclusion of all the needed header files.
The main part of the example prints all of the SI-defining constants:
```cpp title="si_constants.cpp" linenums="17"
--8<-- "example/si_constants.cpp:45:"
```cpp title="si_constants.cpp" linenums="14"
--8<-- "example/si_constants.cpp:42:"
```
While analyzing the output of this program (provided below), we can easily notice that a direct

View File

@ -129,8 +129,8 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
template<typename T, typename Validator, typename Char>
struct MP_UNITS_STD_FMT::formatter<validated_type<T, Validator>, Char> : formatter<T, Char> {
template<typename FormatContext>
auto format(const validated_type<T, Validator>& v, FormatContext& ctx) const -> decltype(ctx.out())
auto format(const validated_type<T, Validator>& val, FormatContext& ctx) const -> decltype(ctx.out())
{
return formatter<T, Char>::format(v.value(), ctx);
return formatter<T, Char>::format(val.value(), ctx);
}
};

View File

@ -39,10 +39,6 @@ import mp_units;
#include <mp-units/systems/si.h>
#endif
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
int main()
{
using namespace mp_units;

View File

@ -91,6 +91,7 @@ if(NOT ${projectPrefix}API_FREESTANDING)
include/mp-units/bits/fmt.h
include/mp-units/bits/requires_hosted.h
include/mp-units/ext/format.h
include/mp-units/cartesian_vector.h
include/mp-units/complex.h
include/mp-units/format.h
include/mp-units/math.h

View File

@ -0,0 +1,230 @@
// 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 <mp-units/bits/requires_hosted.h>
//
#include <mp-units/bits/module_macros.h>
#include <mp-units/framework/customization_points.h>
#if MP_UNITS_HOSTED
#include <mp-units/bits/fmt.h>
#endif
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#ifdef MP_UNITS_IMPORT_STD
import std;
#else
#include <cmath>
#include <cstddef>
#include <type_traits>
#if MP_UNITS_HOSTED
#include <ostream>
#endif
#endif
#endif
namespace mp_units {
MP_UNITS_EXPORT template<typename T = double>
class cartesian_vector {
public:
// public members required to satisfy structural type requirements :-(
T _coordinates_[3];
using value_type = T;
cartesian_vector() = default;
cartesian_vector(const cartesian_vector&) = default;
cartesian_vector(cartesian_vector&&) = default;
cartesian_vector& operator=(const cartesian_vector&) = default;
cartesian_vector& operator=(cartesian_vector&&) = default;
template<std::convertible_to<T> Arg1, std::convertible_to<T>... Args>
constexpr cartesian_vector(Arg1&& arg1, Args&&... args) :
_coordinates_(std::forward<Arg1>(arg1), std::forward<Args>(args)...)
{
}
template<std::convertible_to<T> U>
constexpr cartesian_vector(const cartesian_vector<U>& other) : _coordinates_{other[0], other[1], other[2]}
{
}
template<std::convertible_to<T> U>
constexpr cartesian_vector(cartesian_vector<U>&& other) :
_coordinates_{std::move(other[0]), std::move(other[1]), std::move(other[2])}
{
}
template<std::convertible_to<T> U>
constexpr cartesian_vector& operator=(const cartesian_vector<U>& other)
{
_coordinates_[0] = other[0];
_coordinates_[1] = other[1];
_coordinates_[2] = other[2];
return *this;
}
template<std::convertible_to<T> U>
constexpr cartesian_vector& operator=(cartesian_vector<U>&& other)
{
_coordinates_[0] = std::move(other[0]);
_coordinates_[1] = std::move(other[1]);
_coordinates_[2] = std::move(other[2]);
return *this;
}
[[nodiscard]] constexpr T magnitude() const
requires treat_as_floating_point<T>
{
return std::hypot(_coordinates_[0], _coordinates_[1], _coordinates_[2]);
}
[[nodiscard]] constexpr cartesian_vector unit() const
requires treat_as_floating_point<T>
{
return *this / magnitude();
}
[[nodiscard]] constexpr T& operator[](std::size_t i) { return _coordinates_[i]; }
[[nodiscard]] constexpr const T& operator[](std::size_t i) const { return _coordinates_[i]; }
[[nodiscard]] constexpr cartesian_vector operator+() const { return *this; }
[[nodiscard]] constexpr cartesian_vector operator-() const
{
return {-_coordinates_[0], -_coordinates_[1], -_coordinates_[2]};
}
template<std::same_as<T> U, typename V>
requires requires(U u, V v) { u + v; }
[[nodiscard]] friend constexpr auto operator+(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] + rhs._coordinates_[0],
lhs._coordinates_[1] + rhs._coordinates_[1],
lhs._coordinates_[2] + rhs._coordinates_[2]};
}
template<std::same_as<T> U, typename V>
requires requires(U u, V v) { u - v; }
[[nodiscard]] friend constexpr auto operator-(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] - rhs._coordinates_[0],
lhs._coordinates_[1] - rhs._coordinates_[1],
lhs._coordinates_[2] - rhs._coordinates_[2]};
}
template<std::same_as<T> U>
requires requires(U u, T t) { u* t; }
[[nodiscard]] friend constexpr auto operator*(const cartesian_vector<U>& lhs, const T& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] * rhs, lhs._coordinates_[1] * rhs,
lhs._coordinates_[2] * rhs};
}
template<std::same_as<T> U>
requires requires(T t, U u) { t* u; }
[[nodiscard]] friend constexpr auto operator*(const T& lhs, const cartesian_vector<U>& rhs)
{
return rhs * lhs;
}
template<std::same_as<T> U>
requires requires(U u, T t) { u / t; }
[[nodiscard]] friend constexpr auto operator/(const cartesian_vector<U>& lhs, const T& rhs)
{
return ::mp_units::cartesian_vector{lhs._coordinates_[0] / rhs, lhs._coordinates_[1] / rhs,
lhs._coordinates_[2] / rhs};
}
template<std::same_as<T> U, std::equality_comparable_with<U> V>
[[nodiscard]] friend constexpr bool operator==(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
{
return lhs._coordinates_[0] == rhs._coordinates_[0] && lhs._coordinates_[1] == rhs._coordinates_[1] &&
lhs._coordinates_[2] == rhs._coordinates_[2];
}
[[nodiscard]] friend constexpr T norm(const cartesian_vector& vec)
requires treat_as_floating_point<T>
{
return vec.magnitude();
}
[[nodiscard]] friend constexpr cartesian_vector unit_vector(const cartesian_vector& vec)
requires treat_as_floating_point<T>
{
return vec.unit();
}
template<std::same_as<T> U, typename V>
requires requires(U u, V v, decltype(u * v) t) {
u* v;
t + t;
}
[[nodiscard]] friend constexpr auto scalar_product(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
{
return lhs._coordinates_[0] * rhs._coordinates_[0] + lhs._coordinates_[1] * rhs._coordinates_[1] +
lhs._coordinates_[2] * rhs._coordinates_[2];
}
template<std::same_as<T> U, typename V>
requires requires(U u, V v, decltype(u * v) t) {
u* v;
t - t;
}
[[nodiscard]] friend constexpr auto vector_product(const cartesian_vector<U>& lhs, const cartesian_vector<V>& rhs)
{
return ::mp_units::cartesian_vector{
lhs._coordinates_[1] * rhs._coordinates_[2] - lhs._coordinates_[2] * rhs._coordinates_[1],
lhs._coordinates_[2] * rhs._coordinates_[0] - lhs._coordinates_[0] * rhs._coordinates_[2],
lhs._coordinates_[0] * rhs._coordinates_[1] - lhs._coordinates_[1] * rhs._coordinates_[0]};
}
#if MP_UNITS_HOSTED
friend constexpr std::ostream& operator<<(std::ostream& os, const cartesian_vector& vec)
{
return os << '[' << vec[0] << ", " << vec[1] << ", " << vec[2] << ']';
}
#endif
};
template<typename Arg, typename... Args>
requires(sizeof...(Args) <= 2) && requires { typename std::common_type_t<Arg, Args...>; }
cartesian_vector(Arg, Args...) -> cartesian_vector<std::common_type_t<Arg, Args...>>;
template<class T>
constexpr bool is_vector<cartesian_vector<T>> = true;
} // namespace mp_units
#if MP_UNITS_HOSTED
// TODO use parse and use formatter for the underlying type
template<typename T, typename Char>
struct MP_UNITS_STD_FMT::formatter<mp_units::cartesian_vector<T>, Char> :
formatter<std::basic_string_view<Char>, Char> {
template<typename FormatContext>
auto format(const mp_units::cartesian_vector<T>& vec, FormatContext& ctx) const
{
return format_to(ctx.out(), "[{}, {}, {}]", vec[0], vec[1], vec[2]);
}
};
#endif

View File

@ -28,6 +28,7 @@
#include <mp-units/framework.h>
#if MP_UNITS_HOSTED
#include <mp-units/cartesian_vector.h>
#include <mp-units/complex.h>
#include <mp-units/format.h>
#include <mp-units/math.h>

View File

@ -90,16 +90,16 @@ public:
constexpr T* data() noexcept { return data_; }
constexpr const T* data() const noexcept { return data_; }
constexpr reference push_back(const T& v)
constexpr reference push_back(const T& val)
requires std::constructible_from<T, const T&>
{
return emplace_back(v);
return emplace_back(val);
}
constexpr reference push_back(T&& v)
constexpr reference push_back(T&& val)
requires std::constructible_from<T, T&&>
{
return emplace_back(std::forward<T&&>(v));
return emplace_back(std::forward<T&&>(val));
}
template<typename... Args>

View File

@ -174,21 +174,21 @@ public:
template<typename FwdValue, Reference R2>
requires detail::SameValueAs<R2{}, R, std::remove_cvref_t<FwdValue>, Rep>
constexpr quantity(FwdValue&& v, R2) : numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
constexpr quantity(FwdValue&& val, R2) : numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(val))
{
}
template<typename FwdValue, Reference R2, typename Value = std::remove_cvref_t<FwdValue>>
requires(!detail::SameValueAs<R2{}, R, Value, Rep>) &&
detail::QuantityConvertibleTo<quantity<R2{}, Value>, quantity>
constexpr quantity(FwdValue&& v, R2) : quantity(quantity<R2{}, Value>{std::forward<FwdValue>(v), R2{}})
constexpr quantity(FwdValue&& val, R2) : quantity(quantity<R2{}, Value>{std::forward<FwdValue>(val), R2{}})
{
}
template<detail::ValuePreservingTo<Rep> FwdValue>
requires(unit == ::mp_units::one)
constexpr explicit(false) quantity(FwdValue&& v) :
numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(v))
constexpr explicit(false) quantity(FwdValue&& val) :
numerical_value_is_an_implementation_detail_(std::forward<FwdValue>(val))
{
}
@ -214,9 +214,9 @@ public:
template<detail::ValuePreservingTo<Rep> FwdValue>
requires(unit == ::mp_units::one)
constexpr quantity& operator=(FwdValue&& v)
constexpr quantity& operator=(FwdValue&& val)
{
numerical_value_is_an_implementation_detail_ = std::forward<FwdValue>(v);
numerical_value_is_an_implementation_detail_ = std::forward<FwdValue>(val);
return *this;
}
@ -422,11 +422,11 @@ public:
requires(!Quantity<Value>) && requires(rep a, Value b) {
{ a *= b } -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& v)
friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& val)
{
// TODO use *= when compiler bug is resolved:
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ * v;
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ * val;
return std::forward<Q>(lhs);
}
@ -444,12 +444,12 @@ public:
requires(!Quantity<Value>) && requires(rep a, Value b) {
{ a /= b } -> std::same_as<rep&>;
}
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v)
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& val)
{
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
MP_UNITS_EXPECTS_DEBUG(val != quantity_values<Value>::zero());
// TODO use /= when compiler bug is resolved:
// https://developercommunity.visualstudio.com/t/Discrepancy-in-Behavior-of-operator-an/10732445
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / v;
lhs.numerical_value_is_an_implementation_detail_ = lhs.numerical_value_is_an_implementation_detail_ / val;
return std::forward<Q>(lhs);
}
@ -551,17 +551,17 @@ public:
template<std::derived_from<quantity> Q, typename Value>
requires(!Quantity<Value>) &&
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, Rep, const Value&>
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& q, const Value& v)
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Q& q, const Value& val)
{
return ::mp_units::quantity{q.numerical_value_ref_in(unit) * v, R};
return ::mp_units::quantity{q.numerical_value_ref_in(unit) * val, R};
}
template<typename Value, std::derived_from<quantity> Q>
requires(!Quantity<Value>) &&
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::multiplies<>, const Value&, Rep>
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& v, const Q& q)
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator*(const Value& val, const Q& q)
{
return ::mp_units::quantity{v * q.numerical_value_ref_in(unit), R};
return ::mp_units::quantity{val * q.numerical_value_ref_in(unit), R};
}
template<std::derived_from<quantity> Q, auto R2, typename Rep2>
@ -575,18 +575,18 @@ public:
template<std::derived_from<quantity> Q, typename Value>
requires(!Quantity<Value>) &&
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, Rep, const Value&>
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& q, const Value& v)
[[nodiscard]] friend constexpr QuantityOf<quantity_spec> auto operator/(const Q& q, const Value& val)
{
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
return ::mp_units::quantity{q.numerical_value_ref_in(unit) / v, R};
MP_UNITS_EXPECTS_DEBUG(val != quantity_values<Value>::zero());
return ::mp_units::quantity{q.numerical_value_ref_in(unit) / val, R};
}
template<typename Value, std::derived_from<quantity> Q>
requires(!Quantity<Value>) &&
(!Reference<Value>) && detail::InvokeResultOf<quantity_spec, std::divides<>, const Value&, Rep>
[[nodiscard]] friend constexpr QuantityOf<inverse(quantity_spec)> auto operator/(const Value& v, const Q& q)
[[nodiscard]] friend constexpr QuantityOf<inverse(quantity_spec)> auto operator/(const Value& val, const Q& q)
{
return ::mp_units::quantity{v / q.numerical_value_ref_in(unit), ::mp_units::one / R};
return ::mp_units::quantity{val / q.numerical_value_ref_in(unit), ::mp_units::one / R};
}
template<std::derived_from<quantity> Q, auto R2, typename Rep2>

View File

@ -118,12 +118,11 @@ concept ComplexRepresentation = Complex<T> && WeaklyRegular<T> && requires(T a,
{ a - b } -> Complex;
{ a* b } -> Complex;
{ a / b } -> Complex;
// TBD
// { re(a) } -> Scalar;
// { im(a) } -> Scalar;
// { mod(a) } -> Scalar;
// { arg(a) } -> Scalar;
// { conj(a) } -> Complex;
{ real(a) } -> Scalar;
{ imag(a) } -> Scalar;
{ abs(a) } -> Scalar;
{ arg(a) } -> Scalar;
{ conj(a) } -> Complex;
};
// TODO how to check for a complex(Scalar, Scalar) -> Complex?

View File

@ -68,8 +68,8 @@ void to_stream_impl(std::basic_ostream<CharT, Traits>& os, const quantity<R, Rep
}
template<typename CharT, class Traits, typename T>
std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>& os, const T& v)
requires requires { detail::to_stream_impl(os, v); }
std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>& os, const T& val)
requires requires { detail::to_stream_impl(os, val); }
{
if (os.width()) {
// std::setw() applies to the whole output so it has to be first put into std::string
@ -77,11 +77,11 @@ std::basic_ostream<CharT, Traits>& to_stream(std::basic_ostream<CharT, Traits>&
oss.flags(os.flags());
oss.imbue(os.getloc());
oss.precision(os.precision());
detail::to_stream_impl(oss, v);
detail::to_stream_impl(oss, val);
return os << std::move(oss).str();
}
detail::to_stream_impl(os, v);
detail::to_stream_impl(os, val);
return os;
}
@ -93,10 +93,10 @@ constexpr bool is_mp_units_stream = requires(OStream os, T v) { detail::to_strea
MP_UNITS_EXPORT_BEGIN
template<typename CharT, typename Traits, typename T>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const T& v)
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const T& val)
requires detail::is_mp_units_stream<std::basic_ostream<CharT, Traits>, T>
{
return detail::to_stream(os, v);
return detail::to_stream(os, val);
}
MP_UNITS_EXPORT_END

View File

@ -83,10 +83,10 @@ struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
return q.count();
}
[[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept(
[[nodiscard]] static constexpr T from_numerical_value(const rep& val) noexcept(
std::is_nothrow_copy_constructible_v<rep>)
{
return T(v);
return T(val);
}
};
@ -113,10 +113,10 @@ struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::durati
return tp.time_since_epoch().count();
}
[[nodiscard]] static constexpr T from_numerical_value(const rep& v) noexcept(
[[nodiscard]] static constexpr T from_numerical_value(const rep& val) noexcept(
std::is_nothrow_copy_constructible_v<rep>)
{
return T(std::chrono::duration<Rep, Period>(v));
return T(std::chrono::duration<Rep, Period>(val));
}
};

View File

@ -24,13 +24,14 @@ find_package(Catch2 3 REQUIRED)
add_executable(
unit_tests_runtime
atomic_test.cpp
cartesian_vector_test.cpp
distribution_test.cpp
fixed_string_test.cpp
fmt_test.cpp
math_test.cpp
atomic_test.cpp
truncation_test.cpp
quantity_test.cpp
truncation_test.cpp
)
if(${projectPrefix}BUILD_CXX_MODULES)
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)

View File

@ -0,0 +1,381 @@
// 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.
#include "almost_equals.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/format.h>
#ifdef MP_UNITS_IMPORT_STD
import std;
#else
#include <sstream>
#endif
#ifdef MP_UNITS_MODULES
import mp_units;
#else
#include <mp-units/cartesian_vector.h>
#endif
using namespace mp_units;
using namespace Catch::Matchers;
TEST_CASE("cartesian_vector operations", "[vector]")
{
SECTION("cartesian_vector initialization and access")
{
SECTION("one argument")
{
cartesian_vector v{1.0};
REQUIRE(v[0] == 1.0);
REQUIRE(v[1] == 0);
REQUIRE(v[2] == 0);
}
SECTION("two arguments")
{
cartesian_vector v{1.0, 2.0};
REQUIRE(v[0] == 1.0);
REQUIRE(v[1] == 2.0);
REQUIRE(v[2] == 0);
}
SECTION("all arguments")
{
cartesian_vector v{1.0, 2.0, 3.0};
REQUIRE(v[0] == 1.0);
REQUIRE(v[1] == 2.0);
REQUIRE(v[2] == 3.0);
}
}
SECTION("convertibility")
{
cartesian_vector v1{1, 2, 3};
SECTION("construction")
{
cartesian_vector v2 = v1;
REQUIRE(v2[0] == 1.0);
REQUIRE(v2[1] == 2.0);
REQUIRE(v2[2] == 3.0);
}
SECTION("assignment")
{
cartesian_vector v2{3.0, 2.0, 1.0};
v2 = v1;
REQUIRE(v2[0] == 1.0);
REQUIRE(v2[1] == 2.0);
REQUIRE(v2[2] == 3.0);
}
}
SECTION("cartesian_vector addition")
{
SECTION("double + double")
{
cartesian_vector v1{1.0, 2.0, 3.0};
cartesian_vector v2{4.0, 5.0, 6.0};
cartesian_vector result = v1 + v2;
REQUIRE(result[0] == 5.0);
REQUIRE(result[1] == 7.0);
REQUIRE(result[2] == 9.0);
}
SECTION("double + int")
{
cartesian_vector v1{1.0, 2.0, 3.0};
cartesian_vector v2{4, 5, 6};
cartesian_vector result = v1 + v2;
REQUIRE(result[0] == 5.0);
REQUIRE(result[1] == 7.0);
REQUIRE(result[2] == 9.0);
}
SECTION("int + double")
{
cartesian_vector v1{1, 2, 3};
cartesian_vector v2{4.0, 5.0, 6.0};
cartesian_vector result = v1 + v2;
REQUIRE(result[0] == 5.0);
REQUIRE(result[1] == 7.0);
REQUIRE(result[2] == 9.0);
}
SECTION("int + int")
{
cartesian_vector v1{1, 2, 3};
cartesian_vector v2{4, 5, 6};
cartesian_vector result = v1 + v2;
REQUIRE(result[0] == 5);
REQUIRE(result[1] == 7);
REQUIRE(result[2] == 9);
}
}
SECTION("cartesian_vector subtraction")
{
SECTION("double - double")
{
cartesian_vector v1{4.0, 5.0, 6.0};
cartesian_vector v2{1.0, 2.0, 3.0};
cartesian_vector result = v1 - v2;
REQUIRE(result[0] == 3.0);
REQUIRE(result[1] == 3.0);
REQUIRE(result[2] == 3.0);
}
SECTION("double - int")
{
cartesian_vector v1{4.0, 5.0, 6.0};
cartesian_vector v2{1, 2, 3};
cartesian_vector result = v1 - v2;
REQUIRE(result[0] == 3.0);
REQUIRE(result[1] == 3.0);
REQUIRE(result[2] == 3.0);
}
SECTION("int - double")
{
cartesian_vector v1{4, 5, 6};
cartesian_vector v2{1.0, 2.0, 3.0};
cartesian_vector result = v1 - v2;
REQUIRE(result[0] == 3.0);
REQUIRE(result[1] == 3.0);
REQUIRE(result[2] == 3.0);
}
SECTION("int - int")
{
cartesian_vector v1{4, 5, 6};
cartesian_vector v2{1, 2, 3};
cartesian_vector result = v1 - v2;
REQUIRE(result[0] == 3);
REQUIRE(result[1] == 3);
REQUIRE(result[2] == 3);
}
}
SECTION("cartesian_vector scalar multiplication")
{
SECTION("double * double")
{
cartesian_vector v{1.0, 2.0, 3.0};
cartesian_vector result = v * 2.0;
REQUIRE(result[0] == 2.0);
REQUIRE(result[1] == 4.0);
REQUIRE(result[2] == 6.0);
}
SECTION("double * int")
{
cartesian_vector v{1.0, 2.0, 3.0};
cartesian_vector result = v * 2;
REQUIRE(result[0] == 2.0);
REQUIRE(result[1] == 4.0);
REQUIRE(result[2] == 6.0);
}
SECTION("int * double")
{
cartesian_vector v{1, 2, 3};
cartesian_vector result = v * 2.0;
REQUIRE(result[0] == 2.0);
REQUIRE(result[1] == 4.0);
REQUIRE(result[2] == 6.0);
}
SECTION("int * int")
{
cartesian_vector v{1, 2, 3};
cartesian_vector result = v * 2;
REQUIRE(result[0] == 2);
REQUIRE(result[1] == 4);
REQUIRE(result[2] == 6);
}
}
SECTION("cartesian_vector scalar division")
{
SECTION("double / double")
{
cartesian_vector v{2.0, 4.0, 6.0};
cartesian_vector result = v / 2.0;
REQUIRE(result[0] == 1.0);
REQUIRE(result[1] == 2.0);
REQUIRE(result[2] == 3.0);
}
SECTION("double / int")
{
cartesian_vector v{2.0, 4.0, 6.0};
cartesian_vector result = v / 2;
REQUIRE(result[0] == 1.0);
REQUIRE(result[1] == 2.0);
REQUIRE(result[2] == 3.0);
}
SECTION("int / double")
{
cartesian_vector v{2, 4, 6};
cartesian_vector result = v / 2.0;
REQUIRE(result[0] == 1.0);
REQUIRE(result[1] == 2.0);
REQUIRE(result[2] == 3.0);
}
SECTION("int / int")
{
cartesian_vector v{2, 4, 6};
cartesian_vector result = v / 2;
REQUIRE(result[0] == 1);
REQUIRE(result[1] == 2);
REQUIRE(result[2] == 3);
}
}
SECTION("cartesian_vector magnitude")
{
cartesian_vector v1{3.0, 4.0, 0.0};
cartesian_vector v2{2.0, 3.0, 6.0};
REQUIRE(v1.magnitude() == 5.0);
REQUIRE(v2.magnitude() == 7.0);
}
SECTION("cartesian_vector unit vector")
{
cartesian_vector v{3.0, 4.0, 0.0};
cartesian_vector unit_v = v.unit();
REQUIRE_THAT(unit_v.magnitude(), WithinULP(1.0, 1));
}
SECTION("cartesian_vector equality")
{
cartesian_vector v1{1.0, 2.0, 3.0};
cartesian_vector v2{1, 2, 3};
cartesian_vector v3{1.1, 2.0, 3.0};
cartesian_vector v4{1.0, 2.1, 3.0};
cartesian_vector v5{1.0, 2.0, 3.1};
REQUIRE(v1 == v2);
REQUIRE(v1 != v3);
REQUIRE(v1 != v4);
REQUIRE(v1 != v5);
}
SECTION("cartesian_vector scalar product")
{
SECTION("double * double")
{
cartesian_vector v1{1.0, 2.0, 3.0};
cartesian_vector v2{4.0, 5.0, 6.0};
REQUIRE(scalar_product(v1, v2) == 32.0);
}
SECTION("double * int")
{
cartesian_vector v1{1.0, 2.0, 3.0};
cartesian_vector v2{4, 5, 6};
REQUIRE(scalar_product(v1, v2) == 32.0);
}
SECTION("int * double")
{
cartesian_vector v1{1, 2, 3};
cartesian_vector v2{4.0, 5.0, 6.0};
REQUIRE(scalar_product(v1, v2) == 32.0);
}
SECTION("int * int")
{
cartesian_vector v1{1, 2, 3};
cartesian_vector v2{4, 5, 6};
REQUIRE(scalar_product(v1, v2) == 32);
}
}
SECTION("cartesian_vector vector product")
{
SECTION("double * double")
{
cartesian_vector v1{1.0, 2.0, 3.0};
cartesian_vector v2{4.0, 5.0, 6.0};
cartesian_vector result = vector_product(v1, v2);
REQUIRE(result[0] == -3.0);
REQUIRE(result[1] == 6.0);
REQUIRE(result[2] == -3.0);
}
SECTION("double * int")
{
cartesian_vector v1{1.0, 2.0, 3.0};
cartesian_vector v2{4, 5, 6};
cartesian_vector result = vector_product(v1, v2);
REQUIRE(result[0] == -3.0);
REQUIRE(result[1] == 6.0);
REQUIRE(result[2] == -3.0);
}
SECTION("int * double")
{
cartesian_vector v1{1, 2, 3};
cartesian_vector v2{4.0, 5.0, 6.0};
cartesian_vector result = vector_product(v1, v2);
REQUIRE(result[0] == -3.0);
REQUIRE(result[1] == 6.0);
REQUIRE(result[2] == -3.0);
}
SECTION("int * int")
{
cartesian_vector v1{1, 2, 3};
cartesian_vector v2{4, 5, 6};
cartesian_vector result = vector_product(v1, v2);
REQUIRE(result[0] == -3);
REQUIRE(result[1] == 6);
REQUIRE(result[2] == -3);
}
}
}
TEST_CASE("cartesian_vector text output", "[vector][fmt][ostream]")
{
std::ostringstream os;
SECTION("integral representation")
{
cartesian_vector v{1, 2, 3};
os << v;
SECTION("iostream") { CHECK(os.str() == "[1, 2, 3]"); }
SECTION("fmt with default format {}") { CHECK(MP_UNITS_STD_FMT::format("{}", v) == os.str()); }
}
SECTION("floating-point representation")
{
cartesian_vector v{1.2, 2.3, 3.4};
os << v;
SECTION("iostream") { CHECK(os.str() == "[1.2, 2.3, 3.4]"); }
SECTION("fmt with default format {}") { CHECK(MP_UNITS_STD_FMT::format("{}", v) == os.str()); }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -38,19 +38,22 @@ import mp_units;
using namespace mp_units;
TEST_CASE("fixed_string::at", "[fixed_string]")
TEST_CASE("fixed_string operations", "[fixed_string]")
{
basic_fixed_string txt = "abc";
SECTION("in range")
SECTION("fixed_string::at")
{
CHECK(txt.at(0) == 'a');
CHECK(txt.at(1) == 'b');
CHECK(txt.at(2) == 'c');
}
SECTION("out of range")
{
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
basic_fixed_string txt = "abc";
SECTION("in range")
{
CHECK(txt.at(0) == 'a');
CHECK(txt.at(1) == 'b');
CHECK(txt.at(2) == 'c');
}
SECTION("out of range")
{
REQUIRE_THROWS_MATCHES(txt.at(3), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
REQUIRE_THROWS_MATCHES(txt.at(1024), std::out_of_range, Catch::Matchers::Message("basic_fixed_string::at"));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -43,486 +43,488 @@ using namespace mp_units::si::unit_symbols;
// classical
TEST_CASE("'pow<N>()' on quantity changes the value and the dimension accordingly", "[math][pow]")
TEST_CASE("math operations", "[math]")
{
SECTION("'pow<0>(q)' returns '1'") { CHECK(pow<0>(2 * isq::length[m]) == 1 * one); }
SECTION("'pow<1>(q)' returns 'q'") { CHECK(pow<1>(2 * isq::length[m]) == 2 * isq::length[m]); }
SECTION("'pow<2>(q)' squares both the value and a dimension")
SECTION("'pow<N>()' on quantity changes the value and the dimension accordingly")
{
CHECK(pow<2>(2 * isq::length[m]) == 4 * isq::area[m2]);
SECTION("'pow<0>(q)' returns '1'") { CHECK(pow<0>(2 * isq::length[m]) == 1 * one); }
SECTION("'pow<1>(q)' returns 'q'") { CHECK(pow<1>(2 * isq::length[m]) == 2 * isq::length[m]); }
SECTION("'pow<2>(q)' squares both the value and a dimension")
{
CHECK(pow<2>(2 * isq::length[m]) == 4 * isq::area[m2]);
}
SECTION("'pow<3>(q)' cubes both the value and a dimension")
{
CHECK(pow<3>(2 * isq::length[m]) == 8 * isq::volume[m3]);
}
}
SECTION("'pow<3>(q)' cubes both the value and a dimension")
SECTION("'sqrt()' on quantity changes the value and the dimension accordingly")
{
CHECK(pow<3>(2 * isq::length[m]) == 8 * isq::volume[m3]);
}
}
TEST_CASE("'sqrt()' on quantity changes the value and the dimension accordingly", "[math][sqrt]")
{
REQUIRE(sqrt(4 * isq::area[m2]) == 2 * isq::length[m]);
}
TEST_CASE("'cbrt()' on quantity changes the value and the dimension accordingly", "[math][cbrt]")
{
REQUIRE(cbrt(8 * isq::volume[m3]) == 2 * isq::length[m]);
}
TEST_CASE("'fma()' on quantity changes the value and the dimension accordingly", "[math][fma]")
{
REQUIRE(fma(1.0 * isq::length[m], 2.0 * one, 2.0 * isq::length[m]) == 4.0 * isq::length[m]);
REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m));
}
TEST_CASE("fmod functions", "[math][fmod]")
{
SECTION("fmod should work on the same quantities")
{
REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]);
REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]);
}
SECTION("fmod should work with different units of the same dimension")
{
REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]);
REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]);
}
}
TEST_CASE("remainder functions", "[math][remainder]")
{
SECTION("remainder should work on the same quantities")
{
REQUIRE(remainder(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
REQUIRE(remainder(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
REQUIRE(remainder(3 * isq::length[km], 2 * isq::length[km]) == -1 * isq::length[km]);
REQUIRE(remainder(4 * isq::length[km], 2.75f * isq::length[km]) == 1.25 * isq::length[km]);
}
SECTION("remainder should work with different units of the same dimension")
{
REQUIRE(remainder(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
REQUIRE(remainder(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
REQUIRE(remainder(3. * isq::length[km], 2000. * isq::length[m]) == -1000 * isq::length[m]);
REQUIRE(remainder(4 * isq::length[km], 2750 * isq::length[m]) == 1250 * isq::length[m]);
}
}
TEST_CASE("'isfinite()' accepts dimensioned arguments", "[math][isfinite]") { REQUIRE(isfinite(4.0 * isq::length[m])); }
TEST_CASE("'isinf()' accepts dimensioned arguments", "[math][isinf]") { REQUIRE(!isinf(4.0 * isq::length[m])); }
TEST_CASE("'isnan()' accepts dimensioned arguments", "[math][isnan]") { REQUIRE(!isnan(4.0 * isq::length[m])); }
TEST_CASE("'pow<Num, Den>()' on quantity changes the value and the dimension accordingly", "[math][pow]")
{
REQUIRE(pow<1, 4>(16 * isq::area[m2]) == sqrt(4 * isq::length[m]));
}
// TODO add tests for exp()
TEST_CASE("absolute functions on quantity returns the absolute value", "[math][abs][fabs]")
{
SECTION("'abs()' on a negative quantity returns the abs")
{
SECTION("integral representation") { REQUIRE(abs(-1 * isq::length[m]) == 1 * isq::length[m]); }
SECTION("floating-point representation") { REQUIRE(abs(-1. * isq::length[m]) == 1 * isq::length[m]); }
}
SECTION("'abs()' on a positive quantity returns the abs")
{
SECTION("integral representation") { REQUIRE(abs(1 * isq::length[m]) == 1 * isq::length[m]); }
SECTION("floating-point representation") { REQUIRE(abs(1. * isq::length[m]) == 1 * isq::length[m]); }
}
}
TEST_CASE("numeric_limits functions", "[limits]")
{
SECTION("'epsilon' works as expected using default floating type")
{
REQUIRE(epsilon<double>(isq::length[m]).numerical_value_in(m) ==
std::numeric_limits<decltype(1. * isq::length[m])::rep>::epsilon());
}
SECTION("'epsilon' works as expected using integers")
{
REQUIRE(epsilon<int>(isq::length[m]).numerical_value_in(m) ==
std::numeric_limits<decltype(1 * isq::length[m])::rep>::epsilon());
}
}
TEST_CASE("floor functions", "[floor]")
{
SECTION("floor 1 second with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("floor 1000 milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor 1001 milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor 1999 milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1999 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor -1000 milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("floor -999 milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-999 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("floor 1.3 seconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1.3 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("floor -1.3 seconds with target unit second should be -2 seconds")
{
REQUIRE(floor<si::second>(-1.3 * isq::time[s]) == -2 * isq::time[s]);
}
SECTION("floor 1001. milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor 1999. milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1999. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor -1000. milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("floor -999. milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-999. * isq::time[ms]) == -1 * isq::time[s]);
}
// TODO Add tests for `N`, `kN` and `kg * m / s2` i `kg * km / s2`
}
TEST_CASE("ceil functions", "[ceil]")
{
SECTION("ceil 1 second with target unit second should be 1 second")
{
REQUIRE(ceil<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("ceil 1000 milliseconds with target unit second should be 1 second")
{
REQUIRE(ceil<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1001 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil -1000 milliseconds with target unit second should be -1 second")
{
REQUIRE(ceil<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("ceil -999 milliseconds with target unit second should be 0 seconds")
{
REQUIRE(ceil<si::second>(-999 * isq::time[ms]) == 0 * isq::time[s]);
}
SECTION("ceil 1.3 seconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1.3 * isq::time[s]) == 2 * isq::time[s]);
}
SECTION("ceil -1.3 seconds with target unit second should be -1 second")
{
REQUIRE(ceil<si::second>(-1.3 * isq::time[s]) == -1 * isq::time[s]);
}
SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1001. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
{
REQUIRE(ceil<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
{
REQUIRE(ceil<si::second>(-999. * isq::time[ms]) == 0 * isq::time[s]);
}
}
TEST_CASE("round functions", "[round]")
{
SECTION("round 1 second with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("round 1000 milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1001 milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1499 milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1499 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1500 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1500 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round 1999 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round -1000 milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1001 milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1001 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1499 milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1499 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1500 milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1500 * isq::time[ms]) == -2 * isq::time[s]);
}
SECTION("round -1999 milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1999 * isq::time[ms]) == -2 * isq::time[s]);
}
SECTION("round 1000. milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1000. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1001. milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1499. milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1499. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1500. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1500. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round 1999. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round -1000. milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1001. milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1001. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1499. milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1499. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1500. milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1500. * isq::time[ms]) == -2 * isq::time[s]);
}
SECTION("round -1999. milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1999. * isq::time[ms]) == -2 * isq::time[s]);
}
}
TEST_CASE("hypot functions", "[hypot]")
{
SECTION("hypot should work on the same quantities")
{
REQUIRE(hypot(3. * isq::length[km], 4. * isq::length[km]) == 5. * isq::length[km]);
REQUIRE(hypot(2. * isq::length[km], 3. * isq::length[km], 6. * isq::length[km]) == 7. * isq::length[km]);
}
SECTION("hypot should work with different units of the same dimension")
{
REQUIRE(hypot(3. * isq::length[km], 4000. * isq::length[m]) == 5. * isq::length[km]);
REQUIRE(hypot(2. * isq::length[km], 3000. * isq::length[m], 6. * isq::length[km]) == 7. * isq::length[km]);
}
}
TEST_CASE("SI trigonometric functions", "[trig][si]")
{
SECTION("sin")
{
REQUIRE_THAT(si::sin(0 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::sin(90 * deg), AlmostEquals(1. * one));
REQUIRE_THAT(si::sin(180 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::sin(270 * deg), AlmostEquals(-1. * one));
}
SECTION("cos")
{
REQUIRE_THAT(si::cos(0 * deg), AlmostEquals(1. * one));
REQUIRE_THAT(si::cos(90 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::cos(180 * deg), AlmostEquals(-1. * one));
REQUIRE_THAT(si::cos(270 * deg), AlmostEquals(0. * one));
}
SECTION("tan")
{
REQUIRE_THAT(si::tan(0 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::tan(45. * deg), AlmostEquals(1. * one));
REQUIRE_THAT(si::tan(135. * deg), AlmostEquals(-1. * one));
REQUIRE_THAT(si::tan(180. * deg), AlmostEquals(0. * one));
}
}
TEST_CASE("SI inverse trigonometric functions", "[inv trig][si]")
{
SECTION("asin")
{
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
}
SECTION("acos")
{
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
}
SECTION("atan")
{
REQUIRE_THAT(si::atan(-1 * one), AlmostEquals(-45. * deg));
REQUIRE_THAT(si::atan(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(si::atan(1 * one), AlmostEquals(45. * deg));
}
}
TEST_CASE("SI atan2 functions", "[atan2][si]")
{
SECTION("atan2 should work on the same quantities")
{
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * deg));
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * deg));
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * deg));
}
SECTION("atan2 should work with different units of the same dimension")
{
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * deg));
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * deg));
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * deg));
}
}
TEST_CASE("Angle trigonometric functions", "[trig][angle]")
{
using namespace mp_units::angular;
using namespace mp_units::angular::unit_symbols;
using mp_units::angular::unit_symbols::deg;
SECTION("sin")
{
REQUIRE_THAT(sin(0 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(90 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(180 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(270 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2));
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
}
SECTION("cos")
{
REQUIRE_THAT(cos(0 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(90 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(180 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(270 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(0 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(100 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(200 * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(300 * angle[grad]), AlmostEquals(0. * one));
}
SECTION("tan")
{
REQUIRE_THAT(tan(0 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(45 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(135 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(180 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2));
}
}
TEST_CASE("Angle inverse trigonometric functions", "[inv trig][angle]")
{
using namespace mp_units::angular;
using namespace mp_units::angular::unit_symbols;
using mp_units::angular::unit_symbols::deg;
SECTION("asin")
{
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
}
SECTION("acos")
{
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
}
SECTION("atan")
{
REQUIRE_THAT(atan(-1 * one), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg]));
}
}
TEST_CASE("Angle atan2 functions", "[atan2][angle]")
{
using namespace mp_units::angular;
using namespace mp_units::angular::unit_symbols;
using mp_units::angular::unit_symbols::deg;
SECTION("atan2 should work on the same quantities")
{
REQUIRE_THAT(atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * angle[deg]));
}
SECTION("atan2 should work with different units of the same dimension")
{
REQUIRE_THAT(atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg]));
REQUIRE(sqrt(4 * isq::area[m2]) == 2 * isq::length[m]);
}
SECTION("'cbrt()' on quantity changes the value and the dimension accordingly")
{
REQUIRE(cbrt(8 * isq::volume[m3]) == 2 * isq::length[m]);
}
SECTION("'fma()' on quantity changes the value and the dimension accordingly")
{
REQUIRE(fma(1.0 * isq::length[m], 2.0 * one, 2.0 * isq::length[m]) == 4.0 * isq::length[m]);
REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m));
}
SECTION("fmod functions")
{
SECTION("fmod should work on the same quantities")
{
REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]);
REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]);
}
SECTION("fmod should work with different units of the same dimension")
{
REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]);
REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]);
}
}
SECTION("remainder functions")
{
SECTION("remainder should work on the same quantities")
{
REQUIRE(remainder(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]);
REQUIRE(remainder(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]);
REQUIRE(remainder(3 * isq::length[km], 2 * isq::length[km]) == -1 * isq::length[km]);
REQUIRE(remainder(4 * isq::length[km], 2.75f * isq::length[km]) == 1.25 * isq::length[km]);
}
SECTION("remainder should work with different units of the same dimension")
{
REQUIRE(remainder(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]);
REQUIRE(remainder(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]);
REQUIRE(remainder(3. * isq::length[km], 2000. * isq::length[m]) == -1000 * isq::length[m]);
REQUIRE(remainder(4 * isq::length[km], 2750 * isq::length[m]) == 1250 * isq::length[m]);
}
}
SECTION("'isfinite()' accepts dimensioned arguments") { REQUIRE(isfinite(4.0 * isq::length[m])); }
SECTION("'isinf()' accepts dimensioned arguments") { REQUIRE(!isinf(4.0 * isq::length[m])); }
SECTION("'isnan()' accepts dimensioned arguments") { REQUIRE(!isnan(4.0 * isq::length[m])); }
SECTION("'pow<Num, Den>()' on quantity changes the value and the dimension accordingly")
{
REQUIRE(pow<1, 4>(16 * isq::area[m2]) == sqrt(4 * isq::length[m]));
}
// TODO add tests for exp()
SECTION("absolute functions on quantity returns the absolute value")
{
SECTION("'abs()' on a negative quantity returns the abs")
{
SECTION("integral representation") { REQUIRE(abs(-1 * isq::length[m]) == 1 * isq::length[m]); }
SECTION("floating-point representation") { REQUIRE(abs(-1. * isq::length[m]) == 1 * isq::length[m]); }
}
SECTION("'abs()' on a positive quantity returns the abs")
{
SECTION("integral representation") { REQUIRE(abs(1 * isq::length[m]) == 1 * isq::length[m]); }
SECTION("floating-point representation") { REQUIRE(abs(1. * isq::length[m]) == 1 * isq::length[m]); }
}
}
SECTION("numeric_limits functions")
{
SECTION("'epsilon' works as expected using default floating type")
{
REQUIRE(epsilon<double>(isq::length[m]).numerical_value_in(m) ==
std::numeric_limits<decltype(1. * isq::length[m])::rep>::epsilon());
}
SECTION("'epsilon' works as expected using integers")
{
REQUIRE(epsilon<int>(isq::length[m]).numerical_value_in(m) ==
std::numeric_limits<decltype(1 * isq::length[m])::rep>::epsilon());
}
}
SECTION("floor functions")
{
SECTION("floor 1 second with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("floor 1000 milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor 1001 milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor 1999 milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1999 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor -1000 milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("floor -999 milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-999 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("floor 1.3 seconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1.3 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("floor -1.3 seconds with target unit second should be -2 seconds")
{
REQUIRE(floor<si::second>(-1.3 * isq::time[s]) == -2 * isq::time[s]);
}
SECTION("floor 1001. milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor 1999. milliseconds with target unit second should be 1 second")
{
REQUIRE(floor<si::second>(1999. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("floor -1000. milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("floor -999. milliseconds with target unit second should be -1 second")
{
REQUIRE(floor<si::second>(-999. * isq::time[ms]) == -1 * isq::time[s]);
}
// TODO Add tests for `N`, `kN` and `kg * m / s2` i `kg * km / s2`
}
SECTION("ceil functions")
{
SECTION("ceil 1 second with target unit second should be 1 second")
{
REQUIRE(ceil<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("ceil 1000 milliseconds with target unit second should be 1 second")
{
REQUIRE(ceil<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("ceil 1001 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1001 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil 1999 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil -1000 milliseconds with target unit second should be -1 second")
{
REQUIRE(ceil<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("ceil -999 milliseconds with target unit second should be 0 seconds")
{
REQUIRE(ceil<si::second>(-999 * isq::time[ms]) == 0 * isq::time[s]);
}
SECTION("ceil 1.3 seconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1.3 * isq::time[s]) == 2 * isq::time[s]);
}
SECTION("ceil -1.3 seconds with target unit second should be -1 second")
{
REQUIRE(ceil<si::second>(-1.3 * isq::time[s]) == -1 * isq::time[s]);
}
SECTION("ceil 1001. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1001. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil 1999. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(ceil<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("ceil -1000. milliseconds with target unit second should be -1 second")
{
REQUIRE(ceil<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("ceil -999. milliseconds with target unit second should be 0 seconds")
{
REQUIRE(ceil<si::second>(-999. * isq::time[ms]) == 0 * isq::time[s]);
}
}
SECTION("round functions")
{
SECTION("round 1 second with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1 * isq::time[s]) == 1 * isq::time[s]);
}
SECTION("round 1000 milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1000 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1001 milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1001 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1499 milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1499 * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1500 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1500 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round 1999 milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1999 * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round -1000 milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1000 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1001 milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1001 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1499 milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1499 * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1500 milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1500 * isq::time[ms]) == -2 * isq::time[s]);
}
SECTION("round -1999 milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1999 * isq::time[ms]) == -2 * isq::time[s]);
}
SECTION("round 1000. milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1000. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1001. milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1001. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1499. milliseconds with target unit second should be 1 second")
{
REQUIRE(round<si::second>(1499. * isq::time[ms]) == 1 * isq::time[s]);
}
SECTION("round 1500. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1500. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round 1999. milliseconds with target unit second should be 2 seconds")
{
REQUIRE(round<si::second>(1999. * isq::time[ms]) == 2 * isq::time[s]);
}
SECTION("round -1000. milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1000. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1001. milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1001. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1499. milliseconds with target unit second should be -1 second")
{
REQUIRE(round<si::second>(-1499. * isq::time[ms]) == -1 * isq::time[s]);
}
SECTION("round -1500. milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1500. * isq::time[ms]) == -2 * isq::time[s]);
}
SECTION("round -1999. milliseconds with target unit second should be -2 seconds")
{
REQUIRE(round<si::second>(-1999. * isq::time[ms]) == -2 * isq::time[s]);
}
}
SECTION("hypot functions")
{
SECTION("hypot should work on the same quantities")
{
REQUIRE(hypot(3. * isq::length[km], 4. * isq::length[km]) == 5. * isq::length[km]);
REQUIRE(hypot(2. * isq::length[km], 3. * isq::length[km], 6. * isq::length[km]) == 7. * isq::length[km]);
}
SECTION("hypot should work with different units of the same dimension")
{
REQUIRE(hypot(3. * isq::length[km], 4000. * isq::length[m]) == 5. * isq::length[km]);
REQUIRE(hypot(2. * isq::length[km], 3000. * isq::length[m], 6. * isq::length[km]) == 7. * isq::length[km]);
}
}
SECTION("SI trigonometric functions")
{
SECTION("sin")
{
REQUIRE_THAT(si::sin(0 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::sin(90 * deg), AlmostEquals(1. * one));
REQUIRE_THAT(si::sin(180 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::sin(270 * deg), AlmostEquals(-1. * one));
}
SECTION("cos")
{
REQUIRE_THAT(si::cos(0 * deg), AlmostEquals(1. * one));
REQUIRE_THAT(si::cos(90 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::cos(180 * deg), AlmostEquals(-1. * one));
REQUIRE_THAT(si::cos(270 * deg), AlmostEquals(0. * one));
}
SECTION("tan")
{
REQUIRE_THAT(si::tan(0 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(si::tan(45. * deg), AlmostEquals(1. * one));
REQUIRE_THAT(si::tan(135. * deg), AlmostEquals(-1. * one));
REQUIRE_THAT(si::tan(180. * deg), AlmostEquals(0. * one));
}
}
SECTION("SI inverse trigonometric functions")
{
SECTION("asin")
{
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
}
SECTION("acos")
{
REQUIRE_THAT(si::asin(-1 * one), AlmostEquals(-90. * deg));
REQUIRE_THAT(si::asin(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(si::asin(1 * one), AlmostEquals(90. * deg));
}
SECTION("atan")
{
REQUIRE_THAT(si::atan(-1 * one), AlmostEquals(-45. * deg));
REQUIRE_THAT(si::atan(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(si::atan(1 * one), AlmostEquals(45. * deg));
}
}
SECTION("SI atan2 functions")
{
SECTION("atan2 should work on the same quantities")
{
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * deg));
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * deg));
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * deg));
}
SECTION("atan2 should work with different units of the same dimension")
{
REQUIRE_THAT(si::atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * deg));
REQUIRE_THAT(si::atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * deg));
REQUIRE_THAT(si::atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * deg));
}
}
SECTION("Angle trigonometric functions")
{
using namespace mp_units::angular;
using namespace mp_units::angular::unit_symbols;
using mp_units::angular::unit_symbols::deg;
SECTION("sin")
{
REQUIRE_THAT(sin(0 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(90 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(180 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(270 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2));
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
}
SECTION("cos")
{
REQUIRE_THAT(cos(0 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(90 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(180 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(270 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(0 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(100 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(200 * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(300 * angle[grad]), AlmostEquals(0. * one));
}
SECTION("tan")
{
REQUIRE_THAT(tan(0 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(45 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(135 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(180 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2));
}
}
SECTION("Angle inverse trigonometric functions")
{
using namespace mp_units::angular;
using namespace mp_units::angular::unit_symbols;
using mp_units::angular::unit_symbols::deg;
SECTION("asin")
{
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
}
SECTION("acos")
{
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
}
SECTION("atan")
{
REQUIRE_THAT(atan(-1 * one), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg]));
}
}
SECTION("Angle atan2 functions")
{
using namespace mp_units::angular;
using namespace mp_units::angular::unit_symbols;
using mp_units::angular::unit_symbols::deg;
SECTION("atan2 should work on the same quantities")
{
REQUIRE_THAT(atan2(-1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan2(0. * isq::length[km], 1. * isq::length[km]), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan2(1. * isq::length[km], 1. * isq::length[km]), AlmostEquals(45. * angle[deg]));
}
SECTION("atan2 should work with different units of the same dimension")
{
REQUIRE_THAT(atan2(-1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan2(0. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan2(1. * isq::length[km], 1000. * isq::length[m]), AlmostEquals(45. * angle[deg]));
}
}
}

View File

@ -60,22 +60,25 @@ constexpr bool within_4_ulps(T a, T b)
} // namespace
// conversion requiring radical magnitudes
TEST_CASE("unit conversions support radical magnitudes", "[conversion][radical]")
TEST_CASE("quantity operations", "[quantity]")
{
REQUIRE(within_4_ulps(sqrt((1.0 * m) * (1.0 * km)).numerical_value_in(m), sqrt(1000.0)));
}
// conversion requiring radical magnitudes
SECTION("unit conversions support radical magnitudes")
{
REQUIRE(within_4_ulps(sqrt((1.0 * m) * (1.0 * km)).numerical_value_in(m), sqrt(1000.0)));
}
// Reproducing issue #474 exactly:
TEST_CASE("Issue 474 is fixed", "[conversion][radical]")
{
constexpr auto val_issue_474 = 8.0 * si::si2019::boltzmann_constant * 1000.0 * K / (std::numbers::pi * 10 * Da);
REQUIRE(within_4_ulps(sqrt(val_issue_474).numerical_value_in(m / s),
sqrt(val_issue_474.numerical_value_in(m * m / s / s))));
}
// Reproducing issue #474 exactly:
SECTION("Issue 474 is fixed")
{
constexpr auto val_issue_474 = 8.0 * si::si2019::boltzmann_constant * 1000.0 * K / (std::numbers::pi * 10 * Da);
REQUIRE(within_4_ulps(sqrt(val_issue_474).numerical_value_in(m / s),
sqrt(val_issue_474.numerical_value_in(m * m / s / s))));
}
TEST_CASE("Volatile representation type", "[volatile]")
{
volatile std::int16_t vint = 123;
REQUIRE(quantity(vint * m).numerical_value_in(m) == 123);
SECTION("Volatile representation type")
{
volatile std::int16_t vint = 123;
REQUIRE(quantity(vint * m).numerical_value_in(m) == 123);
}
}

View File

@ -25,24 +25,28 @@
#include <mp-units/systems/isq/mechanics.h>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/units.h>
template<class T>
requires mp_units::is_scalar<T>
constexpr bool mp_units::is_vector<T> = true;
#if MP_UNITS_HOSTED
#include <mp-units/cartesian_vector.h>
#endif
namespace {
using namespace mp_units;
using namespace mp_units::cgs;
using namespace mp_units::cgs::unit_symbols;
#if MP_UNITS_HOSTED
using v = cartesian_vector<double>;
#endif
// https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units#Definitions_and_conversion_factors_of_CGS_units_in_mechanics
static_assert(isq::length(100 * cm) == isq::length(1 * si::metre));
static_assert(isq::mass(1000 * g) == isq::mass(1 * si::kilogram));
static_assert(isq::time(1 * s) == isq::time(1 * si::second));
static_assert(isq::speed(100 * cm / s) == isq::speed(1 * si::metre / si::second));
static_assert(isq::acceleration(100 * Gal) == isq::acceleration(1 * si::metre / square(si::second)));
static_assert(isq::force(100'000 * dyn) == isq::force(1 * si::newton));
#if MP_UNITS_HOSTED
static_assert(isq::acceleration(v{100} * Gal) == isq::acceleration(v{1} * si::metre / square(si::second)));
static_assert(isq::force(v{100'000} * dyn) == isq::force(v{1} * si::newton));
#endif
static_assert(isq::energy(10'000'000 * erg) == isq::energy(1 * si::joule));
static_assert(isq::power(10'000'000 * erg / s) == isq::power(1 * si::watt));
static_assert(isq::pressure(10 * Ba) == isq::pressure(1 * si::pascal));

View File

@ -237,17 +237,17 @@ static_assert(compare(round<si::second>(-1999. * isq::time[ms]), -2. * isq::time
#endif
// non-truncating
static_assert(compare(inverse<us>(250 * Hz), 4000 * us));
static_assert(compare(inverse<us>(250 * kHz), 4 * us));
static_assert(compare(inverse<ks>(250 * uHz), 4 * ks));
static_assert(compare(kind_of<isq::time>(inverse<us>(250 * Hz)), 4000 * us));
static_assert(compare(kind_of<isq::time>(inverse<us>(250 * kHz)), 4 * us));
static_assert(compare(kind_of<isq::time>(inverse<ks>(250 * uHz)), 4 * ks));
// truncating
static_assert(compare(inverse<s>(1 * kHz), 0 * s));
static_assert(compare(kind_of<isq::time>(inverse<s>(1 * kHz)), 0 * s));
// floating-point representation does not truncate
static_assert(compare(inverse<s>(1. * kHz), 0.001 * s));
static_assert(compare(kind_of<isq::time>(inverse<s>(1. * kHz)), 0.001 * s));
// check if constraints work properly for a derived unit of a narrowed kind
static_assert(compare(inverse<Hz>(1 * s), 1 * Hz));
static_assert(compare(kind_of<isq::frequency>(inverse<Hz>(1 * s)), 1 * Hz));
} // namespace