From 393acd2f49f66bfbb2a1b70def0258b5e3ce0b5d Mon Sep 17 00:00:00 2001 From: Pharap Date: Sun, 1 Oct 2017 04:22:25 +0100 Subject: [PATCH] Add files via upload --- LICENCE | 201 ++++++++++++ README.md | 149 ++++++++- .../FixedPointCalculations.ino | 170 ++++++++++ extras/AFixedPointPrimer.md | 69 ++++ extras/Credits.md | 14 + extras/Hello.txt | 8 + extras/Important.txt | 6 + keywords.txt | 47 +++ library.json | 19 ++ library.properties | 10 + src/FixedPoints.h | 1 + src/FixedPoints/Details.h | 219 +++++++++++++ src/FixedPoints/FixedPoints.h | 9 + src/FixedPoints/SFixed.h | 205 ++++++++++++ src/FixedPoints/SFixedBase.h | 64 ++++ src/FixedPoints/SFixedFreeFunctions.h | 297 ++++++++++++++++++ src/FixedPoints/SFixedMemberFunctions.h | 175 +++++++++++ src/FixedPoints/UFixed.h | 205 ++++++++++++ src/FixedPoints/UFixedBase.h | 64 ++++ src/FixedPoints/UFixedFreeFunctions.h | 297 ++++++++++++++++++ src/FixedPoints/UFixedMemberFunctions.h | 155 +++++++++ src/FixedPoints/Utils.h | 233 ++++++++++++++ src/FixedPointsCommon.h | 1 + src/FixedPointsCommon/FixedPointsCommon.h | 4 + src/FixedPointsCommon/SFixedCommon.h | 15 + src/FixedPointsCommon/UFixedCommon.h | 15 + 26 files changed, 2650 insertions(+), 2 deletions(-) create mode 100644 LICENCE create mode 100644 examples/FixedPointCalculations/FixedPointCalculations.ino create mode 100644 extras/AFixedPointPrimer.md create mode 100644 extras/Credits.md create mode 100644 extras/Hello.txt create mode 100644 extras/Important.txt create mode 100644 keywords.txt create mode 100644 library.json create mode 100644 library.properties create mode 100644 src/FixedPoints.h create mode 100644 src/FixedPoints/Details.h create mode 100644 src/FixedPoints/FixedPoints.h create mode 100644 src/FixedPoints/SFixed.h create mode 100644 src/FixedPoints/SFixedBase.h create mode 100644 src/FixedPoints/SFixedFreeFunctions.h create mode 100644 src/FixedPoints/SFixedMemberFunctions.h create mode 100644 src/FixedPoints/UFixed.h create mode 100644 src/FixedPoints/UFixedBase.h create mode 100644 src/FixedPoints/UFixedFreeFunctions.h create mode 100644 src/FixedPoints/UFixedMemberFunctions.h create mode 100644 src/FixedPoints/Utils.h create mode 100644 src/FixedPointsCommon.h create mode 100644 src/FixedPointsCommon/FixedPointsCommon.h create mode 100644 src/FixedPointsCommon/SFixedCommon.h create mode 100644 src/FixedPointsCommon/UFixedCommon.h diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 37c9d4e..6c1fbf9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,147 @@ -# FixedPointsArduino -A Fixed Points Library For Arduino +# FixedPoints +A portable fixed point arithmetic library. + +Some knowledge of how fixed point types are formatted is required to used this library to full effect. +No knowledge of how these operations are implemented is required to use them. + +This library was written with Arduino in mind, as well as CPUs with limited floating point support. +However, given the templated nature of the library, it should still function on a wide variety of CPUs. + +## Requirements: + +- The Compiler must be C++11 compliant. +- The user should ideally be familar with the [Q number format](https://en.wikipedia.org/wiki/Q_(number_format)) for fixed points. + +## Licence + +This code uses the Apache 2.0 Licence. +This means: + +- This code comes with no warranty. +- The licensor and any contributors cannot be held liable for damages. +- If you use this code, edited or unedited, you must: + - Package a copy of LICENCE with your code. + - Package a copy of NOTICE with your code. +- You may modify the source code. + - If you modify a file, you must state this prominently in the file (i.e. in a comment at the top). + - Any modified code may use a different licence. + - Any unmodified code must carry the same Apache 2.0 licence. +- You may not use any trademarks owned by the licensor. + +## Contents +This library supplies two core types and sixteen type aliases. + +### Core Types: +The core types are provided by `FixedPoints.h`. + +- `UFixed`: An unsigned fixed point type where I is the number of bits used for the integer part of the number and F is the number of bits used for the fractional part of the number. +- `SFixed`: An signed fixed point type where I is the number of bits used for the integer part of the number (excluding the implicit sign bit) and F is the number of bits used for the fractional part of the number. + +### Aliases: +The common aliases are provided by `FixedPointsCommon.h`. + +- `UQ4x4`: An alias for `UFixed<4, 4>`, an 8-bit unsigned fixed point in the Q4.4 format. +- `UQ8x8`: An alias for `UFixed<8, 8>`, a 16-bit unsigned fixed point in the Q8.8 format. +- `UQ16x16`: An alias for `UFixed<16, 16>`, a 32-bit unsigned fixed point in the Q16.16 format. +- `UQ32x32`: An alias for `UFixed<32, 32>`, a 64-bit unsigned fixed point in the Q32.32 format. +- `UQ1x7`: An alias for `UFixed<1, 7>`, an 8-bit unsigned fixed point in the Q1.7 format. +- `UQ1x15`: An alias for `UFixed<1, 15>`, a 16-bit unsigned fixed point in the Q1.15 format. +- `UQ1x31`: An alias for `UFixed<1, 31>`, a 32-bit unsigned fixed point in the Q1.31 format. +- `UQ1x63`: An alias for `UFixed<1, 63>`, a 64-bit unsigned fixed point in the Q1.63 format. +- `SQ3x4`: An alias for `SFixed<3, 4>`, an 8-bit signed fixed point in the Q3.4 format with implicit sign bit. +- `SQ7x8`: An alias for `SFixed<7, 8>`, a 16-bit signed fixed point in the Q7.8 format with implicit sign bit. +- `SQ15x16`: An alias for `SFixed<15, 16>`, a 32-bit signed fixed point in the Q15.16 format with implicit sign bit. +- `SQ31x32`: An alias for `SFixed<31, 32>`, a 64-bit signed fixed point in the Q31.32 format with implicit sign bit. +- `SQ1x6`: An alias for `SFixed<1, 6>`, an 8-bit signed fixed point in the Q1.6 format with implicit sign bit. +- `SQ1x14`: An alias for `SFixed<1, 14>`, a 16-bit signed fixed point in the Q1.14 format with implicit sign bit. +- `SQ1x30`: An alias for `SFixed<1, 30>`, a 32-bit signed fixed point in the Q1.30 format with implicit sign bit. +- `SQ1x62`: An alias for `SFixed<1, 62>`, a 64-bit signed fixed point in the Q1.62 format with implicit sign bit. + +([About Q Format](https://en.wikipedia.org/wiki/Q_(number_format)).) + +### Operators: + +- `+`: Adds two `UFixed`s or two `SFixed`s +- `-`: Subtracts two `UFixed`s or two `SFixed`s +- `*`: Multiplies two `UFixed`s or two `SFixed`s +- `/`: Divides two `UFixed`s or two `SFixed`s +- `==`: Compares two `UFixed`s or two `SFixed`s +- `!=`: Compares two `UFixed`s or two `SFixed`s +- `<`: Compares two `UFixed`s or two `SFixed`s +- `<=`: Compares two `UFixed`s or two `SFixed`s +- `>`: Compares two `UFixed`s or two `SFixed`s +- `>=`: Compares two `UFixed`s or two `SFixed`s + +### Free Functions: + +- `floorFixed`: The floor operation. +- `ceilFixed`: The Ceiling operation +- `roundFixed`: Rounding operation. +- `truncFixed`: Truncation operation. +- `signbitFixed`: Returns `true` for signed numbers and `false` for unsigned numbers. +- `copysignFixed`: Returns a value with the magnitude of the first argument and the sign of the second argument. +- `multiply`: Multiplies two `UFixed`s or two `SFixed`s, returns a result that is twice the resolution of the input. + +### Member Functions: + +- `UFixed::getInteger`: Gets the integer part of an unsigned fixed point. +- `UFixed::getFraction`: Gets the fractional part of an unsigned fixed point. +- `UFixed::getInternal`: Gets the internal representation of an unsigned fixed point. + +- `SFixed::getInteger`: Gets the integer part of a signed fixed point. +- `SFixed::getFraction`: Gets the fractional part of a signed fixed point. +- `SFixed::getInternal`: Gets the internal representation of a signed fixed point. + +### Static Functions: + +- `UFixed::fromInternal`: Produces an unsigned fixed point number from its internal representation. +- `SFixed::fromInternal`: Produces a signed fixed point number from its internal representation. + +## Construction: + +Note that both `UFixed` and `SFixed` are implicitly compile-time constructable from all integer and decimal literals. +This means that you may write code such as `UFixed<8, 8> value = 0.5;` without incurring a runtime cost for converting from `double` to `UFixed<8, 8>` because the constructor is `constexpr`. + +`UFixed` is constructable from: +- Any integer literal type, regardless of sign. +-- This constructs the fixed point as an integer with no fractional part. +-- A value that does not fit shall be truncated without warning. +-- If a constant value is used, the fixed point shall be constructed at compile time. +- An unsigned integer part and an unsigned fractional part. +-- The integer part is of the smallest type capable of representing `I` bits. +-- The fractional part is of the smallest type capable of representing `F` bits. +-- If constant values are used, the fixed point shall be constructed at compile time. +- Any decimal literal type, regardless of sign. +-- This constructs the fixed point as a best approximation of the provided value. +-- A value that does not fit shall be truncated without warning. +-- If a constant value is used, the fixed point shall be constructed at compile time. + +`SFixed` is constructable from: +- Any integer literal type, regardless of sign. +-- This constructs the fixed point as an integer with no fractional part. +-- A value that does not fit shall be truncated without warning. +-- If a constant value is used, the fixed point shall be constructed at compile time. +- A signed integer part and an unsigned fractional part. +-- The integer part is of the smallest type capable of representing `I + 1` bits. +-- The fractional part is of the smallest type capable of representing `F` bits. +-- If constant values are used, the fixed point shall be constructed at compile time. +- Any decimal literal type, regardless of sign. +-- This constructs the fixed point as a best approximation of the provided value. +-- A value that does not fit shall be truncated without warning. +-- If a constant value is used, the fixed point shall be constructed at compile time. + +### Casts: + +`UFixed` is explicitly convertible to: +- `float`. +- `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. + +`SFixed` 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. + + diff --git a/examples/FixedPointCalculations/FixedPointCalculations.ino b/examples/FixedPointCalculations/FixedPointCalculations.ino new file mode 100644 index 0000000..3b7bdf9 --- /dev/null +++ b/examples/FixedPointCalculations/FixedPointCalculations.ino @@ -0,0 +1,170 @@ +#include +#include + +void TestUQ8x8(void) +{ + Serial.println(F("The size of UQ8x8 on your system is:")); + Serial.println(sizeof(UQ8x8)); + Serial.println(); + + UQ8x8 a = 1.5; + Serial.println(F("Displaying a as float:")); + Serial.println(static_cast(a)); + Serial.println(); + + Serial.println(F("Displaying the integer part of a")); + Serial.println(a.getInteger()); + Serial.println(); + + Serial.println(F("Displaying floorFixed(a):")); + Serial.println(static_cast(floorFixed(a))); + Serial.println(); + + Serial.println(F("Displaying ceilFixed(a):")); + Serial.println(static_cast(ceilFixed(a))); + Serial.println(); + + Serial.println(F("Displaying roundFixed(a):")); + Serial.println(static_cast(roundFixed(a))); + Serial.println(); + + Serial.println(F("Displaying truncFixed(a):")); + Serial.println(static_cast(truncFixed(a))); + Serial.println(); + + UQ8x8 b = 5.25; + Serial.println(F("Displaying b as float:")); + Serial.println(static_cast(b)); + Serial.println(); + + Serial.println(F("Displaying the integer part of b")); + Serial.println(b.getInteger()); + Serial.println(); + + Serial.println(F("Displaying floorFixed(b):")); + Serial.println(static_cast(floorFixed(b))); + Serial.println(); + + Serial.println(F("Displaying ceilFixed(b):")); + Serial.println(static_cast(ceilFixed(b))); + Serial.println(); + + Serial.println(F("Displaying roundFixed(b):")); + Serial.println(static_cast(roundFixed(b))); + Serial.println(); + + Serial.println(F("Displaying truncFixed(b):")); + Serial.println(static_cast(truncFixed(b))); + Serial.println(); + + Serial.println(F("Displaying a + b as float:")); + Serial.println(static_cast(a + b)); + Serial.println(); + + Serial.println(F("Displaying a - b as float:")); + Serial.println(F("(Note the underflow due lack of sign bit)")); + Serial.println(static_cast(a - b)); + Serial.println(); + + Serial.println(F("Displaying b - a as float:")); + Serial.println(static_cast(b - a)); + Serial.println(); + + Serial.println(F("Displaying a * b as float:")); + Serial.println(static_cast(a * b)); + Serial.println(); + + Serial.println(F("Displaying a / b as float:")); + Serial.println(static_cast(a / b)); + Serial.println(); +} + +void TestSQ7x8(void) +{ + Serial.println(F("The size of SQ7x8 on your system is:")); + Serial.println(sizeof(SQ7x8)); + Serial.println(); + + SQ7x8 a = 1.5; + Serial.println(F("Displaying a as float:")); + Serial.println(static_cast(a)); + Serial.println(); + + Serial.println(F("Displaying the integer part of a")); + Serial.println(a.getInteger()); + Serial.println(); + + Serial.println(F("Displaying floorFixed(a):")); + Serial.println(static_cast(floorFixed(a))); + Serial.println(); + + Serial.println(F("Displaying ceilFixed(a):")); + Serial.println(static_cast(ceilFixed(a))); + Serial.println(); + + Serial.println(F("Displaying roundFixed(a):")); + Serial.println(static_cast(roundFixed(a))); + Serial.println(); + + Serial.println(F("Displaying truncFixed(a):")); + Serial.println(static_cast(truncFixed(a))); + Serial.println(); + + SQ7x8 b = 5.25; + Serial.println(F("Displaying b as float:")); + Serial.println(static_cast(b)); + Serial.println(); + + Serial.println(F("Displaying the integer part of b")); + Serial.println(b.getInteger()); + Serial.println(); + + Serial.println(F("Displaying floorFixed(b):")); + Serial.println(static_cast(floorFixed(b))); + Serial.println(); + + Serial.println(F("Displaying ceilFixed(b):")); + Serial.println(static_cast(ceilFixed(b))); + Serial.println(); + + Serial.println(F("Displaying roundFixed(b):")); + Serial.println(static_cast(roundFixed(b))); + Serial.println(); + + Serial.println(F("Displaying truncFixed(b):")); + Serial.println(static_cast(truncFixed(b))); + Serial.println(); + + Serial.println(F("Displaying a + b as float:")); + Serial.println(static_cast(a + b)); + Serial.println(); + + Serial.println(F("Displaying a - b as float:")); + Serial.println(F("(Note this is correct due to sign bit)")); + Serial.println(static_cast(a - b)); + Serial.println(); + + Serial.println(F("Displaying b - a as float:")); + Serial.println(static_cast(b - a)); + Serial.println(); + + Serial.println(F("Displaying a * b as float:")); + Serial.println(static_cast(a * b)); + Serial.println(); + + Serial.println(F("Displaying a / b as float:")); + Serial.println(static_cast(a / b)); + Serial.println(); +} + +void setup() +{ + while(!Serial); + + TestUQ8x8(); + TestSQ7x8(); +} + +void loop() +{ +} diff --git a/extras/AFixedPointPrimer.md b/extras/AFixedPointPrimer.md new file mode 100644 index 0000000..3413465 --- /dev/null +++ b/extras/AFixedPointPrimer.md @@ -0,0 +1,69 @@ +# A Fixed Point Primer + +## What Are Fixed Point Numbers? + +Normally computers operate on what are called 'integers'. +Integers are numbers that do not have a fractional component in them. +For example 0, 1, 42, 128, 10000 are all integers + +Numbers that do have a fractional component are called 'rational' numbers. +They are often represented either as fractions or as numbers with a 'radix point'in them (often called a 'decimal point' in day-to-day maths) +For example 0.5, 1.75, 42.42, 128.367 and 10000.00001 are all rational numbers. + +Normally computers represent these in what is called a 'floating point' format. +The name 'floating point' refers to the fact that the radix point can change position within the number, i.e. the radix point can 'float' around. + +Another way to represent real numbers is what is called a 'fixed point' format. +The name 'fixed point' refers to the fact that the radix point does not change position within the number, i.e. it is 'fixed' in place. +This means that the range of numbers that can be represented by a fixed point is much more limited in terms of scale (how large or small the numbers can be). + +## Why Use Fixed Point Numbers? + +To allow the ability to move the radix point, the most common floating point formats are quite complicated and typically need special hardware or lots of complicated functions to implement them. + +Almost all modern CPUs have floating point capabilities built into them. +However many CPUs intended for use in embedded systems do not have this capability built in because it is not always needed and it is often cheaper to leave it out. + +This is where fixed points come in. + +By limiting the format so that the radix point is fixed in place, the implementation of fixed point numbers becomes a lot simpler. + +In fact for the most basic operations, addition and subtraction, the operations are exactly the same as they are for integers. Some of the more complicated operations like multiplication and division are very similar and merely involve a bit of bit shifting to adjust the results. + +This means that even the simplest of CPUs can use fixed point numbers without any special hardware support. As long as they support integer and bit shifting operations, they can support fixed point numbers. + +## Why Aren't Fixed Point Numbers Used More Often? + +Fixed point numbers have fallen out of favour because modern CPUs tend to come with floating point support as standard. The wide range of support for floating points and the sheer dominance of the IEEE 754 floating point format means that for most cases using floating points to perform operations on real numbers is preferred. + +Even if a particular application could be sped up by switching to fixed points, there are many reasons why people tend not to choose this option, not least because of the lack of awareness of fixed point numbers and because of the lack of available fixed point libraries. + +Indeed, finding information about fixed point numbers can be quite a struggle. + +## How Do Fixed Points Work? + +Fixed point numbers are comprised of two parts, the integer part and the fractional part. +Each part takes up a set number of bits. For example, a common format (often called `Q8.8`) has `8` bits for the integer part and 8 bits for the fractional part. + +The integer part stores the digits to the left of the radix point. +This part behaves exactly like a regular integer and for all intents and purposes can be imagined as one. If you understand how computers represent integers, you already understand the integer part. + +For example, in the `Q8.8` format, there are `8` bits in the integer part, thus it is capable of holding `256` values. The values are `0` to `255` when unsigned or `-128` to `127` when signed and in two's complement format. + +The fractional part stores the digits to the right of the radix point. +This part has special behaviour that can be a bit tricky to comprehend at first. +The simplest way to think of it is as a fraction. + +For example in the `Q8.8` format, there are `8` bits for the fractional part, which means it is capable of holding `256` values. +When thought of as a fraction, the fractional part is treated as an unsigned integer, meaning it can hold values of `0` to `255`. This integer is then imagined as the numerator of a fraction in which the denominator is the number of possible values that can be held, i.e. `256`. + +If the fractional part had the integer value of `64`, then its effective value can be represented as `64/256`, which would be equivalent to the decimal value of `0.25`. + +As a more in depth example, consider a Q8.8 value where the integer part is `5` and the fractional part has an integer value of `37`. This can be thought of as a mixed fraction `5 37/256`. +Using a calculator to divide `37` by `256` you will find that `37/256 = 0.14453125`, which means that the number being represented is `5.14453125`. + +## Further Reading + +- Wikipedia's article about [Radix Points](https://en.wikipedia.org/wiki/Radix_point) +- Wikipedia's article about [Integers](https://en.wikipedia.org/wiki/Integer) +- Wikipedia's article about [Rational Numbers](https://en.wikipedia.org/wiki/Rational_number) diff --git a/extras/Credits.md b/extras/Credits.md new file mode 100644 index 0000000..5bf721f --- /dev/null +++ b/extras/Credits.md @@ -0,0 +1,14 @@ +## Main Credits: + +- [Pharap](https://github.com/Pharap) - main programmer + +## Extra special thanks to: + +- [bakagamedev](https://github.com/bakagamedev) - extra-handy duck +- [filmote](https://github.com/filmote) - testing and debugging +- [eried](https://github.com/eried) - testing and debugging + +## Special mentions: + +- crait +- Bergasms diff --git a/extras/Hello.txt b/extras/Hello.txt new file mode 100644 index 0000000..189a7ee --- /dev/null +++ b/extras/Hello.txt @@ -0,0 +1,8 @@ + + /\___/\ ~ + ( O . O ) \\ + > ^ < // + ( \ / )// + u U U u + + ヘロー エブリニャン! diff --git a/extras/Important.txt b/extras/Important.txt new file mode 100644 index 0000000..702d4cd --- /dev/null +++ b/extras/Important.txt @@ -0,0 +1,6 @@ +Do not attempt to use anything from the Details namespace. +Everything in the Details namespace is merely an implementation detail which may be changed without notice, +thus any attempt to use anything from the Details namespace may result in your own code breaking in future updates. + +To wrap the fixed point types in the FixedPoints namespace (e.g. in case of name conflicts): +#define FIXED_POINTS_USE_NAMESPACE \ No newline at end of file diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..e713eaa --- /dev/null +++ b/keywords.txt @@ -0,0 +1,47 @@ + +#################################### +# Syntax Colouring For FixedPoints # +#################################### + +#################################### +# Datatypes (KEYWORD1) # +#################################### + +SFixed KEYWORD1 +UFixed KEYWORD1 +UQ4x4 KEYWORD1 +UQ8x8 KEYWORD1 +UQ16x16 KEYWORD1 +UQ32x32 KEYWORD1 +UQ1x7 KEYWORD1 +UQ1x15 KEYWORD1 +UQ1x31 KEYWORD1 +UQ1x63 KEYWORD1 +SQ3x4 KEYWORD1 +SQ7x8 KEYWORD1 +SQ15x16 KEYWORD1 +SQ31x32 KEYWORD1 +SQ1x6 KEYWORD1 +SQ1x14 KEYWORD1 +SQ1x30 KEYWORD1 +SQ1x62 KEYWORD1 + +#################################### +# Methods and Functions (KEYWORD2) # +#################################### + +getInternal KEYWORD2 +getInteger KEYWORD2 +getFraction KEYWORD2 +fromInternal KEYWORD2 +multiply KEYWORD2 +floorFixed KEYWORD2 +ceilFixed KEYWORD2 +roundFixed KEYWORD2 +truncFixed KEYWORD2 +signbitFixed KEYWORD2 +absFixed KEYWORD2 +copysignFixed KEYWORD2 +nextafterFixed KEYWORD2 +randomUFixed KEYWORD2 +randomSFixed KEYWORD2 diff --git a/library.json b/library.json new file mode 100644 index 0000000..79f10f7 --- /dev/null +++ b/library.json @@ -0,0 +1,19 @@ +{ + "name": "FixedPoints", + "keywords": "maths, fixed, point, fixed point, arithmetic", + "description": "A portable templated header-only fixed point arithmetic library", + "repository": { + "type": "git", + "url": "https://github.com/Pharap/FixedPointsArduino.git" + }, + "version": "1.0.0", + "authors": { + "name": "Pharap", + "url": "https://github.com/Pharap" + }, + "exclude": [ + "tests", + ], + "frameworks": "arduino", + "platforms": "*" +} \ No newline at end of file diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..3877e98 --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=FixedPoints +version=1.0.0 +author=Pharap +maintainer=Pharap +sentence=A template library for defining fixed point types of varying sizes. +paragraph=The library is designed to be generic so it should be applicable to almost all size requirements and processor architectures. +category=Data Processing +url=https://github.com/Pharap/FixedPointsArduino +architectures=* +includes=FixedPoints.h, FixedPointsCommon.h \ No newline at end of file diff --git a/src/FixedPoints.h b/src/FixedPoints.h new file mode 100644 index 0000000..a8dc35d --- /dev/null +++ b/src/FixedPoints.h @@ -0,0 +1 @@ +#include "FixedPoints/FixedPoints.h" \ No newline at end of file diff --git a/src/FixedPoints/Details.h b/src/FixedPoints/Details.h new file mode 100644 index 0000000..6574ca4 --- /dev/null +++ b/src/FixedPoints/Details.h @@ -0,0 +1,219 @@ +#pragma once + +#include +#include + +#if defined(FIXED_POINTS_USE_NAMESPACE) +#define FIXED_POINTS_NAMESPACE FixedPoints +#define FIXED_POINTS_BEGIN_NAMESPACE namespace FIXED_POINTS_NAMESPACE\ +{ +#define FIXED_POINTS_END_NAMESPACE } +#define FIXED_POINTS_DETAILS Details +#else +#define FIXED_POINTS_NAMESPACE +#define FIXED_POINTS_BEGIN_NAMESPACE +#define FIXED_POINTS_END_NAMESPACE +#define FIXED_POINTS_DETAILS FixedPointsDetails +#endif + +// Pay no attention to the man behind the curtains + +FIXED_POINTS_BEGIN_NAMESPACE +namespace FIXED_POINTS_DETAILS +{ + template< typename T > + struct BitSize + { + BitSize(void) = delete; + constexpr static const auto Value = sizeof(T) * CHAR_BIT; + }; + + template< typename T, typename U = T > + constexpr auto Max(const T & left, const U & right) noexcept -> decltype((left > right) ? left : right) + { + return (left > right) ? left : right; + } + + template< typename T, T Left, T Right > + constexpr auto Max(void) noexcept -> decltype((Left > Right) ? Left : Right) + { + return (Left > Right) ? Left : Right; + } + + template< typename T, typename U = T > + constexpr auto Min(const T & left, const U & right) noexcept -> decltype((left < right) ? left : right) + { + return (left < right) ? left : right; + } + + template< typename T, T Left, T Right > + constexpr auto Min(void) noexcept -> decltype((Left < Right) ? Left : Right) + { + return (Left < Right) ? Left : Right; + } + + template< bool Condition, typename TTrue, typename TFalse > + struct Conditional; + + template< typename TTrue, typename TFalse > + struct Conditional< true, TTrue, TFalse > { using Type = TTrue; }; + + template< typename TTrue, typename TFalse > + struct Conditional< false, TTrue, TFalse > { using Type = TFalse; }; + + template< bool Condition, typename TTrue, typename TFalse > + using ConditionalT = typename Conditional::Type; + + template< typename T, typename U > + using LargerType = ConditionalT<(BitSize::Value > BitSize::Value), T, U>; + + template< typename T, typename U > + using StrictLargerType = ConditionalT< (BitSize::Value > BitSize::Value), T, ConditionalT< (BitSize::Value > BitSize::Value), U, void > >; + + template< typename T, typename U > + using SmallerType = ConditionalT<(BitSize::Value < BitSize::Value), T, U>; + + template< typename T, typename U > + using StrictSmallerType = ConditionalT< (BitSize::Value < BitSize::Value), T, ConditionalT< (BitSize::Value < BitSize::Value), U, void > >; + + template< unsigned Bits, typename... Ts > + struct LeastTypeHelper; + + template< unsigned Bits, typename T, typename... Ts > + struct LeastTypeHelper + { + LeastTypeHelper(void) = delete; + using Type = ConditionalT<(Bits <= BitSize::Value), T, typename LeastTypeHelper::Type>; + }; + + template< unsigned Bits > + struct LeastTypeHelper + { + LeastTypeHelper(void) = delete; + using Type = void; + }; + + + template< unsigned Bits, typename... Ts > + using LeastType = typename LeastTypeHelper::Type; + + template< unsigned Bits > + struct LeastUIntDef + { + static_assert(Bits <= BitSize::Value, "No type large enough"); + LeastUIntDef(void) = delete; + using Type = LeastType; + }; + + + template< unsigned Bits > + using LeastUInt = typename LeastUIntDef::Type; + + template< unsigned Bits > + struct LeastIntDef + { + static_assert(Bits <= BitSize::Value, "No type large enough"); + LeastIntDef(void) = delete; + using Type = LeastType; + }; + + template< unsigned Bits > + using LeastInt = typename LeastIntDef::Type; + + template< unsigned Bits > + struct MsbMask + { + MsbMask(void) = delete; + constexpr const static LeastUInt Value = (1ull << (Bits - 1)); + }; + + template< unsigned Bits > + struct IdentityMask + { + IdentityMask(void) = delete; + constexpr const static LeastUInt Value = 1 | (IdentityMask::Value << 1); + }; + + template<> + struct IdentityMask<0> + { + IdentityMask(void) = delete; + constexpr const static LeastUInt<0> Value = 0; + }; + + using IntegerLiteral = decltype(0); + using IntegerLiteralU = decltype(0U); + using IntegerLiteralL = decltype(0L); + using IntegerLiteralUL = decltype(0UL); + using IntegerLiteralLL = decltype(0LL); + using IntegerLiteralULL = decltype(0ULL); + + using DecimalLiteral = decltype(0.0); + using DecimalLiteralF = decltype(0.0F); + using DecimalLiteralL = decltype(0.0L); + + template< typename T > + struct RandomHelper; + + template<> + struct RandomHelper + { + static inline uint8_t Random() { return static_cast(random()); } + }; + + template<> + struct RandomHelper + { + static inline uint16_t Random() { return static_cast(random()); } + }; + + template<> + struct RandomHelper + { + static inline uint32_t Random() { return static_cast(random()); } + }; + + template<> + struct RandomHelper + { + static inline uint64_t Random() { return (static_cast(random()) << 32) | static_cast(random()); } + }; + + template<> + struct RandomHelper + { + static inline int8_t Random() { return static_cast(random()); } + }; + + template<> + struct RandomHelper + { + static inline int16_t Random() { return static_cast(random()); } + }; + + template<> + struct RandomHelper + { + static inline int32_t Random() { return static_cast(random()); } + }; + + template<> + struct RandomHelper + { + static inline int64_t Random() { return (static_cast(random()) << 32) | static_cast(random()); } + }; + + // Here be dragons!! + // + // + // + // /\___/\ ~ + // ( O . O ) \\ + // > ^ < // + // ( \ / )// + // u U U u + // + // Or cats? + // ~Mwrow~ +} +FIXED_POINTS_END_NAMESPACE \ No newline at end of file diff --git a/src/FixedPoints/FixedPoints.h b/src/FixedPoints/FixedPoints.h new file mode 100644 index 0000000..97a1a6c --- /dev/null +++ b/src/FixedPoints/FixedPoints.h @@ -0,0 +1,9 @@ +#include "Details.h" + +#include "UFixedBase.h" +#include "SFixedBase.h" + +#include "UFixed.h" +#include "SFixed.h" + +#include "Utils.h" \ No newline at end of file diff --git a/src/FixedPoints/SFixed.h b/src/FixedPoints/SFixed.h new file mode 100644 index 0000000..3101d37 --- /dev/null +++ b/src/FixedPoints/SFixed.h @@ -0,0 +1,205 @@ +#pragma once + +#include "Details.h" +#include "SFixedBase.h" + +FIXED_POINTS_BEGIN_NAMESPACE + +// +// Declaration +// + +template< unsigned Integer, unsigned Fraction > +class SFixed : FIXED_POINTS_DETAILS::SFixedBase< Integer, Fraction > +{ +public: + static_assert(((Integer + 1) + Fraction) <= FIXED_POINTS_DETAILS::BitSize::Value, "Platform does not have a native type large enough for SFixed."); + +public: + using IntegerType = FIXED_POINTS_DETAILS::LeastInt; + using FractionType = FIXED_POINTS_DETAILS::LeastUInt; + using InternalType = FIXED_POINTS_DETAILS::LeastInt<(Integer + 1) + Fraction>; + + using ShiftType = FIXED_POINTS_DETAILS::LeastUInt<(Integer + 1) + Fraction>; + using MaskType = FIXED_POINTS_DETAILS::LeastUInt<(Integer + 1) + Fraction>; + + constexpr const static unsigned IntegerSize = Integer + 1; + constexpr const static unsigned FractionSize = Fraction; + constexpr const static unsigned LogicalSize = IntegerSize + FractionSize; + constexpr const static unsigned InternalSize = FIXED_POINTS_DETAILS::BitSize::Value; + + constexpr const static unsigned long long Scale = 1ULL << FractionSize; + +public: + constexpr const static ShiftType IntegerShift = FractionSize; + constexpr const static ShiftType FractionShift = 0; + + constexpr const static MaskType IntegerMask = FIXED_POINTS_DETAILS::IdentityMask::Value; + constexpr const static MaskType FractionMask = FIXED_POINTS_DETAILS::IdentityMask::Value; + + constexpr const static MaskType IdentityMask = (IntegerMask << IntegerShift) | (FractionMask << FractionShift); + + constexpr const static MaskType MidpointMask = FIXED_POINTS_DETAILS::MsbMask::Value; + constexpr const static MaskType LesserMidpointMask = MidpointMask - 1; + +private: + using Base = FIXED_POINTS_DETAILS::SFixedBase; + using RawType = typename Base::RawType; + +public: + using Base::Base; + + constexpr SFixed(void); + constexpr SFixed(const IntegerType & integer); + constexpr SFixed(const IntegerType & integer, const FractionType & fraction); + + constexpr InternalType getInternal(void) const; + constexpr IntegerType getInteger(void) const; + constexpr FractionType getFraction(void) const; + + constexpr explicit operator IntegerType(void) const; + constexpr explicit operator float(void) const; + constexpr explicit operator double(void) const; + + template< unsigned IntegerOut, unsigned FractionOut > + constexpr explicit operator SFixed(void) const; + + constexpr static SFixed fromInternal(const InternalType & value); + + constexpr SFixed operator -(void) const; + SFixed & operator ++(void); + SFixed & operator --(void); + SFixed & operator +=(const SFixed & other); + SFixed & operator -=(const SFixed & other); + SFixed & operator *=(const SFixed & other); + SFixed & operator /=(const SFixed & other); + +public: + const static SFixed Epsilon; + const static SFixed MinValue; + const static SFixed MaxValue; + + const static SFixed Pi; + const static SFixed E; + const static SFixed Phi; + const static SFixed Tau; +}; + +// +// Variables +// + +template< unsigned Integer, unsigned Fraction > +constexpr const SFixed SFixed::Epsilon = SFixed::fromInternal(1); + +template< unsigned Integer, unsigned Fraction > +constexpr const SFixed SFixed::MinValue = SFixed::fromInternal(FIXED_POINTS_DETAILS::MsbMask::Value); + +template< unsigned Integer, unsigned Fraction > +constexpr const SFixed SFixed::MaxValue = SFixed::fromInternal(~FIXED_POINTS_DETAILS::MsbMask::Value); + +// 40 digits is probably enough for these +template< unsigned Integer, unsigned Fraction > +constexpr const SFixed SFixed::Pi = 3.1415926535897932384626433832795028841971; + +template< unsigned Integer, unsigned Fraction > +constexpr const SFixed SFixed::E = 2.718281828459045235360287471352662497757; + +template< unsigned Integer, unsigned Fraction > +constexpr const SFixed SFixed::Phi = 1.6180339887498948482045868343656381177203; + +template< unsigned Integer, unsigned Fraction > +constexpr const SFixed SFixed::Tau = 6.2831853071795864769252867665590057683943; + + +// +// Free functions +// + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed multiply(const SFixed & left, const SFixed & right); + +// +// Basic Logic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator ==(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator !=(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <=(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >=(const SFixed & left, const SFixed & right); + +// +// Inter-size Logic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator ==(const SFixed & left, const SFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator !=(const SFixed & left, const SFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <(const SFixed & left, const SFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >(const SFixed & left, const SFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <=(const SFixed & left, const SFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >=(const SFixed & left, const SFixed & right); + +// +// Basic Arithmetic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator +(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator -(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator *(const SFixed & left, const SFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator /(const SFixed & left, const SFixed & right); + +// +// Inter-size Arithmetic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator +(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed >; + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator -(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed >; + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator *(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed >; + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +inline constexpr auto operator /(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed >; + +FIXED_POINTS_END_NAMESPACE + +#include "SFixedMemberFunctions.h" +#include "SFixedFreeFunctions.h" \ No newline at end of file diff --git a/src/FixedPoints/SFixedBase.h b/src/FixedPoints/SFixedBase.h new file mode 100644 index 0000000..250d34d --- /dev/null +++ b/src/FixedPoints/SFixedBase.h @@ -0,0 +1,64 @@ +#pragma once + +#include "Details.h" + +FIXED_POINTS_BEGIN_NAMESPACE +namespace FIXED_POINTS_DETAILS +{ + template< unsigned Integer, unsigned Fraction > + class SFixedBase + { + public: + using IntegerType = FIXED_POINTS_DETAILS::LeastInt; + using FractionType = FIXED_POINTS_DETAILS::LeastUInt; + using InternalType = FIXED_POINTS_DETAILS::LeastInt<(Integer + 1) + Fraction>; + + constexpr const static unsigned long long Scale = 1ULL << Fraction; + + protected: + class RawType + { + private: + const InternalType value; + + public: + constexpr inline explicit RawType(const InternalType & value) : value(value) {} + constexpr inline explicit operator InternalType(void) const { return this->value; } + }; + + protected: + InternalType value; + + constexpr SFixedBase(void) : value(0) {} + constexpr SFixedBase(const RawType & value) : value(static_cast(value)) {} + + public: + constexpr SFixedBase(const IntegerLiteral & value) + : value(static_cast(static_cast >(value) << Fraction)) {} + + constexpr SFixedBase(const IntegerLiteralU & value) + : value(static_cast(static_cast >(value) << Fraction)) {} + + constexpr SFixedBase(const IntegerLiteralL & value) + : value(static_cast(static_cast >(value) << Fraction)) {} + + constexpr SFixedBase(const IntegerLiteralUL & value) + : value(static_cast(static_cast>(value) << Fraction)) {} + + constexpr SFixedBase(const IntegerLiteralLL & value) + : value(static_cast(static_cast>(value) << Fraction)) {} + + constexpr SFixedBase(const IntegerLiteralULL & value) + : value(static_cast(static_cast >(value) << Fraction)) {} + + constexpr SFixedBase(const DecimalLiteral & value) + : value(static_cast(value * Scale)) {} + + constexpr SFixedBase(const DecimalLiteralF & value) + : value(static_cast(value * Scale)) {} + + constexpr SFixedBase(const DecimalLiteralL & value) + : value(static_cast(value * Scale)) {} + }; +} +FIXED_POINTS_END_NAMESPACE \ No newline at end of file diff --git a/src/FixedPoints/SFixedFreeFunctions.h b/src/FixedPoints/SFixedFreeFunctions.h new file mode 100644 index 0000000..adaf352 --- /dev/null +++ b/src/FixedPoints/SFixedFreeFunctions.h @@ -0,0 +1,297 @@ +FIXED_POINTS_BEGIN_NAMESPACE + +// +// multiply +// + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed multiply(const SFixed & left, const SFixed & right) +{ + using ResultType = SFixed; + using InternalType = typename ResultType::InternalType; + return ResultType::fromInternal(static_cast(static_cast(left.getInternal()) * static_cast(right.getInternal()))); +} + +// +// Basic Logic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator ==(const SFixed & left, const SFixed & right) +{ + return (left.getInternal() == right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator !=(const SFixed & left, const SFixed & right) +{ + return (left.getInternal() != right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <(const SFixed & left, const SFixed & right) +{ + return (left.getInternal() < right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >(const SFixed & left, const SFixed & right) +{ + return (left.getInternal() > right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <=(const SFixed & left, const SFixed & right) +{ + return (left.getInternal() <= right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >=(const SFixed & left, const SFixed & right) +{ + return (left.getInternal() >= right.getInternal()); +} + +// +// Inter-size Logic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator ==(const SFixed & left, const SFixed & right) +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator == has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) == static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator !=(const SFixed & left, const SFixed & right) +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator != has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) != static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <(const SFixed & left, const SFixed & right) +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator < has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) < static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >(const SFixed & left, const SFixed & right) +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator > has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) > static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <=(const SFixed & left, const SFixed & right) +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator <= has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) <= static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >=(const SFixed & left, const SFixed & right) +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator >= has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) >= static_cast(right)); +} + +// +// Basic Arithmetic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator +(const SFixed & left, const SFixed & right) +{ + using InternalType = typename SFixed::InternalType; + return SFixed::fromInternal(static_cast(left.getInternal() + right.getInternal())); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator -(const SFixed & left, const SFixed & right) +{ + using InternalType = typename SFixed::InternalType; + return SFixed::fromInternal(static_cast(left.getInternal() - right.getInternal())); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator *(const SFixed & left, const SFixed & right) +{ + using InternalType = typename SFixed::InternalType; + using PrecisionType = typename SFixed::InternalType; + return SFixed::fromInternal(static_cast((static_cast(left.getInternal()) * static_cast(right.getInternal())) >> Fraction)); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed operator /(const SFixed & left, const SFixed & right) +{ + using InternalType = typename SFixed::InternalType; + using PrecisionType = typename SFixed::InternalType; + return SFixed::fromInternal(static_cast((static_cast(left.getInternal()) << Fraction) / right.getInternal())); +} + +// +// Inter-size Arithmetic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator +(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed > +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator + has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) + static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator -(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed > +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator - has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) - static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator *(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed > +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator * has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) * static_cast(right)); +} + + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator /(const SFixed & left, const SFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< SFixed, SFixed > +{ + using LeftType = SFixed; + using RightType = SFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator / has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) / static_cast(right)); +} + +// +// Literal-type Operators +// Generated by macro to make maintenance easier +// + +#define LOGIC_OPERATOR( type, op )\ + template< unsigned Integer, unsigned Fraction >\ + constexpr bool operator op (const SFixed & left, const type & right)\ + {\ + return (left op SFixed(right));\ + }\ + \ + template< unsigned Integer, unsigned Fraction >\ + constexpr bool operator op (const type & left, const SFixed & right)\ + {\ + return (SFixed(left) op right);\ + } + +#define ARITHMETIC_OPERATOR( type, op )\ + template< unsigned Integer, unsigned Fraction >\ + constexpr SFixed operator op (const SFixed & left, const type & right)\ + {\ + return (left op SFixed(right));\ + }\ + \ + template< unsigned Integer, unsigned Fraction >\ + constexpr SFixed operator op (const type & left, const SFixed & right)\ + {\ + return (SFixed(left) op right);\ + } + +#define OPERATORS( opType, op ) \ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteral, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralU, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralUL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralLL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralULL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::DecimalLiteral, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::DecimalLiteralF, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::DecimalLiteralL, op ) + +#define LOGIC_OPERATORS( op ) OPERATORS( LOGIC, op ) +#define ARITHMETIC_OPERATORS( op ) OPERATORS( ARITHMETIC, op ) + +LOGIC_OPERATORS( == ) +LOGIC_OPERATORS( != ) +LOGIC_OPERATORS( < ) +LOGIC_OPERATORS( > ) +LOGIC_OPERATORS( <= ) +LOGIC_OPERATORS( >= ) + +ARITHMETIC_OPERATORS( + ) +ARITHMETIC_OPERATORS( - ) +ARITHMETIC_OPERATORS( * ) +ARITHMETIC_OPERATORS( / ) + +// Prevent Macro-bleed: + +#undef LOGIC_OPERATOR +#undef ARITHMETIC_OPERATOR +#undef OPERATORS +#undef LOGIC_OPERATORS +#undef ARITHMETIC_OPERATORS + +FIXED_POINTS_END_NAMESPACE \ No newline at end of file diff --git a/src/FixedPoints/SFixedMemberFunctions.h b/src/FixedPoints/SFixedMemberFunctions.h new file mode 100644 index 0000000..cbaf1d5 --- /dev/null +++ b/src/FixedPoints/SFixedMemberFunctions.h @@ -0,0 +1,175 @@ +FIXED_POINTS_BEGIN_NAMESPACE + +// +// Constructors +// + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed::SFixed(void) + : Base() +{ +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed::SFixed(const IntegerType & integer) + : Base(RawType(static_cast(integer) << FractionSize)) +{ +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed::SFixed(const IntegerType & integer, const FractionType & fraction) + : Base(RawType((static_cast(integer) << FractionSize) | fraction)) +{ +} + +// +// Getters +// + +template< unsigned Integer, unsigned Fraction > +constexpr typename SFixed::InternalType SFixed::getInternal(void) const +{ + return this->value; +} + +template< unsigned Integer, unsigned Fraction > +constexpr typename SFixed::IntegerType SFixed::getInteger(void) const +{ + return static_cast(this->value >> IntegerShift) & IntegerMask | ((this->value < 0) ? ~IntegerMask : 0); +} + +template< unsigned Integer, unsigned Fraction > +constexpr typename SFixed::FractionType SFixed::getFraction(void) const +{ + return static_cast(this->value >> FractionShift) & FractionMask; +} + +// +// Cast Operators +// + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed::operator IntegerType(void) const +{ + return this->getInteger(); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed::operator float(void) const +{ + /*return (1.0f / Scale) * + (this->value < 0) ? + -((-this->value) & IdentityMask) : + this->value & IdentityMask; */ + return (1.0f / Scale) * + static_cast + ((this->value & IdentityMask) | + ((this->value < 0) ? ~IdentityMask : 0)); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed::operator double(void) const +{ + /*return (1.0 / Scale) * + (this->value < 0) ? + -((-this->value) & IdentityMask) : + this->value & IdentityMask; */ + return (1.0 / Scale) * + static_cast + ((this->value & IdentityMask) | + ((this->value < 0) ? ~IdentityMask : 0)); +} + +template< unsigned Integer, unsigned Fraction > +template< unsigned IntegerOut, unsigned FractionOut > +constexpr SFixed::operator SFixed(void) const +{ + using OutputType = UFixed; + using OutputInternalType = typename OutputType::InternalType; + using OutputShiftType = typename OutputType::ShiftType; + + using InputType = UFixed; + using InputInternalType = typename InputType::InternalType; + using InputShiftType = typename InputType::ShiftType; + + return + (FractionOut > FractionSize) ? + OutputType::fromInternal(static_cast(static_cast(this->value) << ((FractionOut > FractionSize) ? (FractionOut - FractionSize) : 0))) : + (FractionSize > FractionOut) ? + OutputType::fromInternal(static_cast(static_cast(this->value) >> ((FractionSize > FractionOut) ? (FractionSize - FractionOut) : 0))) : + OutputType::fromInternal(this->value); +} + +// +// Static Functions +// + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed SFixed::fromInternal(const typename SFixed::InternalType & value) +{ + return SFixed(RawType(value)); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed SFixed::operator -(void) const +{ + return SFixed::fromInternal(-this->value); +} + +// +// Member Operators +// + +template< unsigned Integer, unsigned Fraction > +SFixed & SFixed::operator ++(void) +{ + this->value += (1 << FractionSize); + return *this; +} + +template< unsigned Integer, unsigned Fraction > +SFixed & SFixed::operator --(void) +{ + this->value -= (1 << FractionSize); + return *this; +} + +// +// Compound Assignment Operators +// + +template< unsigned Integer, unsigned Fraction > +SFixed & SFixed::operator +=(const SFixed & other) +{ + this->value += other.value; + return *this; +} + +template< unsigned Integer, unsigned Fraction > +SFixed & SFixed::operator -=(const SFixed & other) +{ + this->value -= other.value; + return *this; +} + +template< unsigned Integer, unsigned Fraction > +SFixed & SFixed::operator *=(const SFixed & other) +{ + using InternalType = typename SFixed::InternalType; + using PrecisionType = typename SFixed::InternalType; + const PrecisionType temp = (static_cast(this->value) * static_cast(other.value)) >> Fraction; + this->value = static_cast(temp); + return *this; +} + +template< unsigned Integer, unsigned Fraction > +SFixed & SFixed::operator /=(const SFixed & other) +{ + using InternalType = typename SFixed::InternalType; + using PrecisionType = typename SFixed::InternalType; + const PrecisionType temp = (static_cast(this->value) << Fraction) / static_cast(other.value); + this->value = static_cast(temp); + return *this; +} + +FIXED_POINTS_END_NAMESPACE \ No newline at end of file diff --git a/src/FixedPoints/UFixed.h b/src/FixedPoints/UFixed.h new file mode 100644 index 0000000..4940e32 --- /dev/null +++ b/src/FixedPoints/UFixed.h @@ -0,0 +1,205 @@ +#pragma once + +#include "Details.h" +#include "UFixedBase.h" + +FIXED_POINTS_BEGIN_NAMESPACE + +// +// Declaration +// + +template< unsigned Integer, unsigned Fraction > +class UFixed : FIXED_POINTS_DETAILS::UFixedBase< Integer, Fraction > +{ +public: + static_assert((Integer + Fraction) <= FIXED_POINTS_DETAILS::BitSize::Value, "Platform does not have a native type large enough for UFixed."); + +public: + using IntegerType = FIXED_POINTS_DETAILS::LeastUInt; + using FractionType = FIXED_POINTS_DETAILS::LeastUInt; + using InternalType = FIXED_POINTS_DETAILS::LeastUInt; + + using ShiftType = FIXED_POINTS_DETAILS::LeastUInt; + using MaskType = FIXED_POINTS_DETAILS::LeastUInt; + + constexpr const static uintmax_t IntegerSize = Integer; + constexpr const static uintmax_t FractionSize = Fraction; + constexpr const static uintmax_t LogicalSize = IntegerSize + FractionSize; + constexpr const static uintmax_t InternalSize = FIXED_POINTS_DETAILS::BitSize::Value; + + constexpr const static uintmax_t Scale = 1ULL << FractionSize; + +public: + constexpr const static ShiftType IntegerShift = FractionSize; + constexpr const static ShiftType FractionShift = 0; + + constexpr const static MaskType IntegerMask = FIXED_POINTS_DETAILS::IdentityMask::Value; + constexpr const static MaskType FractionMask = FIXED_POINTS_DETAILS::IdentityMask::Value; + + constexpr const static MaskType IdentityMask = (IntegerMask << IntegerShift) | (FractionMask << FractionShift); + + constexpr const static MaskType MidpointMask = FIXED_POINTS_DETAILS::MsbMask::Value; + constexpr const static MaskType LesserMidpointMask = MidpointMask - 1; + +private: + using Base = FIXED_POINTS_DETAILS::UFixedBase; + using RawType = typename Base::RawType; + +public: + using Base::Base; + + constexpr UFixed(void); + constexpr UFixed(const IntegerType & integer); + constexpr UFixed(const IntegerType & integer, const FractionType & fraction); + + constexpr InternalType getInternal(void) const; + constexpr IntegerType getInteger(void) const; + constexpr FractionType getFraction(void) const; + + constexpr explicit operator IntegerType(void) const; + constexpr explicit operator float(void) const; + constexpr explicit operator double(void) const; + + template< unsigned IntegerOut, unsigned FractionOut > + constexpr explicit operator UFixed(void) const; + + constexpr static UFixed fromInternal(const InternalType & value); + + UFixed & operator ++(void); + UFixed & operator --(void); + UFixed & operator +=(const UFixed & other); + UFixed & operator -=(const UFixed & other); + UFixed & operator *=(const UFixed & other); + UFixed & operator /=(const UFixed & other); + +public: + const static UFixed Epsilon; + const static UFixed MinValue; + const static UFixed MaxValue; + + const static UFixed Pi; + const static UFixed E; + const static UFixed Phi; + const static UFixed Tau; +}; + +// +// Variables +// + +template< unsigned Integer, unsigned Fraction > +constexpr const UFixed UFixed::Epsilon = UFixed::fromInternal(1); + +template< unsigned Integer, unsigned Fraction > +constexpr const UFixed UFixed::MinValue = UFixed::fromInternal(0); + +template< unsigned Integer, unsigned Fraction > +constexpr const UFixed UFixed::MaxValue = UFixed::fromInternal(~0); + +// 40 digits is probably enough for these +template< unsigned Integer, unsigned Fraction > +constexpr const UFixed UFixed::Pi = 3.1415926535897932384626433832795028841971; + +template< unsigned Integer, unsigned Fraction > +constexpr const UFixed UFixed::E = 2.718281828459045235360287471352662497757; + +template< unsigned Integer, unsigned Fraction > +constexpr const UFixed UFixed::Phi = 1.6180339887498948482045868343656381177203; + +template< unsigned Integer, unsigned Fraction > +constexpr const UFixed UFixed::Tau = 6.2831853071795864769252867665590057683943; + + +// +// Free functions +// + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed multiply(const UFixed & left, const UFixed & right); + + +// +// Basic Logic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator ==(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator !=(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <=(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >=(const UFixed & left, const UFixed & right); + +// +// Inter-size Logic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator ==(const UFixed & left, const UFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator !=(const UFixed & left, const UFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <(const UFixed & left, const UFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >(const UFixed & left, const UFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <=(const UFixed & left, const UFixed & right); + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >=(const UFixed & left, const UFixed & right); + +// +// Basic Arithmetic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator +(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator -(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator *(const UFixed & left, const UFixed & right); + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator /(const UFixed & left, const UFixed & right); + +// +// Inter-size Arithmetic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator +(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed >; + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator -(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed >; + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator *(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed >; + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +inline constexpr auto operator /(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed >; + +FIXED_POINTS_END_NAMESPACE + +#include "UFixedMemberFunctions.h" +#include "UFixedFreeFunctions.h" \ No newline at end of file diff --git a/src/FixedPoints/UFixedBase.h b/src/FixedPoints/UFixedBase.h new file mode 100644 index 0000000..b1359cf --- /dev/null +++ b/src/FixedPoints/UFixedBase.h @@ -0,0 +1,64 @@ +#pragma once + +#include "Details.h" + +FIXED_POINTS_BEGIN_NAMESPACE +namespace FIXED_POINTS_DETAILS +{ + template< unsigned Integer, unsigned Fraction > + class UFixedBase + { + public: + using IntegerType = FIXED_POINTS_DETAILS::LeastInt; + using FractionType = FIXED_POINTS_DETAILS::LeastUInt; + using InternalType = FIXED_POINTS_DETAILS::LeastUInt; + + constexpr const static unsigned long long Scale = 1ULL << Fraction; + + protected: + class RawType + { + private: + const InternalType value; + + public: + constexpr inline explicit RawType(const InternalType & value) : value(value) {} + constexpr inline explicit operator InternalType(void) const { return this->value; } + }; + + protected: + InternalType value; + + constexpr UFixedBase(void) : value(0) {} + constexpr UFixedBase(const RawType & value) : value(static_cast(value)) {} + + public: + constexpr UFixedBase(const IntegerLiteral & value) + : value(static_cast(static_cast< LargerType >(value) << Fraction)) {} + + constexpr UFixedBase(const IntegerLiteralU & value) + : value(static_cast(static_cast< LargerType >(value) << Fraction)) {} + + constexpr UFixedBase(const IntegerLiteralL & value) + : value(static_cast(static_cast< LargerType >(value) << Fraction)) {} + + constexpr UFixedBase(const IntegerLiteralUL & value) + : value(static_cast(static_cast< LargerType>(value) << Fraction)) {} + + constexpr UFixedBase(const IntegerLiteralLL & value) + : value(static_cast(static_cast< LargerType>(value) << Fraction)) {} + + constexpr UFixedBase(const IntegerLiteralULL & value) + : value(static_cast(static_cast< LargerType >(value) << Fraction)) {} + + constexpr UFixedBase(const DecimalLiteral & value) + : value(static_cast(value * Scale)) {} + + constexpr UFixedBase(const DecimalLiteralF & value) + : value(static_cast(value * Scale)) {} + + constexpr UFixedBase(const DecimalLiteralL & value) + : value(static_cast(value * Scale)) {} + }; +} +FIXED_POINTS_END_NAMESPACE \ No newline at end of file diff --git a/src/FixedPoints/UFixedFreeFunctions.h b/src/FixedPoints/UFixedFreeFunctions.h new file mode 100644 index 0000000..00c5dc7 --- /dev/null +++ b/src/FixedPoints/UFixedFreeFunctions.h @@ -0,0 +1,297 @@ +FIXED_POINTS_BEGIN_NAMESPACE + +// +// multiply +// + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed multiply(const UFixed & left, const UFixed & right) +{ + using ResultType = UFixed; + using InternalType = typename ResultType::InternalType; + return ResultType::fromInternal(static_cast(static_cast(left.getInternal()) * static_cast(right.getInternal()))); +} + +// +// Basic Logic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator ==(const UFixed & left, const UFixed & right) +{ + return (left.getInternal() == right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator !=(const UFixed & left, const UFixed & right) +{ + return (left.getInternal() != right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <(const UFixed & left, const UFixed & right) +{ + return (left.getInternal() < right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >(const UFixed & left, const UFixed & right) +{ + return (left.getInternal() > right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator <=(const UFixed & left, const UFixed & right) +{ + return (left.getInternal() <= right.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool operator >=(const UFixed & left, const UFixed & right) +{ + return (left.getInternal() >= right.getInternal()); +} + +// +// Inter-size Logic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator ==(const UFixed & left, const UFixed & right) +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator == has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) == static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator !=(const UFixed & left, const UFixed & right) +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator != has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) != static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <(const UFixed & left, const UFixed & right) +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator < has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) < static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >(const UFixed & left, const UFixed & right) +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator > has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) > static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator <=(const UFixed & left, const UFixed & right) +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator <= has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) <= static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr bool operator >=(const UFixed & left, const UFixed & right) +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator >= has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) >= static_cast(right)); +} + +// +// Basic Arithmetic Operations +// + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator +(const UFixed & left, const UFixed & right) +{ + using InternalType = typename UFixed::InternalType; + return UFixed::fromInternal(static_cast(left.getInternal() + right.getInternal())); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator -(const UFixed & left, const UFixed & right) +{ + using InternalType = typename UFixed::InternalType; + return UFixed::fromInternal(static_cast(left.getInternal() - right.getInternal())); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator *(const UFixed & left, const UFixed & right) +{ + using InternalType = typename UFixed::InternalType; + using PrecisionType = typename UFixed::InternalType; + return UFixed::fromInternal(static_cast((static_cast(left.getInternal()) * static_cast(right.getInternal())) >> Fraction)); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed operator /(const UFixed & left, const UFixed & right) +{ + using InternalType = typename UFixed::InternalType; + using PrecisionType = typename UFixed::InternalType; + return UFixed::fromInternal(static_cast((static_cast(left.getInternal()) << Fraction) / static_cast(right.getInternal()))); +} + +// +// Inter-size Arithmetic Operations +// + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator +(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed > +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator + has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) + static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator -(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed > +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator - has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) - static_cast(right)); +} + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator *(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed > +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator * has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) * static_cast(right)); +} + + +template< unsigned IntegerLeft, unsigned FractionLeft, unsigned IntegerRight, unsigned FractionRight > +constexpr auto operator /(const UFixed & left, const UFixed & right) + -> FIXED_POINTS_DETAILS::LargerType< UFixed, UFixed > +{ + using LeftType = UFixed; + using RightType = UFixed; + + static_assert(LeftType::InternalSize != RightType::InternalSize, "operator / has ambiguous result"); + + using CompareType = FIXED_POINTS_DETAILS::LargerType; + + return (static_cast(left) / static_cast(right)); +} + +// +// Literal-type Operators +// Generated by macro to make maintenance easier +// + +#define LOGIC_OPERATOR( type, op )\ + template< unsigned Integer, unsigned Fraction >\ + constexpr bool operator op (const UFixed & left, const type & right)\ + {\ + return (left op UFixed(right));\ + }\ + \ + template< unsigned Integer, unsigned Fraction >\ + constexpr bool operator op (const type & left, const UFixed & right)\ + {\ + return (UFixed(left) op right);\ + } + +#define ARITHMETIC_OPERATOR( type, op )\ + template< unsigned Integer, unsigned Fraction >\ + constexpr UFixed operator op (const UFixed & left, const type & right)\ + {\ + return (left op UFixed(right));\ + }\ + \ + template< unsigned Integer, unsigned Fraction >\ + constexpr UFixed operator op (const type & left, const UFixed & right)\ + {\ + return (UFixed(left) op right);\ + } + +#define OPERATORS( opType, op ) \ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteral, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralU, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralUL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralLL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::IntegerLiteralULL, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::DecimalLiteral, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::DecimalLiteralF, op )\ + opType##_OPERATOR( FIXED_POINTS_DETAILS::DecimalLiteralL, op ) + +#define LOGIC_OPERATORS( op ) OPERATORS( LOGIC, op ) +#define ARITHMETIC_OPERATORS( op ) OPERATORS( ARITHMETIC, op ) + +LOGIC_OPERATORS( == ) +LOGIC_OPERATORS( != ) +LOGIC_OPERATORS( < ) +LOGIC_OPERATORS( > ) +LOGIC_OPERATORS( <= ) +LOGIC_OPERATORS( >= ) + +ARITHMETIC_OPERATORS( + ) +ARITHMETIC_OPERATORS( - ) +ARITHMETIC_OPERATORS( * ) +ARITHMETIC_OPERATORS( / ) + +// Prevent Macro-bleed: + +#undef LOGIC_OPERATOR +#undef ARITHMETIC_OPERATOR +#undef OPERATORS +#undef LOGIC_OPERATORS +#undef ARITHMETIC_OPERATORS + +FIXED_POINTS_END_NAMESPACE \ No newline at end of file diff --git a/src/FixedPoints/UFixedMemberFunctions.h b/src/FixedPoints/UFixedMemberFunctions.h new file mode 100644 index 0000000..4d43b70 --- /dev/null +++ b/src/FixedPoints/UFixedMemberFunctions.h @@ -0,0 +1,155 @@ +FIXED_POINTS_BEGIN_NAMESPACE + +// +// Constructors +// + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed::UFixed(void) + : Base() +{ +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed::UFixed(const IntegerType & integer) + : Base(RawType(static_cast(integer) << FractionSize)) +{ +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed::UFixed(const IntegerType & integer, const FractionType & fraction) + : Base(RawType((static_cast(integer) << FractionSize) | fraction)) +{ +} + +// +// Getters +// + +template< unsigned Integer, unsigned Fraction > +constexpr typename UFixed::InternalType UFixed::getInternal(void) const +{ + return this->value; +} + +template< unsigned Integer, unsigned Fraction > +constexpr typename UFixed::IntegerType UFixed::getInteger(void) const +{ + return static_cast(this->value >> IntegerShift) & IntegerMask; +} + +template< unsigned Integer, unsigned Fraction > +constexpr typename UFixed::FractionType UFixed::getFraction(void) const +{ + return static_cast(this->value >> FractionShift) & FractionMask; +} + +// +// Cast Operators +// + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed::operator IntegerType(void) const +{ + return this->getInteger(); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed::operator float(void) const +{ + return ((this->value & IdentityMask) * (1.0f / Scale)); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed::operator double(void) const +{ + return ((this->value & IdentityMask) * (1.0 / Scale)); +} + +template< unsigned Integer, unsigned Fraction > +template< unsigned IntegerOut, unsigned FractionOut > +constexpr UFixed::operator UFixed(void) const +{ + using OutputType = UFixed; + using OutputInternalType = typename OutputType::InternalType; + using OutputShiftType = typename OutputType::ShiftType; + + using InputType = UFixed; + using InputInternalType = typename InputType::InternalType; + using InputShiftType = typename InputType::ShiftType; + + return + (FractionOut > FractionSize) ? + OutputType::fromInternal(static_cast(static_cast(this->value) << ((FractionOut > FractionSize) ? (FractionOut - FractionSize) : 0))) : + (FractionSize > FractionOut) ? + OutputType::fromInternal(static_cast(static_cast(this->value) >> ((FractionSize > FractionOut) ? (FractionSize - FractionOut) : 0))) : + OutputType::fromInternal(this->value); +} + +// +// Static Functions +// + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed UFixed::fromInternal(const typename UFixed::InternalType & value) +{ + return UFixed(RawType(value)); +} + +// +// Member Operators +// + +template< unsigned Integer, unsigned Fraction > +UFixed & UFixed::operator ++(void) +{ + this->value += (1 << FractionSize); + return *this; +} + +template< unsigned Integer, unsigned Fraction > +UFixed & UFixed::operator --(void) +{ + this->value -= (1 << FractionSize); + return *this; +} + +// +// Compound Assignment Operators +// + +template< unsigned Integer, unsigned Fraction > +UFixed & UFixed::operator +=(const UFixed & other) +{ + this->value += other.value; + return *this; +} + +template< unsigned Integer, unsigned Fraction > +UFixed & UFixed::operator -=(const UFixed & other) +{ + this->value -= other.value; + return *this; +} + +template< unsigned Integer, unsigned Fraction > +UFixed & UFixed::operator *=(const UFixed & other) +{ + using InternalType = typename UFixed::InternalType; + using PrecisionType = typename UFixed::InternalType; + const PrecisionType temp = (static_cast(this->value) * static_cast(other.value)) >> FractionSize; + this->value = static_cast(temp); + return *this; +} + +template< unsigned Integer, unsigned Fraction > +UFixed & UFixed::operator /=(const UFixed & other) +{ + using InternalType = typename UFixed::InternalType; + using PrecisionType = typename UFixed::InternalType; + const PrecisionType temp = (static_cast(this->value) << FractionSize) / static_cast(other.value); + this->value = static_cast(temp); + return *this; +} + +FIXED_POINTS_END_NAMESPACE \ No newline at end of file diff --git a/src/FixedPoints/Utils.h b/src/FixedPoints/Utils.h new file mode 100644 index 0000000..49bdecf --- /dev/null +++ b/src/FixedPoints/Utils.h @@ -0,0 +1,233 @@ +#pragma once + +#include "Details.h" +#include "UFixed.h" +#include "SFixed.h" + +// +// Declaration +// + +FIXED_POINTS_BEGIN_NAMESPACE +template< unsigned Integer, unsigned Fraction > +constexpr UFixed floorFixed(const UFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed floorFixed(const SFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed ceilFixed(const UFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed ceilFixed(const SFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed roundFixed(const UFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed roundFixed(const SFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr bool signbitFixed(const SFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed absFixed(const SFixed & value); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed copysignFixed(const SFixed & x, const SFixed & y); + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed nextafterFixed(const SFixed & from, const SFixed & to); + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed nextafterFixed(const UFixed & from, const UFixed & to); + +// +// Unsigned Random +// + +template< unsigned Integer, unsigned Fraction > +UFixed randomUFixed(void); + +template< unsigned Integer, unsigned Fraction > +UFixed randomUFixed(const UFixed & exclusiveUpperBound); + +template< unsigned Integer, unsigned Fraction > +UFixed randomUFixed(const UFixed & inclusiveLowerBound, const UFixed & exclusiveUpperBound); + +// +// Signed Random +// + +template< unsigned Integer, unsigned Fraction > +SFixed randomSFixed(void); + +template< unsigned Integer, unsigned Fraction > +SFixed randomSFixed(const SFixed & exclusiveUpperBound); + +template< unsigned Integer, unsigned Fraction > +SFixed randomSFixed(const SFixed & inclusiveLowerBound, const SFixed & exclusiveUpperBound); +FIXED_POINTS_END_NAMESPACE + +// +// Definition +// + +FIXED_POINTS_BEGIN_NAMESPACE +template< unsigned Integer, unsigned Fraction > +constexpr UFixed floorFixed(const UFixed & value) +{ + using OutputType = UFixed; + using InternalType = typename OutputType::InternalType; + return OutputType::fromInternal(static_cast(value.getInternal() & ~OutputType::FractionMask)); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed floorFixed(const SFixed & value) +{ + using OutputType = SFixed; + using InternalType = typename OutputType::InternalType; + return OutputType::fromInternal(static_cast(value.getInternal() & ~OutputType::FractionMask)); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed ceilFixed(const UFixed & value) +{ + return UFixed((value.getFraction() == 0) ? value.getInteger() : (value.getInteger() + 1), 0); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed ceilFixed(const SFixed & value) +{ + return SFixed((value.getFraction() == 0) ? value.getInteger() : (value.getInteger() + 1), 0); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed roundFixed(const UFixed & value) +{ + using OutputType = UFixed; + return (value.getInternal() & OutputType::MidpointMask) != 0 ? ceilFixed(value) : floorFixed(value); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed roundFixed(const SFixed & value) +{ + using OutputType = SFixed; + return + ( + ((value.getInternal() & OutputType::MidpointMask) != 0) && + !signbitFixed(value) || + ((value.getInternal() & OutputType::LesserMidpointMask) != 0) + ) + ? ceilFixed(value) + : floorFixed(value); +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed truncFixed(const UFixed & value) +{ + return UFixed(value.getInteger(), 0); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed truncFixed(const SFixed & value) +{ + using OutputType = SFixed; + return + (value.getInternal() < 0) ? + OutputType::fromInternal(value.getInternal() & ~OutputType::FractionMask) + OutputType(1, 0) : + OutputType::fromInternal(value.getInternal() & ~OutputType::FractionMask); +} + +template< unsigned Integer, unsigned Fraction > +constexpr bool signbitFixed(const SFixed & value) +{ + return (value.getInternal() < 0); +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed absFixed(const SFixed & value) +{ + return (signbitFixed(value)) ? -value : value; +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed copysignFixed(const SFixed & x, const SFixed & y) +{ + return (signbitFixed(x) != signbitFixed(y)) ? -x : x; +} + +template< unsigned Integer, unsigned Fraction > +constexpr SFixed nextafterFixed(const SFixed & from, const SFixed & to) +{ + using ResultType = SFixed; + return + (from < to) ? + ResultType::fromInternal(from.getInternal() + 1) : + (from > to) ? + ResultType::fromInternal(from.getInternal() - 1): + to; +} + +template< unsigned Integer, unsigned Fraction > +constexpr UFixed nextafterFixed(const UFixed & from, const UFixed & to) +{ + using ResultType = UFixed; + return + (from < to) ? + ResultType::fromInternal(from.getInternal() + 1) : + (from > to) ? + ResultType::fromInternal(from.getInternal() - 1): + to; +} + +// +// Unsigned Random +// + +template< unsigned Integer, unsigned Fraction > +UFixed randomUFixed(void) +{ + using InternalType = typename UFixed::InternalType; + return UFixed::fromInternal(FIXED_POINTS_DETAILS::RandomHelper::Random()); +} + +template< unsigned Integer, unsigned Fraction > +UFixed randomUFixed(const UFixed & exclusiveUpperBound) +{ + using InternalType = typename UFixed::InternalType; + return UFixed::fromInternal(FIXED_POINTS_DETAILS::RandomHelper::Random() % exclusiveUpperBound.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +UFixed randomUFixed(const UFixed & inclusiveLowerBound, const UFixed & exclusiveUpperBound) +{ + using InternalType = typename UFixed::InternalType; + return UFixed::fromInternal(inclusiveLowerBound.getInternal() + FIXED_POINTS_DETAILS::RandomHelper::Random() % exclusiveUpperBound.getInternal()); +} + +// +// Signed Random +// + +template< unsigned Integer, unsigned Fraction > +SFixed randomSFixed(void) +{ + using InternalType = typename SFixed::InternalType; + return SFixed::fromInternal(FIXED_POINTS_DETAILS::RandomHelper::Random()); +} + +template< unsigned Integer, unsigned Fraction > +SFixed randomSFixed(const SFixed & exclusiveUpperBound) +{ + using InternalType = typename SFixed::InternalType; + return SFixed::fromInternal(FIXED_POINTS_DETAILS::RandomHelper::Random() % exclusiveUpperBound.getInternal()); +} + +template< unsigned Integer, unsigned Fraction > +SFixed randomSFixed(const SFixed & inclusiveLowerBound, const SFixed & exclusiveUpperBound) +{ + using InternalType = typename SFixed::InternalType; + return SFixed::fromInternal(inclusiveLowerBound.getInternal() + FIXED_POINTS_DETAILS::RandomHelper::Random() % exclusiveUpperBound.getInternal()); +} +FIXED_POINTS_END_NAMESPACE diff --git a/src/FixedPointsCommon.h b/src/FixedPointsCommon.h new file mode 100644 index 0000000..aaf6810 --- /dev/null +++ b/src/FixedPointsCommon.h @@ -0,0 +1 @@ +#include "FixedPointsCommon/FixedPointsCommon.h" \ No newline at end of file diff --git a/src/FixedPointsCommon/FixedPointsCommon.h b/src/FixedPointsCommon/FixedPointsCommon.h new file mode 100644 index 0000000..e269e88 --- /dev/null +++ b/src/FixedPointsCommon/FixedPointsCommon.h @@ -0,0 +1,4 @@ +#include "../FixedPoints.h" + +#include "SFixedCommon.h" +#include "UFixedCommon.h" \ No newline at end of file diff --git a/src/FixedPointsCommon/SFixedCommon.h b/src/FixedPointsCommon/SFixedCommon.h new file mode 100644 index 0000000..b4f701d --- /dev/null +++ b/src/FixedPointsCommon/SFixedCommon.h @@ -0,0 +1,15 @@ +#pragma once + +#include "../FixedPoints.h" + +FIXED_POINTS_BEGIN_NAMESPACE +using SQ3x4 = SFixed<3, 4>; +using SQ7x8 = SFixed<7, 8>; +using SQ15x16 = SFixed<15, 16>; +using SQ31x32 = SFixed<31, 32>; + +using SQ1x6 = SFixed<1, 6>; +using SQ1x14 = SFixed<1, 14>; +using SQ1x30 = SFixed<1, 30>; +using SQ1x62 = SFixed<1, 62>; +FIXED_POINTS_END_NAMESPACE diff --git a/src/FixedPointsCommon/UFixedCommon.h b/src/FixedPointsCommon/UFixedCommon.h new file mode 100644 index 0000000..bd8c24f --- /dev/null +++ b/src/FixedPointsCommon/UFixedCommon.h @@ -0,0 +1,15 @@ +#pragma once + +#include "../FixedPoints.h" + +FIXED_POINTS_BEGIN_NAMESPACE +using UQ4x4 = UFixed<4, 4>; +using UQ8x8 = UFixed<8, 8>; +using UQ16x16 = UFixed<16, 16>; +using UQ32x32 = UFixed<32, 32>; + +using UQ1x7 = UFixed<1, 7>; +using UQ1x15 = UFixed<1, 15>; +using UQ1x31 = UFixed<1, 31>; +using UQ1x63 = UFixed<1, 63>; +FIXED_POINTS_END_NAMESPACE \ No newline at end of file