@@ -20,16 +20,36 @@ namespace unlib {
20
20
21
21
namespace detail {
22
22
23
- template <typename U, typename S, typename V, typename T, std::intmax_t N, std::intmax_t D>
24
- auto pow (const quantity<U,S,T,V>& q, const std::ratio<N,D>) {
25
- return pow_quantity_t <quantity<U,S,T,V>, std::ratio<N,D>>{ std::pow ( static_cast <double >(q.get ()), static_cast <double >(N)/D ) };
23
+ template <typename Float1, typename T>
24
+ using if_float1_pt_t = typename std::enable_if< is_floating_point<Float1>::value, T >::type;
25
+ template <typename Float1, typename Float2, typename T>
26
+ using if_float2_pt_t = typename std::enable_if< is_floating_point<Float1>::value
27
+ and is_floating_point<Float2>::value, T >::type;
28
+
29
+ template <typename D, typename S, typename V, typename T, std::intmax_t Nom, std::intmax_t Den>
30
+ auto pow (const quantity<D,S,T,V>& q, const std::ratio<Nom,Den>) {
31
+ return pow_quantity_t <quantity<D,S,T,V>, std::ratio<Nom,Den>>{ std::pow ( static_cast <double >(q.get ()), static_cast <double >(Nom)/Den ) };
26
32
}
27
33
28
34
template <typename U, typename S, typename V, typename T>
29
35
auto pow (const quantity<U,S,T,V>& q, const std::ratio<1 ,2 >) {
30
36
return pow_quantity_t <quantity<U,S,T,V>,std::ratio<1 ,2 >>(std::sqrt (q.get ()));
31
37
}
32
38
39
+ template <typename TolTag, typename F, typename >
40
+ struct tolerance_aux {
41
+ using tolerance_tag_type = TolTag;
42
+ using value_type = F;
43
+ value_type v;
44
+ };
45
+
46
+ struct tolerance_val_tag ;
47
+ struct tolerance_nom_tag ;
48
+ struct tolerance_frc_tag ;
49
+
50
+ template <typename V, typename S=scale_t <1 >>
51
+ using fraction = quantity<dimensionless, S, V, no_tag>;
52
+
33
53
}
34
54
35
55
/* *
@@ -113,6 +133,284 @@ template<typename U, typename S, typename V, typename T>
113
133
auto cbrt (quantity<U,S,T,V> q) {return pow<std::ratio<1 ,3 >>(q);}
114
134
/* * @} */
115
135
136
+ /* *
137
+ * @brief Provide the minimum of two quantities
138
+ *
139
+ * @param lhs, rhs quantities to compare
140
+ *
141
+ * @return minimum value of @p lhs and @p rhs
142
+ */
143
+ template <typename D, typename F, typename S1, typename S2, typename T>
144
+ auto min (const unlib::quantity<D,S1,F,T> lhs, const unlib::quantity<D,S2,F,T> rhs) {
145
+ using std::min;
146
+ return unlib::quantity<D,S1,F,T>{min (lhs.get (), rhs.template get_scaled <S1>())};
147
+ }
148
+
149
+ /* *
150
+ * @brief Provide the maximum of two quantities
151
+ *
152
+ * @param lhs, rhs quantities to compare
153
+ *
154
+ * @return maximum value of @p lhs and @p rhs
155
+ */
156
+ template <typename D, typename F, typename S1, typename S2, typename T>
157
+ auto max (const unlib::quantity<D,S1,F,T> lhs, const unlib::quantity<D,S2,F,T> rhs) {
158
+ using std::max;
159
+ return unlib::quantity<D,S1,F,T>{max (lhs.get (), rhs.template get_scaled <S1>())};
160
+ }
161
+
162
+ /* *
163
+ * @{
164
+ *
165
+ * @brief Floating point comparisons
166
+ *
167
+ * Due to the limitations of floating point precision, more often than
168
+ * never values which, mathematically, ought to be equal, are in fact not
169
+ * totally equal. Therefore, floating point comparisons should be done with
170
+ * a certain tolerance.
171
+ *
172
+ * This provides a framework for comparing floating point types and
173
+ * quantities of floating point types. The tolerance can either be an
174
+ * absolute value, or a fraction of a nominal value. For example, in order to
175
+ * test whether two masses are equal, the tolerance could be given either as
176
+ * an absolute value (10mg) or as a fraction (0.1%) of a nominal weight (1t).
177
+ * The library provides default values for the nominal and the weight, which
178
+ * can be used, but this must be indicated explicitly.
179
+ *
180
+ * The defaults are accessed via traits which can be specialized by users in
181
+ * order to provide their own defaults.
182
+ */
183
+
184
+ constexpr double tolerance_default_nominal = 1 .; /* *< default tolerance nominal for is_near() etc. */
185
+ constexpr double tolerance_default_fraction = 0.0001 ; /* *< default tolerance fraction for is_near() etc. */
186
+
187
+ /* *
188
+ * @{
189
+ *
190
+ * @brief wrapper for tolerance values
191
+ *
192
+ * In order to distinguish between tolerance values, nominals, and fractions
193
+ * in floating point comparisons, those are wrapped in these types.
194
+ *
195
+ * @note These are the result types of @ref tolerance_value(), @ref tolerance_nominal(),
196
+ * and @ref tolerance_fraction(). Users should not have to deal with them
197
+ * directly.
198
+ */
199
+ template <typename V> using tolerance_val = detail::tolerance_aux<detail::tolerance_val_tag,V,V>;
200
+ template <typename V> using tolerance_nom = detail::tolerance_aux<detail::tolerance_nom_tag,V,V>;
201
+ template <typename V, typename Q> using tolerance_frc = detail::tolerance_aux<detail::tolerance_frc_tag,V,Q>;
202
+ /* * @} */
203
+
204
+ /* *
205
+ * @{
206
+ *
207
+ * @brief tolerance value calculations
208
+ *
209
+ * A tolerance value can be calculated by multiplying a nominal and a fraction.
210
+ *
211
+ * @param n tolerance nominal value
212
+ * @param f tolerance fraction value
213
+ *
214
+ * @return tolerance value
215
+ */
216
+ template <typename U, typename S, typename SF, typename V, typename T, typename Q>
217
+ inline constexpr
218
+ tolerance_val<quantity<U,S,V,T>> operator *(tolerance_nom<quantity<U,S,V,T>> n, tolerance_frc<detail::fraction<V,SF>,Q> f)
219
+ {return tolerance_val<quantity<U,S,V,T>>{n.v * f.v .template get_scaled <no_scaling>()};}
220
+
221
+ template <typename F, typename SF, typename Q>
222
+ inline constexpr
223
+ tolerance_val<F> operator *(tolerance_nom<F> n, tolerance_frc<detail::fraction<F,SF>,Q> f)
224
+ {return tolerance_val<F>{n.v * f.v .template get_scaled <no_scaling>()};}
225
+ /* * @} */
226
+
227
+ /* *
228
+ * @{
229
+ * @brief tolerance traits
230
+ *
231
+ * These provide the necessary functions to obtain default tolerance values,
232
+ * nominals, and fractions.
233
+ *
234
+ * @tparam F Floating point type.
235
+ */
236
+ template <typename F>
237
+ struct tolerance_traits {
238
+ using float_t = detail::if_float1_pt_t <F,F>;
239
+ using fract_t = detail::fraction<float_t >;
240
+
241
+ static constexpr auto value () {return nominal () * fraction ();}
242
+ static constexpr auto nominal () {return tolerance_nom<float_t >{ static_cast <float_t >(tolerance_default_nominal ) };}
243
+ static constexpr auto fraction () {return tolerance_frc<fract_t ,float_t >{fract_t {static_cast <float_t >(tolerance_default_fraction)}};}
244
+ };
245
+
246
+ /* * specialization for quantities of floating point types */
247
+ template <typename U, typename S, typename F, typename T>
248
+ struct tolerance_traits <quantity<U,S,F,T>> {
249
+ using float_t = detail::if_float1_pt_t <F,F>;
250
+ using quant_t = quantity<U,S,float_t ,T>;
251
+ using fract_t = detail::fraction<float_t >;
252
+
253
+ static constexpr auto value () {return nominal () * fraction ();}
254
+ static constexpr auto nominal () {return tolerance_nom<quant_t >{quant_t {tolerance_traits<float_t >::nominal ().v }};}
255
+ static constexpr auto fraction () {return tolerance_frc<fract_t ,quant_t >{ tolerance_traits<float_t >::fraction ().v };}
256
+ };
257
+ /* * @} */
258
+
259
+ /* *
260
+ * @{
261
+ *
262
+ * @brief create a tolerance value, nominal, or fraction
263
+ *
264
+ * These function turn a value into a tolerance value, nominal, or fraction.
265
+ *
266
+ * @param val value to turn into a tolerance value, nominal, or fraction
267
+ *
268
+ * @return tolerance value, nominal, or fraction
269
+ */
270
+ template <typename T> constexpr auto tolerance_value (T val) {return tolerance_val<T >{val};}
271
+ template <typename T> constexpr auto tolerance_nominal (T nom) {return tolerance_nom<T >{nom};}
272
+ template <typename T, typename F> constexpr auto tolerance_fraction (F frc) {return tolerance_frc<F,T>{frc};}
273
+ /* * @} */
274
+
275
+ /* *
276
+ * @{
277
+ *
278
+ * @brief get the default tolerance value, nominal, or fraction
279
+ *
280
+ * These function return the tolerance value, nominal, or fraction for a
281
+ * given type.
282
+ *
283
+ * @return default tolerance value, nominal, or fraction
284
+ */
285
+ template <typename T> constexpr auto tolerance_value () {return tolerance_traits<T>::value ();}
286
+ template <typename T> constexpr auto tolerance_nominal () {return tolerance_traits<T>::nominal ();}
287
+ template <typename T> constexpr auto tolerance_fraction () {return tolerance_traits<T>::fraction ();}
288
+ /* * @} */
289
+
290
+ namespace detail {
291
+
292
+ template <typename V, typename F, typename Q> constexpr auto get_tol_val (tolerance_nom<V> n, tolerance_frc<F,Q> f) {return n * f;}
293
+ template <typename V, typename F, typename Q> constexpr auto get_tol_val (tolerance_frc<F,Q> f, tolerance_nom<V> n) {return get_tol_val (n,f);}
294
+ template <typename V> constexpr auto get_tol_val (tolerance_aux<tolerance_val_tag,V,V> v) {return v;}
295
+ template <typename V> constexpr auto get_tol_val (tolerance_aux<tolerance_nom_tag,V,V> n) {return get_tol_val (n, tolerance_traits<V>::fraction ());}
296
+ template <typename F, typename Q> constexpr auto get_tol_val (tolerance_aux<tolerance_frc_tag,F,Q> f) {return get_tol_val (f, tolerance_traits<Q>::nominal ());}
297
+
298
+ template <typename T1, typename T2, typename Tol> constexpr bool is_near_impl (T1 lval, T2 rval, tolerance_val<Tol> tol) {using std::abs; return abs (lval-rval) <= abs (tol.v );}
299
+ template <typename T1, typename T2, typename Tol> constexpr bool is_smaller_impl (T1 lval, T2 rval, tolerance_val<Tol> tol) {using std::abs; return not is_near_impl (lval,rval,tol) and lval<rval;}
300
+ template <typename T1, typename T2, typename Tol> constexpr bool is_greater_impl (T1 lval, T2 rval, tolerance_val<Tol> tol) {using std::abs; return not is_near_impl (lval,rval,tol) and lval>rval;}
301
+
302
+ template <typename T1, typename T2, typename Aux> constexpr detail::if_float2_pt_t <T1,T2,bool > is_near (T1 lval, T2 rval, Aux tol) {return is_near_impl (lval, rval, get_tol_val (tol));}
303
+ template <typename T1, typename T2, typename Aux> constexpr detail::if_float2_pt_t <T1,T2,bool > is_smaller (T1 lval, T2 rval, Aux tol) {return is_smaller_impl (lval, rval, get_tol_val (tol));}
304
+ template <typename T1, typename T2, typename Aux> constexpr detail::if_float2_pt_t <T1,T2,bool > is_greater (T1 lval, T2 rval, Aux tol) {return is_greater_impl (lval, rval, get_tol_val (tol));}
305
+ template <typename T1, typename T2, typename Aux1, typename Aux2> constexpr detail::if_float2_pt_t <T1,T2,bool > is_near (T1 lval, T2 rval, Aux1 tol1, Aux2 tol2) {return is_near_impl (lval, rval, get_tol_val (tol1,tol2));}
306
+ template <typename T1, typename T2, typename Aux1, typename Aux2> constexpr detail::if_float2_pt_t <T1,T2,bool > is_smaller (T1 lval, T2 rval, Aux1 tol1, Aux2 tol2) {return is_smaller_impl (lval, rval, get_tol_val (tol1,tol2));}
307
+ template <typename T1, typename T2, typename Aux1, typename Aux2> constexpr detail::if_float2_pt_t <T1,T2,bool > is_greater (T1 lval, T2 rval, Aux1 tol1, Aux2 tol2) {return is_greater_impl (lval, rval, get_tol_val (tol1,tol2));}
308
+
309
+ }
310
+
311
+ /* * @{
312
+ * @note These algorithms take tolerances. As tolerances, either an absolute
313
+ * tolerance value (created via @ref tolerance_value()) or a tolerance
314
+ * nominal (created via @ref tolerance_nominal()) and a tolerance
315
+ * fraction (created via @ref tolerance_fraction()) can be passed.
316
+ * Either the nominal or the fraction can also be omitted, in which
317
+ * case the default is obtained via the @ref tolerance_traits.
318
+ *
319
+ * @note Tolerances are inclusive. That is, two values which differ exactly by
320
+ * the tolerance value are considered equal.
321
+ *
322
+ * @note These algorithms will always use the `abs()` value of the tolerance,
323
+ * so the sign of the tolerance values will be ignored.
324
+ *
325
+ */
326
+
327
+ /* * @{
328
+ * @brief compare two floating point values
329
+ *
330
+ * This compares two floating point values or two quantities of floating point
331
+ * values.
332
+ *
333
+ * @param lval left value to compare
334
+ * @param rval right value to compare
335
+ * @param tol, tol1, tol2 tolerance
336
+ *
337
+ * @note The quantities do not need to be of the same scale, but they need to
338
+ * have the same unit, value type, and tag.
339
+ */
340
+ /* *
341
+ * @{
342
+ * @return true if both quantities are equal within the given tolerance.
343
+ */
344
+ template <typename F1, typename F2, typename TT, typename TF, typename X>
345
+ bool is_near (F1 lval, F2 rval, detail::tolerance_aux<TT,TF,X> tol) {return detail::is_near (lval, rval, tol);}
346
+ template <typename F1, typename F2, typename TT1, typename TF1, typename TT2, typename TF2, typename X1, typename X2>
347
+ bool is_near (F1 lval, F2 rval, detail::tolerance_aux<TT1,TF1,X1> tol1, detail::tolerance_aux<TT2,TF2,X2> tol2) {return detail::is_near (lval, rval, tol1, tol2);}
348
+ /* * @} */
349
+ /* *
350
+ * @{
351
+ * @return true if both quantities are not equal within the given tolerance
352
+ * and the left value is smaller than the right value.
353
+ */
354
+ template <typename F1, typename F2, typename TT1, typename TF1, typename TT2, typename TF2, typename X1, typename X2>
355
+ bool is_smaller (F1 lval, F2 rval, detail::tolerance_aux<TT1,TF1,X1> tol1, detail::tolerance_aux<TT2,TF2,X2> tol2) {return detail::is_smaller (lval, rval, tol1, tol2);}
356
+ template <typename F1, typename F2, typename TT, typename TF, typename X>
357
+ bool is_smaller (F1 lval, F2 rval, detail::tolerance_aux<TT,TF,X> tol) {return detail::is_smaller (lval, rval, tol);}
358
+ /* * @} */
359
+ /* *
360
+ * @{
361
+ * @return true if both quantities are not equal within the given tolerance
362
+ * and the left value is greater than the right value.
363
+ */
364
+ template <typename F1, typename F2, typename TT, typename TF, typename X>
365
+ bool is_greater (F1 lval, F2 rval, detail::tolerance_aux<TT,TF,X> tol) {return detail::is_greater (lval, rval, tol);}
366
+ template <typename F1, typename F2, typename TT1, typename TF1, typename TT2, typename TF2, typename X1, typename X2>
367
+ bool is_greater (F1 lval, F2 rval, detail::tolerance_aux<TT1,TF1,X1> tol1, detail::tolerance_aux<TT2,TF2,X2> tol2) {return detail::is_greater (lval, rval, tol1, tol2);}
368
+ /* * @} */
369
+ /* * @} */
370
+
371
+ /* * @{
372
+ * @brief compare a floating point value to zero
373
+ *
374
+ * This compares a floating point value, or a quantity of a floating point
375
+ * value, with zero.
376
+ *
377
+ * @param val value to compare
378
+ * @param tol, tol1, tol2 tolerance
379
+ *
380
+ */
381
+ /* @{
382
+ * @return true if the quantity is equal to zero within the tolerance
383
+ * given
384
+ */
385
+ template <typename F, typename TT, typename TF, typename X>
386
+ bool is_near_zero (F val, detail::tolerance_aux<TT,TF,X> tol) {return detail::is_near (val,F{}, tol);}
387
+ template <typename F, typename TT1, typename TF1, typename TT2, typename TF2, typename X1, typename X2>
388
+ bool is_near_zero (F val, detail::tolerance_aux<TT1,TF1,X1> tol1, detail::tolerance_aux<TT2,TF2,X2> tol2) {return detail::is_near (val,F{}, tol1, tol2);}
389
+ /* * @} */
390
+ /* @{
391
+ * @return true if the quantity is smaller than zero within the tolerance
392
+ * given
393
+ */
394
+ template <typename F, typename TT, typename TF, typename X>
395
+ bool is_smaller_zero (F val, detail::tolerance_aux<TT,TF,X> tol) {return detail::is_smaller (val,F{}, tol);}
396
+ template <typename F, typename TT1, typename TF1, typename TT2, typename TF2, typename X1, typename X2>
397
+ bool is_smaller_zero (F val, detail::tolerance_aux<TT1,TF1,X1> tol1, detail::tolerance_aux<TT2,TF2,X2> tol2) {return detail::is_smaller (val,F{}, tol1, tol2);}
398
+ /* * @} */
399
+ /* @{
400
+ * @return true if the quantity is greater than zero within the tolerance
401
+ * given
402
+ */
403
+ template <typename F, typename TT, typename TF, typename X>
404
+ bool is_greater_zero (F val, detail::tolerance_aux<TT,TF,X> tol) {return detail::is_greater (val,F{}, tol);}
405
+ template <typename F, typename TT1, typename TF1, typename TT2, typename TF2, typename X1, typename X2>
406
+ bool is_greater_zero (F val, detail::tolerance_aux<TT1,TF1,X1> tol1, detail::tolerance_aux<TT2,TF2,X2> tol2) {return detail::is_greater (val,F{}, tol1, tol2);}
407
+ /* * @} */
408
+ /* * @} */
409
+
410
+ /* * @} */
411
+
412
+ /* * @} */
413
+
116
414
}
117
415
118
416
#endif // UNLIB_MATH_HPP
0 commit comments