Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ExtendedNumber.h
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <limits>
5#include <ostream>
6#include <type_traits>
7#include <utility>
8#include <vector>
9
15
16namespace storm::utility {
17
36template<typename ValueType>
38 public:
40
44 ExtendedNumber() : kind(Kind::Finite), value(storm::utility::zero<ValueType>()) {
45 // Intentionally left empty
46 }
47
52 ExtendedNumber(ValueType const& value) : kind(Kind::Finite), value(value) {
53 // Intentionally left empty
54 }
55
56 ExtendedNumber(ValueType&& value) : kind(Kind::Finite), value(std::move(value)) {
57 // Intentionally left empty
58 }
59
60 ExtendedNumber(ExtendedNumber const&) = default;
64 ~ExtendedNumber() = default;
65
72
79
83 bool isFinite() const {
84 return kind == Kind::Finite;
85 }
86
90 bool isInfinite() const {
91 return kind != Kind::Finite;
92 }
93
97 bool isPositiveInfinity() const {
98 return kind == Kind::PositiveInfinity;
99 }
100
104 bool isNegativeInfinity() const {
105 return kind == Kind::NegativeInfinity;
106 }
107
112 ValueType const& getFinite() const {
113 STORM_LOG_ASSERT(isFinite(), "Tried to get the finite value of " << *this << ".");
114 return value;
115 }
116
121 ValueType& getFinite() {
122 STORM_LOG_ASSERT(isFinite(), "Tried to get the finite value of " << *this << ".");
123 return value;
124 }
125
126 friend bool operator==(ExtendedNumber const& first, ExtendedNumber const& second) {
127 if (first.kind != second.kind) {
128 return false;
129 }
130 return !first.isFinite() || first.value == second.value;
131 }
132
133 friend bool operator!=(ExtendedNumber const& first, ExtendedNumber const& second) {
134 return !(first == second);
135 }
136
137 friend bool operator<(ExtendedNumber const& first, ExtendedNumber const& second) {
138 if (first.kind != second.kind) {
139 return first.kind < second.kind;
140 }
141 return first.isFinite() && first.value < second.value;
142 }
143
144 friend bool operator<=(ExtendedNumber const& first, ExtendedNumber const& second) {
145 return !(second < first);
146 }
147
148 friend bool operator>(ExtendedNumber const& first, ExtendedNumber const& second) {
149 return second < first;
150 }
151
152 friend bool operator>=(ExtendedNumber const& first, ExtendedNumber const& second) {
153 return !(first < second);
154 }
155
162 friend bool operator==(ExtendedNumber const& first, ValueType const& second) {
163 return first.isFinite() && first.value == second;
164 }
165
166 friend bool operator==(ValueType const& first, ExtendedNumber const& second) {
167 return second.isFinite() && first == second.value;
168 }
169
170 friend bool operator!=(ExtendedNumber const& first, ValueType const& second) {
171 return !(first == second);
172 }
173
174 friend bool operator!=(ValueType const& first, ExtendedNumber const& second) {
175 return !(first == second);
176 }
177
178 friend bool operator<(ExtendedNumber const& first, ValueType const& second) {
179 // Every finite value is above -infinity and below +infinity.
180 return first.isFinite() ? first.value < second : first.isNegativeInfinity();
181 }
182
183 friend bool operator<(ValueType const& first, ExtendedNumber const& second) {
184 return second.isFinite() ? first < second.value : second.isPositiveInfinity();
185 }
186
187 friend bool operator<=(ExtendedNumber const& first, ValueType const& second) {
188 return !(second < first);
189 }
190
191 friend bool operator<=(ValueType const& first, ExtendedNumber const& second) {
192 return !(second < first);
193 }
194
195 friend bool operator>(ExtendedNumber const& first, ValueType const& second) {
196 return second < first;
197 }
198
199 friend bool operator>(ValueType const& first, ExtendedNumber const& second) {
200 return second < first;
201 }
202
203 friend bool operator>=(ExtendedNumber const& first, ValueType const& second) {
204 return !(first < second);
205 }
206
207 friend bool operator>=(ValueType const& first, ExtendedNumber const& second) {
208 return !(first < second);
209 }
210
212 switch (kind) {
214 return negativeInfinity();
216 return infinity();
217 default:
218 return ExtendedNumber(-value);
219 }
220 }
221
222 ExtendedNumber const& operator+() const {
223 return *this;
224 }
225
226 friend ExtendedNumber operator+(ExtendedNumber const& first, ExtendedNumber const& second) {
227 if (first.isFinite() && second.isFinite()) {
228 return ExtendedNumber(first.value + second.value);
229 }
230 if (first.isInfinite() && second.isInfinite()) {
231 STORM_LOG_THROW(first.kind == second.kind, storm::exceptions::InvalidOperationException, "Tried to compute " << first << " + " << second << ".");
232 return first;
233 }
234 return first.isInfinite() ? first : second;
235 }
236
237 friend ExtendedNumber operator-(ExtendedNumber const& first, ExtendedNumber const& second) {
238 return first + (-second);
239 }
240
241 friend ExtendedNumber operator*(ExtendedNumber const& first, ExtendedNumber const& second) {
242 if (first.isFinite() && second.isFinite()) {
243 return ExtendedNumber(first.value * second.value);
244 }
245 // At least one operand is infinite, so the result is determined by the signs unless the other operand is zero.
246 int const signs = first.sign() * second.sign();
247 STORM_LOG_THROW(signs != 0, storm::exceptions::InvalidOperationException, "Tried to compute " << first << " * " << second << ".");
248 return signs > 0 ? infinity() : negativeInfinity();
249 }
250
251 friend ExtendedNumber operator/(ExtendedNumber const& first, ExtendedNumber const& second) {
252 STORM_LOG_THROW(!(first.isInfinite() && second.isInfinite()), storm::exceptions::InvalidOperationException,
253 "Tried to compute " << first << " / " << second << ".");
254 if (second.isInfinite()) {
255 // A finite value divided by an infinite one.
257 }
258 int const divisorSign = second.sign();
259 STORM_LOG_THROW(divisorSign != 0, storm::exceptions::InvalidOperationException, "Tried to compute " << first << " / " << second << ".");
260 if (first.isFinite()) {
261 return ExtendedNumber(first.value / second.value);
262 }
263 return (first.sign() * divisorSign) > 0 ? infinity() : negativeInfinity();
264 }
265
267 return *this = *this + other;
268 }
269
271 return *this = *this - other;
272 }
273
275 return *this = *this * other;
276 }
277
279 return *this = *this / other;
280 }
281
282 friend std::ostream& operator<<(std::ostream& out, ExtendedNumber const& number) {
283 switch (number.kind) {
285 out << "inf";
286 break;
288 out << "-inf";
289 break;
290 default:
291 out << number.value;
292 break;
293 }
294 return out;
295 }
296
297 private:
298 explicit ExtendedNumber(Kind kind) : kind(kind), value(storm::utility::zero<ValueType>()) {
299 // Intentionally left empty
300 }
301
305 int sign() const {
306 switch (kind) {
308 return 1;
310 return -1;
311 default:
312 if (storm::utility::isZero(value)) {
313 return 0;
314 }
315 return value < storm::utility::zero<ValueType>() ? -1 : 1;
316 }
317 }
318
319 Kind kind;
320
323 ValueType value;
324};
325
330template<typename ValueType>
331using ExtendedValueType = std::conditional_t<storm::NumberTraits<ValueType>::HasInfinity, ValueType, ExtendedNumber<ValueType>>;
332
333namespace detail {
334template<typename T>
335struct IsExtendedNumber : std::false_type {};
336
337template<typename T>
338struct IsExtendedNumber<ExtendedNumber<T>> : std::true_type {};
339
341template<typename T>
343 typedef T type;
344};
345
346template<typename T>
348 typedef T type;
349};
350} // namespace detail
351
356template<typename ValueType>
358
362template<typename ValueType>
370
374template<typename ValueType>
382
388template<typename ValueType>
389ValueType const& getFinite(ExtendedNumber<ValueType> const& value) {
390 return value.getFinite();
391}
392
393template<typename ValueType>
394 requires(!detail::IsExtendedNumber<ValueType>::value)
395ValueType const& getFinite(ValueType const& value) {
396 return value;
397}
398
402template<typename ValueType>
404 return value.isFinite();
405}
406
407template<typename ValueType>
408 requires(!detail::IsExtendedNumber<ValueType>::value)
409bool isFinite(ValueType const& value) {
411 // A NaN is not infinite, but it is not a finite value either: the guards built on this predicate are there to
412 // keep a value that is not a number out of a computation, and a NaN is exactly that.
413 return !storm::utility::isInfinity(value) && !storm::utility::isInfinity(ValueType(-value)) && !storm::utility::isNan(value);
414 } else {
415 return true;
416 }
417}
418
423template<typename ValueType>
424std::vector<ValueType> narrowFinite(std::vector<ExtendedValueType<ValueType>>&& values) {
425 if constexpr (std::is_same_v<ExtendedValueType<ValueType>, ValueType>) {
426 return std::move(values);
427 } else {
428 std::vector<ValueType> result;
429 result.reserve(values.size());
430 for (auto& value : values) {
431 STORM_LOG_THROW(value.isFinite(), storm::exceptions::InvalidOperationException, "Tried to narrow " << value << " to a type that cannot hold it.");
432 result.push_back(std::move(value.getFinite()));
433 }
434 return result;
435 }
436}
437
442template<typename ValueType>
443std::vector<ValueType> narrowFinite(std::vector<ExtendedValueType<ValueType>> const& values) {
444 if constexpr (std::is_same_v<ExtendedValueType<ValueType>, ValueType>) {
445 return values;
446 } else {
447 std::vector<ValueType> result;
448 result.reserve(values.size());
449 for (auto const& value : values) {
450 STORM_LOG_THROW(value.isFinite(), storm::exceptions::InvalidOperationException, "Tried to narrow " << value << " to a type that cannot hold it.");
451 result.push_back(value.getFinite());
452 }
453 return result;
454 }
455}
456
461template<typename ValueType>
462std::vector<ValueType> narrowFinite(std::vector<ExtendedValueType<ValueType>>&& values, ValueType const& defaultValue) {
463 std::vector<ValueType> result;
464 result.reserve(values.size());
465 for (auto& value : values) {
466 if constexpr (std::is_same_v<ExtendedValueType<ValueType>, ValueType>) {
467 result.push_back(storm::utility::isFinite(value) ? std::move(value) : defaultValue);
468 } else {
469 result.push_back(value.isFinite() ? std::move(value.getFinite()) : defaultValue);
470 }
471 }
472 return result;
473}
474
480template<typename ValueType>
481ValueType narrow(ExtendedValueType<ValueType> const& value) {
483 STORM_LOG_THROW(!value.isInfinite(), storm::exceptions::NotSupportedException,
484 "There is no representation of " << value << " in the value type it would have to be narrowed to.");
485 return value.getFinite();
486 } else {
487 return value;
488 }
489}
490
495template<typename ValueType>
496std::vector<ExtendedValueType<ValueType>> widen(std::vector<ValueType>&& values) {
497 if constexpr (std::is_same_v<ExtendedValueType<ValueType>, ValueType>) {
498 return std::move(values);
499 } else {
500 std::vector<ExtendedValueType<ValueType>> result;
501 result.reserve(values.size());
502 for (auto& value : values) {
503 result.push_back(ExtendedValueType<ValueType>(std::move(value)));
504 }
505 return result;
506 }
507}
508
516template<typename ValueType>
517 requires(!std::is_reference_v<ValueType>)
520 "storm::utility::fromSentinel, which exists only for as long as parts of Storm still produce the sentinel that storm::utility::infinity yields.");
521 if (storm::utility::isInfinity(value)) {
523 }
524 // The constraint above rules out a reference type, so this forward is a move.
525 return std::forward<ValueType>(value);
526}
527
533template<typename ValueType>
534std::vector<ExtendedValueType<ValueType>> fromSentinel(std::vector<ValueType>&& values) {
536 "storm::utility::fromSentinel, which exists only for as long as parts of Storm still produce the sentinel that storm::utility::infinity yields.");
537 if constexpr (std::is_same_v<ExtendedValueType<ValueType>, ValueType>) {
538 return std::move(values);
539 } else {
540 std::vector<ExtendedValueType<ValueType>> result;
541 result.reserve(values.size());
542 ValueType const sentinel = storm::utility::infinity<ValueType>();
543 for (auto& value : values) {
544 if (value == sentinel) {
546 } else {
547 result.push_back(std::move(value));
548 }
549 }
550 return result;
551 }
552}
553
554template<typename ValueType>
557 "storm::utility::fromSentinel, which exists only for as long as parts of Storm still produce the sentinel that storm::utility::infinity yields.");
558 if (storm::utility::isInfinity(value)) {
560 }
561 return value;
562}
563
569template<typename ValueType>
572 "storm::utility::toSentinel, which exists only for as long as parts of Storm still expect the sentinel that storm::utility::infinity yields.");
574 if (value.isInfinite()) {
575 STORM_LOG_THROW(value.isPositiveInfinity(), storm::exceptions::NotSupportedException, "There is no representation of " << value << " here.");
577 }
578 return value.getFinite();
579 } else {
580 return value;
581 }
582}
583
589template<typename ValueType>
590 requires(detail::IsExtendedNumber<ValueType>::value)
591ValueType zero() {
593}
594
595template<typename ValueType>
596 requires(detail::IsExtendedNumber<ValueType>::value)
597ValueType one() {
599}
600
601template<typename ValueType>
602 requires(detail::IsExtendedNumber<ValueType>::value)
603ValueType infinity() {
604 return ValueType::infinity();
605}
606
611template<typename ValueType>
613 return number.isPositiveInfinity();
614}
615
620template<typename ValueType>
622 return number.isNegativeInfinity();
623}
624
625template<typename ValueType>
626 requires(!detail::IsExtendedNumber<ValueType>::value)
627bool isNegativeInfinity(ValueType const& value) {
629 return storm::utility::isInfinity(ValueType(-value));
630 } else {
631 return false;
632 }
633}
634
635template<typename ValueType>
637 return number.isFinite() && storm::utility::isZero(number.getFinite());
638}
639
640template<typename ValueType>
641bool isOne(ExtendedNumber<ValueType> const& number) {
642 return number.isFinite() && storm::utility::isOne(number.getFinite());
643}
644
645template<typename ValueType>
647 return number.isInfinite() || storm::utility::isConstant(number.getFinite());
648}
649
650template<typename ValueType>
657
663template<typename TargetType, typename SourceType>
664TargetType convertNumber(ExtendedNumber<SourceType> const& number) {
665 if (number.isFinite()) {
666 return TargetType(convertNumber<FiniteValueType<TargetType>, SourceType>(number.getFinite()));
667 }
669 return number.isPositiveInfinity() ? TargetType::infinity() : TargetType::negativeInfinity();
670 } else {
671 static_assert(storm::NumberTraits<TargetType>::HasInfinity, "Tried to convert an infinite value to a target type that cannot represent infinity.");
673 }
674}
675
680template<typename TargetType, typename SourceType>
681 requires(detail::IsExtendedNumber<TargetType>::value && !detail::IsExtendedNumber<SourceType>::value)
682TargetType convertNumber(SourceType const& number) {
684 // The source brings its own infinity, which the underlying conversion has no way of expressing.
685 if (storm::utility::isInfinity(number)) {
686 return TargetType::infinity();
687 }
688 if (storm::utility::isInfinity(SourceType(-number))) {
689 return TargetType::negativeInfinity();
690 }
691 }
692 return TargetType(convertNumber<FiniteValueType<TargetType>, SourceType>(number));
693}
694
695} // namespace storm::utility
696
697namespace storm {
703
709
714template<typename ValueType>
720} // namespace storm
721
722namespace std {
723template<typename ValueType>
724struct hash<storm::utility::ExtendedNumber<ValueType>> {
726 // The infinities have no payload to hash, so they get an arbitrary fixed value each.
727 if (number.isPositiveInfinity()) {
728 return 0x9e3779b9;
729 }
730 if (number.isNegativeInfinity()) {
731 return 0x85ebca6b;
732 }
733 return std::hash<ValueType>()(number.getFinite());
734 }
735};
736
742template<typename ValueType>
743struct numeric_limits<storm::utility::ExtendedNumber<ValueType>> {
745
746 static constexpr bool is_specialized = true;
747 static constexpr bool is_signed = true;
748 static constexpr bool is_integer = false;
750 static constexpr bool has_infinity = true;
751 static constexpr bool has_quiet_NaN = false;
752 static constexpr bool has_signaling_NaN = false;
753 static constexpr bool is_iec559 = false;
754 static constexpr bool is_bounded = false;
755
756 static type infinity() {
757 return type::infinity();
758 }
759
760 static type lowest() {
761 return type(std::numeric_limits<ValueType>::lowest());
762 }
763
764 static type min() {
765 return type(std::numeric_limits<ValueType>::min());
766 }
767
768 static type max() {
769 return type(std::numeric_limits<ValueType>::max());
770 }
771
772 static type epsilon() {
773 return type(std::numeric_limits<ValueType>::epsilon());
774 }
775};
776} // namespace std
A value of ValueType extended with the two infinities -infinity and +infinity.
friend ExtendedNumber operator+(ExtendedNumber const &first, ExtendedNumber const &second)
static ExtendedNumber negativeInfinity()
friend bool operator<(ValueType const &first, ExtendedNumber const &second)
friend bool operator==(ExtendedNumber const &first, ExtendedNumber const &second)
ExtendedNumber & operator+=(ExtendedNumber const &other)
friend bool operator<(ExtendedNumber const &first, ExtendedNumber const &second)
ExtendedNumber & operator=(ExtendedNumber &&)=default
friend bool operator!=(ExtendedNumber const &first, ValueType const &second)
ExtendedNumber(ExtendedNumber const &)=default
friend std::ostream & operator<<(std::ostream &out, ExtendedNumber const &number)
ExtendedNumber(ValueType &&value)
ExtendedNumber & operator-=(ExtendedNumber const &other)
friend bool operator<=(ValueType const &first, ExtendedNumber const &second)
ExtendedNumber(ExtendedNumber &&)=default
friend bool operator>=(ExtendedNumber const &first, ExtendedNumber const &second)
friend bool operator!=(ValueType const &first, ExtendedNumber const &second)
ValueType const & getFinite() const
friend bool operator>(ExtendedNumber const &first, ValueType const &second)
friend bool operator>=(ValueType const &first, ExtendedNumber const &second)
friend bool operator>=(ExtendedNumber const &first, ValueType const &second)
ExtendedNumber & operator=(ExtendedNumber const &)=default
ExtendedNumber(ValueType const &value)
Creates a finite value.
ExtendedNumber()
Creates a finite value that is zero.
friend bool operator>(ExtendedNumber const &first, ExtendedNumber const &second)
friend ExtendedNumber operator-(ExtendedNumber const &first, ExtendedNumber const &second)
ExtendedNumber operator-() const
ExtendedNumber const & operator+() const
friend bool operator<=(ExtendedNumber const &first, ExtendedNumber const &second)
friend ExtendedNumber operator/(ExtendedNumber const &first, ExtendedNumber const &second)
friend bool operator!=(ExtendedNumber const &first, ExtendedNumber const &second)
friend bool operator<=(ExtendedNumber const &first, ValueType const &second)
friend bool operator<(ExtendedNumber const &first, ValueType const &second)
friend bool operator==(ExtendedNumber const &first, ValueType const &second)
The comparisons against a plain value.
friend bool operator==(ValueType const &first, ExtendedNumber const &second)
ExtendedNumber & operator*=(ExtendedNumber const &other)
ExtendedNumber & operator/=(ExtendedNumber const &other)
friend bool operator>(ValueType const &first, ExtendedNumber const &second)
friend ExtendedNumber operator*(ExtendedNumber const &first, ExtendedNumber const &second)
static ExtendedNumber infinity()
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
#define STORM_LOG_DEPRECATED(message)
Definition macros.h:24
SFTBDDChecker::ValueType ValueType
ExtendedValueType< ValueType > positiveInfinity()
bool isOne(ValueType const &a)
Definition constants.cpp:39
bool isFinite(ExtendedNumber< ValueType > const &value)
bool isConstant(ValueType const &)
ExtendedValueType< ValueType > fromSentinel(ValueType &&value)
Recognises the value that storm::utility::infinity still yields for the value types that have no infi...
std::vector< ExtendedValueType< ValueType > > widen(std::vector< ValueType > &&values)
Widens a vector of finite values into the extended value type.
ExtendedValueType< ValueType > negativeInfinity()
bool isNegativeInfinity(ExtendedNumber< ValueType > const &number)
bool isZero(ValueType const &a)
Definition constants.cpp:44
ValueType abs(ValueType const &number)
ValueType zero()
Definition constants.cpp:26
ValueType infinity()
Definition constants.cpp:31
std::conditional_t< storm::NumberTraits< ValueType >::HasInfinity, ValueType, ExtendedNumber< ValueType > > ExtendedValueType
The type to use for values that may be infinite.
bool isNan(ValueType const &)
Definition constants.cpp:49
ValueType narrow(ExtendedValueType< ValueType > const &value)
Narrows an extended value back into the plain value type, for the interfaces that cannot hold an infi...
std::vector< ValueType > narrowFinite(std::vector< ExtendedValueType< ValueType > > &&values)
The counterpart of widen for a vector all of whose values are finite.
ValueType toSentinel(ExtendedValueType< ValueType > const &value)
The counterpart of fromSentinel: hands an extended value back to a part of Storm that still expects t...
ValueType one()
Definition constants.cpp:21
typename detail::FiniteValueType< ValueType >::type FiniteValueType
The finite value type underlying a (possibly extended) type.
bool isInfinity(ValueType const &a)
ValueType const & getFinite(ExtendedNumber< ValueType > const &value)
TargetType convertNumber(SourceType const &number)
storm::utility::ExtendedValueType< RationalNumber > ExtendedRationalNumber
The rational number type extended with the two infinities.
storm::utility::ExtendedValueType< RationalFunction > ExtendedRationalFunction
The rational function type extended with the two infinities.
size_t operator()(storm::utility::ExtendedNumber< ValueType > const &number) const
static const bool SupportsExponential
static const bool HasInfinity
Whether the type can represent +/- infinity itself.
static const bool IsExact
The finite value type underlying a (possibly extended) type.