From 95dba437d720d6f01ce53733264066a6f18055da Mon Sep 17 00:00:00 2001
From: Dirk Petrautzki <dirk@dirk-petrautzki.de>
Date: Fri, 4 Sep 2020 11:01:45 +0200
Subject: [PATCH] Add support for casting UFixed to SFixed and vice versa

---
 README.md                               |  4 ++--
 src/FixedPoints/SFixed.h                |  5 +++++
 src/FixedPoints/SFixedMemberFunctions.h | 25 ++++++++++++++++++++++---
 src/FixedPoints/UFixed.h                |  5 +++++
 src/FixedPoints/UFixedMemberFunctions.h | 19 +++++++++++++++++++
 5 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index d6173af..8093173 100644
--- a/README.md
+++ b/README.md
@@ -176,11 +176,11 @@ This means that you may write code such as `UFixed<8, 8> value = 0.5;` without i
 - `double`.
 - The smallest unsigned type capable of holding its integer part. I.e. a type of at least `I` bits.
 - Another `UFixed` type of a different scale. E.g. `UFixed<4, 4>` may be converted to `UFixed<8, 8>` and vice versa.
+- Another `SFixed` type of a different scale. E.g. `UFixed<4, 4>` may be converted to `SFixed<7, 8>` and vice versa.
 
 `SFixed<I, F>` is explicitly convertible to:
 - `float`.
 - `double`.
 - The smallest signed type capable of holding its integer part. I.e. a type of at least `I + 1` bits.
 - Another `SFixed` type of a different scale. E.g. `SFixed<3, 4>` may be converted to `SFixed<7, 8>` and vice versa.
-
-
+- Another `UFixed` type of a different scale. E.g. `SFixed<3, 4>` may be converted to `UFixed<8, 8>` and vice versa.
diff --git a/src/FixedPoints/SFixed.h b/src/FixedPoints/SFixed.h
index b2aa96d..1d9fab7 100644
--- a/src/FixedPoints/SFixed.h
+++ b/src/FixedPoints/SFixed.h
@@ -22,6 +22,9 @@ FIXED_POINTS_BEGIN_NAMESPACE
 // Declaration
 //
 
+template< unsigned IntegerOut, unsigned FractionOut >
+class UFixed; // forward declaration of UFixed for casting between UFixed and SFixed
+
 template< unsigned Integer, unsigned Fraction >
 class SFixed
 {
@@ -102,6 +105,8 @@ public:
 
 	template< unsigned IntegerOut, unsigned FractionOut >
 	constexpr explicit operator SFixed<IntegerOut, FractionOut>() const;
+	template< unsigned IntegerOut, unsigned FractionOut >
+	constexpr explicit operator UFixed<IntegerOut, FractionOut>() const;
 
 	static constexpr SFixed fromInternal(const InternalType & value);
 
diff --git a/src/FixedPoints/SFixedMemberFunctions.h b/src/FixedPoints/SFixedMemberFunctions.h
index e2ab090..43b167f 100644
--- a/src/FixedPoints/SFixedMemberFunctions.h
+++ b/src/FixedPoints/SFixedMemberFunctions.h
@@ -182,14 +182,33 @@ constexpr SFixed<Integer, Fraction>::operator long double() const
 template< unsigned Integer, unsigned Fraction >
 template< unsigned IntegerOut, unsigned FractionOut >
 constexpr SFixed<Integer, Fraction>::operator SFixed<IntegerOut, FractionOut>() const
-{	
+{
 	using OutputType = SFixed<IntegerOut, FractionOut>;
 	using OutputInternalType = typename OutputType::InternalType;
 	using OutputShiftType = typename OutputType::ShiftType;
-	
+
+	using InputType = SFixed<Integer, Fraction>;
+	using InputShiftType = typename InputType::ShiftType;
+
+	return
+	(FractionOut > FractionSize) ?
+		OutputType::fromInternal(static_cast<OutputInternalType>(static_cast<OutputShiftType>(this->value) << ((FractionOut > FractionSize) ? (FractionOut - FractionSize) : 0))) :
+	(FractionSize > FractionOut) ?
+		OutputType::fromInternal(static_cast<OutputInternalType>(static_cast<InputShiftType>(this->value) >> ((FractionSize > FractionOut) ? (FractionSize - FractionOut) : 0))) :
+		OutputType::fromInternal(this->value);
+}
+
+template< unsigned Integer, unsigned Fraction >
+template< unsigned IntegerOut, unsigned FractionOut >
+constexpr SFixed<Integer, Fraction>::operator UFixed<IntegerOut, FractionOut>() const
+{
+	using OutputType = UFixed<IntegerOut, FractionOut>;
+	using OutputInternalType = typename OutputType::InternalType;
+	using OutputShiftType = typename OutputType::ShiftType;
+
 	using InputType = SFixed<Integer, Fraction>;
 	using InputShiftType = typename InputType::ShiftType;
-	
+
 	return
 	(FractionOut > FractionSize) ?
 		OutputType::fromInternal(static_cast<OutputInternalType>(static_cast<OutputShiftType>(this->value) << ((FractionOut > FractionSize) ? (FractionOut - FractionSize) : 0))) :
diff --git a/src/FixedPoints/UFixed.h b/src/FixedPoints/UFixed.h
index 64079da..7c64a80 100644
--- a/src/FixedPoints/UFixed.h
+++ b/src/FixedPoints/UFixed.h
@@ -22,6 +22,9 @@ FIXED_POINTS_BEGIN_NAMESPACE
 // Declaration
 //
 
+template< unsigned IntegerOut, unsigned FractionOut >
+class SFixed; // forward declaration of SFixed for casting between SFixed and UFixed
+
 template< unsigned Integer, unsigned Fraction >
 class UFixed
 {
@@ -103,6 +106,8 @@ public:
 
 	template< unsigned IntegerOut, unsigned FractionOut >
 	constexpr explicit operator UFixed<IntegerOut, FractionOut>() const;
+	template< unsigned IntegerOut, unsigned FractionOut >
+	constexpr explicit operator SFixed<IntegerOut, FractionOut>() const;
 
 	static constexpr UFixed fromInternal(const InternalType & value);
 
diff --git a/src/FixedPoints/UFixedMemberFunctions.h b/src/FixedPoints/UFixedMemberFunctions.h
index 9f7ddd0..c55cde2 100644
--- a/src/FixedPoints/UFixedMemberFunctions.h
+++ b/src/FixedPoints/UFixedMemberFunctions.h
@@ -189,6 +189,25 @@ constexpr UFixed<Integer, Fraction>::operator UFixed<IntegerOut, FractionOut>()
 		OutputType::fromInternal(this->value);
 }
 
+template< unsigned Integer, unsigned Fraction >
+template< unsigned IntegerOut, unsigned FractionOut >
+constexpr UFixed<Integer, Fraction>::operator SFixed<IntegerOut, FractionOut>() const
+{
+	using OutputType = SFixed<IntegerOut, FractionOut>;
+	using OutputInternalType = typename OutputType::InternalType;
+	using OutputShiftType = typename OutputType::ShiftType;
+
+	using InputType = UFixed<Integer, Fraction>;
+	using InputShiftType = typename InputType::ShiftType;
+
+	return
+	(FractionOut > FractionSize) ?
+		OutputType::fromInternal(static_cast<OutputInternalType>(static_cast<OutputShiftType>(this->value) << ((FractionOut > FractionSize) ? (FractionOut - FractionSize) : 0))) :
+	(FractionSize > FractionOut) ?
+		OutputType::fromInternal(static_cast<OutputInternalType>(static_cast<InputShiftType>(this->value) >> ((FractionSize > FractionOut) ? (FractionSize - FractionOut) : 0))) :
+		OutputType::fromInternal(this->value);
+}
+
 //
 // Static Functions
 //