Giter VIP home page Giter VIP logo

fixedpointsarduino's Introduction

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.

Project Showcase

Here's a list of projects that use FixedPoints:

If you have a project that uses FixedPoints and would like your work to be showcased here, please raise an issue.

Requirements:

  • The Compiler must be C++11 compliant.
  • The user should ideally be familar with the 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, modified or unmodified:
    • You must package a copy of LICENCE with your code.
    • You must package a copy of NOTICE with your code.
    • You are not required to distribute the source code.
  • You must not use any trademarks owned by the licensor.
    • Unless you have specific permission to do so.
  • You may modify the source code.
    • If you modify the code, you must state this fact prominently within the source file.
      • E.g. in a comment at the top of the source file.
    • You are under no obligation to distribute the source code of your modifications.
  • You may incorporate any part of the code into another project.
    • That project may use a different licence.
    • That code must retain the Apache 2.0 licence notice, including the copyright notice.
    • If the code is modified, the modifications may be published under a different licence.
      • The Apache 2.0 licence still applies to unmodified portions.
      • The copyright and licence notices for the unmodified portions must still be prominently displayed.
      • It is advised that you do not do this, as it is highly unusual and untested in court.

Conditional Compilation

These are symbols you can define prior to library inclusion to alter the behaviour of the library.

  • FIXED_POINTS_USE_NAMESPACE: Define this to wrap all classes and functions in the namespace FixedPoints. Useful for preventing naming conflicts.
  • FIXED_POINTS_NO_RANDOM: Define this to disable the random utility functions. Useful for systems that don't have access to long random(void) from avr-libc.

FAQ

  • Why can't I multiply UQ32x32 or SQ31x32 by another type?
    • Because it would require a 128-bit integer type to provide enough precision for accurate multiplication.

Contents

This library supplies two core types and sixteen type aliases.

Defines

  • FIXED_POINTS_NAMESPACE: The namespace used by FixedPoints. This is empty unless FIXED_POINTS_USE_NAMESPACE is defined prior to inclusion.
  • FIXED_POINTS_DETAILS: An infrastructure macro that should not be used in user code. It is safe to undefine this if it is causing problems.
  • FIXED_POINTS_BEGIN_NAMESPACE: An infrastructure macro that should not be used in user code. It is safe to undefine this if it is causing problems.
  • FIXED_POINTS_END_NAMESPACE: An infrastructure macro that should not be used in user code. It is safe to undefine this if it is causing problems.

Core Types:

The core types are provided by FixedPoints.h.

  • UFixed<I, F>: 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<I, F>: 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.)

Operators:

  • +: Adds two UFixeds or two SFixeds
  • -: Subtracts two UFixeds or two SFixeds
  • *: Multiplies two UFixeds or two SFixeds
  • /: Divides two UFixeds or two SFixeds
  • ==: Compares two UFixeds or two SFixeds
  • !=: Compares two UFixeds or two SFixeds
  • <: Compares two UFixeds or two SFixeds
  • <=: Compares two UFixeds or two SFixeds
  • >: Compares two UFixeds or two SFixeds
  • >=: Compares two UFixeds or two SFixeds

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 UFixeds or two SFixeds, returns a result that is twice the resolution of the input.

Member Functions:

  • UFixed<I, F>::getInteger: Gets the integer part of an unsigned fixed point.

  • UFixed<I, F>::getFraction: Gets the fractional part of an unsigned fixed point.

  • UFixed<I, F>::getInternal: Gets the internal representation of an unsigned fixed point.

  • SFixed<I, F>::getInteger: Gets the integer part of a signed fixed point.

  • SFixed<I, F>::getFraction: Gets the fractional part of a signed fixed point.

  • SFixed<I, F>::getInternal: Gets the internal representation of a signed fixed point.

Static Functions:

  • UFixed<I, F>::fromInternal: Produces an unsigned fixed point number from its internal representation.
  • SFixed<I, F>::fromInternal: Produces a signed fixed point number from its internal representation.

Construction:

Note that both UFixed<I, F> and SFixed<I, F> 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<I, F> 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<I, F> 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<I, F> 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<I, F> is explicitly convertible to:

  • float.
  • double.
  • The smallest signed type capable of holding its integer part. I.e. a type of at least I + 1 bits.
  • Another SFixed type of a different scale. E.g. SFixed<3, 4> may be converted to SFixed<7, 8> and vice versa.

fixedpointsarduino's People

Contributors

pharap avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fixedpointsarduino's Issues

Compile error in Arduino environment

Doesn't compile in Arduino 1.8.5 and Platform.io.

The first error message:

/home/mpamplona/Arduino/libraries/FixedPoints/src/FixedPoints/Details.h: In static member function 'static uint8_t FixedPointsDetails::RandomHelper::Random()':
/home/mpamplona/Arduino/libraries/FixedPoints/src/FixedPoints/Details.h:176:71: error: no matching function for call to 'random()'
static inline uint8_t Random() { return static_cast<uint8_t>(random()); }

Converting to float provides an incorrect result for certain types.

Brief Description

With certain types such as SQ1x30, converting to float produces an incorrect result.

Observed Behaviour

Converting a value of -0.9000854 to SQ1x30 and back again results in a value of -0.4000854015.

Expected Behaviour
Converting a value of -0.9000854 to SQ1x30 and back again should result in a value of -0.9000854, or thereabouts.

Source Code

#include <FixedPointsCommon.h>

SQ1x30 value = -0.9000854;

void setup()
{
  Serial.begin(9600);
  while(!Serial);

  // Print -0.4000854015
  Serial.println(static_cast<float>(value), 10);

  // Prints false
  Serial.println(static_cast<float>(value) == -0.9000854 ? F("true") : F("false"));

  while(true);
}

void loop()
{
}

Provide formatting and printing functions

I'm getting a little fed up with having to put static_cast<float> everywhere when debugging so I'd like to find a means to print fixed points.

Implementing printing in fractional form should be pretty straightforward in most cases.
Something like this ought to work for most cases:

printer.print(fixed.getInteger());
printer.print(F(" "));
printer.print(fixed.getFraction());
printer.print(F("/");
printer.print(1ul << FractionSize);

Since inheriting Arduino's Printable would incur a ridiculously unnecessary overhead (2 bytes per object to provide the pointer to the virtual table) I'll most likely either use named functions (e.g. printFractional(printer, fixed)) or make use of << as an output operator in the style of C++'s std::ostream.

Being able to provide printing in a decimal format would be ideal, but at present I'm not sure how to do that beyond casting to float, so I may have to abuse that as a stop-gap. Hopefully I'll have time to research how floating point printing works to see if I can learn anything useful that could be applied to printing fixed points in a decimal format.

If I can't manage to find a way to implement decimal format printing then I'll just stick to the fractional representation because it's easy to implement, relatively cheap (it's mostly just bit shifting and integer printing) and reasonably easy to understand.

Investigate the potential of adding specalisations for fixed points with zero-width fractions

At present there's a strong possibility that the compiler does not optimise fixed points defined with a zero-width fraction part due to the extra operations required for fractional fixed points.

Although I think it's unlikely that anyone would actually want to use a fixed point with a zero-width fraction part (because this would be more or less equivalent to an integer of the same size), I think it would be good to specialise this case at some point on the off chance that there is a genuine need for such types given that there's a clear way to optimise in this situation.

In particular, some of the possible optimisations are:

  • getInteger() can avoid shifting by simply returning this->value.
  • getFraction() can avoid shifting by simply returning 0.
  • Multiplication and division do not need to upscale and downscale.
  • roundFixed() and similar functions could simply return their argument.

Consider providing templates implementing other ways of expressing fixed points

There are times where it may be useful to be able to represent fixed points using notation other than the conventional Q format.

For example, it may be useful to have a type expressed as Fixed<sign, integer, fraction>, where:

  • Fixed<0, integer, fraction> is implemented with UFixed
  • Fixed<1, integer, fraction> is implemented with SFixed
  • All other representations are undefined

Other notations include:

  • s:m:f format, as described above as Fixed<sign, integer, fraction>
  • fx format where fx1.16 describes a 16-bit value with 1 integer but and 15 fractional bits.

This isn't a particularly important feature, but there are cases where it might be useful.


There's the possibility of implementing it through template 'trickery' and type aliases or inheritance, but there's also the possibility (should the Fixed approach turn out to be more convinient) of inversing things such that UFixed and SFixed become inheritors of the true implementation with minimal changes to the API such that it could still be compatible for more abstract use cases.

Such an eventuality is probably still a long way down the road and may not be worthwhile, but it's at least worth pondering for the time being.

Update date on licence headers

The date still reads 2017 and needs to be changed to 2017-2018.

(Note: perhaps look into how to make a bot account to do this, if an easy method doesn't already exist.)

Replace use of LargerType in constructors with ShiftType

This change would have two potential benefits:

  • The undefined behaviour eminating from << with a negative left hand value would be replaced with the well-defined behaviour of an unsigned type.
  • Theoretically this should generate less code because the code would no longer be using unnecessary excess bytes during the shift operation. Whether this is true in practice would have to be tested - the compiler may already be optimising this.

The code should be functionally equivalent, hence this should be a 'patch' change.

Add version detection mechanism

Add a define-based version detection mechanism to facilitate better backwards compatibility and version-based conditional compilation

Remove MidpointMask and LesserMidpointMask

These are no longer in use within the code and are unlikely to be needed in external code.

They should be removed.
This is a breaking change and should be scheduled for v2.0.0

Replace shifts with enforced byte dropping in special cases

Aparently cases of >> 8, >> 16 and >>32 aren't being optimised to simply drop the bytes as I was originally expecting.

This isn't much of an issue for more powerful processors,
but it's a bit of an issue for AVR chips.

The fix will be to manually enforce the byte dropping.
I will look into the best way to do this.

If all else fails, type punning may be the only option.

Define the default constructor as explicitly defaulted

Defaulting to zero is probably expected behaviour for inexperienced programmers,
but experienced programmers are probably more used to default construction being a nop.

As more experienced programmers are becoming interested in this library and as being a more optimal choice than floats is part of this library's goal, it might make sense to undo the decision to make default construction initialise with 0.

Serial stops working in example sketch

When I run the example sketch on an arduino uno, the serial monitor stops printing in testQ7x8 at 'Displayin'(sic) just before showing the float result of a*b. Although when I move the multiplication test higher up, it works fine, but breaks later anyway. I have tried using different terminals to display serial (platformio device monitor on different emulators) as well as the arduino ide integrated serial monitor. It always breaks at some point. Just calling one of the two test functions also breaks, it just stops printing at some point. Just printing a string from a loop works fine, though.

It may well be that I made a stupid mistake somewhere, but just in case I wanted to ask here.

Rearrange member constants and member type aliases

Currently the definitions of the member type aliases and member constants of UFixed and SFixed are somewhat disorganised.

The definitions should be rearranged to give their definitions better semantics and make the definitions less repetative.

I.e.

  • Replace Integer with IntegerSize
  • Replace Fraction with FractionSize
  • Replace Integer + Fraction with LogicalSize

Add facility to showcase people's creations

As interest in the library picks up, it would be good to be able to showcase some of the projects that are using FixedPoints.

At the moment there's no facility for it, so it would be good to add one, either as a .md or .txt file in extras or perhaps embedded in the README.md on the front page.

Compile error Arduino, Details.h, Max(void), Min(Void)

Get the following error compiling the example:

Arduino: 1.8.5 (Mac OS X), Board:"Arduino Due (Programming Port)"

In file included from /Users/username/Documents/Arduino/libraries/FixedPoints/src/FixedPoints/FixedPoints.h:15:0,
                 from /Users/username/Documents/Arduino/libraries/FixedPoints/src/FixedPoints.h:15,
                 from /Users/username/Documents/Arduino/libraries/FixedPoints/examples/FixedPointCalculations/FixedPointCalculations.ino:1:
/Users/username/Documents/Arduino/libraries/FixedPoints/src/FixedPoints/Details.h:57:25: error: macro "Max" requires 2 arguments, but only 1 given
  constexpr auto Max(void) noexcept -> decltype((Left > Right) ? Left : Right)

                         ^
/Users/username/Documents/Arduino/libraries/FixedPoints/src/FixedPoints/Details.h:69:25: error: macro "Min" requires 2 arguments, but only 1 given
  constexpr auto Min(void) noexcept -> decltype((Left < Right) ? Left : Right)

                         ^

Consider replacing some member functions and operators with free functions

Following the current style of not defining functions inside the main body of the class, this would mean that there would be fewer member functions kept inside the class.

This is considered 'breaking' because technically someone could be doing (for example) fixed.operator+=(value) somewhere in their code, which would break when operator+= is no longer a member function.

Anyone using the form fixed += value would be unaffected, and the fact that the vast majority of people are likely to be doing this is precisely why I have little concern about making this change.

However I will schedule this for the next 'breaking' change anyway, as a precaution. This change is more of an internal benefit than an external benefit, so it will most likely go unnoticed for the end user.

Typically there are other benefits associated with using free functions rather than member functions, but the primary driving force behind this change is the improved maintainability.

Add conversions between SFixed types and UFixed types

At the moment it is not possible to convert between signed and unsigned fixed point types.
People will most likely want/expect this behaviour at some point as it is possible to convert beween signed and unsigned integers, thus it should be implemented.

Devise tests

This library's gone a long time without a proper set of tests.

At some point in the future it would be nice to have a set of tests that could be used to ensure the code is working correctly and nothing gets broken during bugfixes.

Investigate replacing const references with values

Sometimes const references are better than values.
Sometimes values are better than const references (due to copy elision).

It might be worth looking into replacing some of the current usage of const references with values if it would produce smaller/faster code in certain circumstances.

Add random number support for other board targets

Background

The crux of the random number issue is as thus:

  • rand() returns int
  • sizeof(int) on AVR is 2 (i.e. int is 16 bits wide)
  • avr-libc introduced random() to provide an equivalent to rand() that returns a 32-bit value instead of a 16-bit value
  • Because random() originates from avr-libc rather than Arduino, a lot of devices with Arduino support don't provide many of the extra features provided by avr-libc, including random()
  • This library was originally written for an AVR target, leading to a dependency on random() (which at the time was believed by the author to originate from Arduino rather than avr-libc) which unfortunately causes problems with other targets for the aforementioned reasons.

Up until now the solution has been to conditionally disable random number generation depending on which processor is being used.

Recently (see #73 & #74) this was changed to disable random number support for all non-AVR processors in the hopes that non-AVR processors that don't support random() are significantly more plentiful than non-AVR processors that do support random().

Looking forward, it would be good to provide random number support for other non-AVR targets. The most obvious way would be to use rand(), but this should be considered carefully before implementation in case there's another platform with a 16-bit int floating around out there. I.e. at minimum a solution would have to either reject a target with a non-32-bit int or adapt to accomodate it, either by not providing random number generation functions or by generating a function that calls rand() as many times as required to fill the requested number of bits.


Additionally, it would be good to provide a way of interfacing with random number generators that follow the precedent set by the C++ standard library's <random> header, and perhaps a way to accept user-provided function-only PRNGs via detection of the return type.

(As ever, this would be a lot simpler if C++ standard library support were provided for AVR targets. At the very least the <type_traits> header would be invaluable.)

Consider eliminating SFixedBase and UFixedBase

At some point these solved a particular problem, but it seems that is no longer the case.
Perhaps it is time to remove them?

As they are part of the 'details' namespace, they can be removed in a backwards compatible way because nobody should be using them in their code.

Add more integer cast operators

Currently there's only an explicit cast for IntegerType.
It might be useful to have casts for other integer types as well.

Add bitshift operators

I was surprises to see that shift operations are apparently missing.

#include <FixedPoints.h>
#include <FixedPointsCommon.h>

void setup()
{
  SQ15x16 a = 1;
  a >>= 5;
}

void loop()
{
}

The above fails to compile with the following message:


FixedPointCalculations:7:5: error: no match for 'operator>>=' (operand types are 'SQ15x16 {aka SFixed<15u, 16u>}' and 'int')

   a >>= 5;
     ^

no match for 'operator>>=' (operand types are 'SQ15x16 {aka SFixed<15u, 16u>}' and 'int')

I am using version 1.0.7

Consider providing constants in a separate namespace

At the moment the named constants (such as E, Pi, Tau and Phi) are static class members of SFixed and UFixed.

From a maintenance point of view, it would be beneficial to reduce the duplication by moving this functionality into a single place, e.g. a class or namespace provided by Utils.h.

These constants could then take the form of constexpr template functions rather than static constexpr data members. The functions would essentially pass a floating point value to the appropriate constructor of each type.

The old values could be deprecated and removed in some future breaking change, but for now simply adding a separate (preferred) source of constants would be a non-breaking additional improvement.

Update the dates on all licence headers

Once again, the licence dates need updating.

I really should have done this before the last few commits, but as long as it's done before the next release it shouldn't be too much of an issue.

Use of 'inline' in 'RawType' is redundant

constexpr inline explicit RawType(const InternalType & value) : value(value) {}
constexpr inline explicit operator InternalType() const { return this->value; }

constexpr inline explicit RawType(const InternalType & value) : value(value) {}
constexpr inline explicit operator InternalType() const { return this->value; }

Compiling for teensy 3.2 (ARM 32-bit)

Hello again,
I am wondering how much work it would be to bring this library to the teensy. I tried compiling it, and there are really many errors of the form:

In file included from src/FixedPoints/FixedPoints.h:17:0, from src/FixedPoints.h:15, from src/main.cpp:3: src/FixedPoints/UFixedBase.h:71:13: error: 'constexpr FixedPointsDetails::UFixedBase<Integer, Fraction>::UFixedBase(const DecimalLiteralF&)' cannot be overloaded constexpr UFixedBase(const DecimalLiteralF & value) ^ src/FixedPoints/UFixedBase.h:68:13: error: with 'constexpr FixedPointsDetails::UFixedBase<Integer, Fraction>::UFixedBase(const DecimalLiteral&)' constexpr UFixedBase(const DecimalLiteral & value) ^

On the teensy, I think something is done to convert all decimal literals to float unless specified as double.
So one of DecimalLiteralF and DecimalLiteralL seems to collide with DecimalLiteral I think, but this is just speculation.

Do you think it is feasible to port your library to the teensy? I am willing to put some time into this.

Provide trigonometry functions

Hi,
This is a very interesting lib for smal CPU without FPU.
On my case I need sin(x) cos(x),
It could be nice to add this into your library, with precomputed table.

Add template aliases SQ and UQ in FixedPointsCommon

Add template aliases SQ<I, F> and UQ<I, F> to FixedPointsCommon.h.

These would effectively serve as a more convinient short-hand for those who are familiar with Q notation and would probably be direct aliases for SFixed and UFixed.

I.e.

template< unsigned Integer, unsigned Fraction >
using SQ = SFixed<Integer, Fraction>;

template< unsigned Integer, unsigned Fraction >
using UQ = UFixed<Integer, Fraction>;

If this change is made after the resolution of #19 then it may be worth having a more complex implementation that also permits a single template parameter when no integer part is desired.

Fix bug in definition of UFixedBase::IntegerType

UFixedBase::IntegerType is mistakenly defined as:

using IntegerType = FIXED_POINTS_DETAILS::LeastInt<Integer + 1>;

It should in fact be defined as:

using IntegerType = FIXED_POINTS_DETAILS::LeastUInt<Integer>;

Remove redundant consts

constexpr implies const when used on variables, so all cases of constexpr static const are redundant.

The redundant consts could be removed for better code clarity.

Duplication of IntegerType, FractionType, InternalType and Scale

Both UFixedBase, UFixed, SFixedBase and SFixed all define IntegerType, FractionType, InternalType and Scale.

The definitions should be unified to prevent conflicting definitions, either by putting all definitions in the base classes (UFixedBase and SFixedBase) and having the child classes (UFixed and SFixed) inherit them,
or by eliminating them from the base classes.

Disable random number functionality for non-AVR boards

Originally I believed random() to originate from Arduino because Arduino defines several overloads of random(), but I have since learned that the zero-parameter version of random() originates from avr-libc.

Despite learning this a long time ago, I haven't made an attept to rectify FixedPoints' behaviour in regard to this issue until now.

This issue proposes that rather than testing for specific non-AVR processors, a better approach for handling the random number issue (for now) is to define FIXED_POINTS_NO_RANDOM if the target processor is not an AVR target, or more specifically if __AVR__ is not defined. Hence:

#if !defined(__AVR__)
#define FIXED_POINTS_NO_RANDOM
#endif

There is a slight chance that this may break the library for boards that provide random() but do not identify as AVR, but I expect this to be an exceptionally rare occurance. In comparison the number of boards that this library could compile for but won't because of the fixed point issue is becoming increasingly notable.

Rather than end up with a long list of issues requesting that a board be supported, it makes more sense to simply assume a target doesn't support random() and provide support for all the other features of the library.

Fix boolean expression bug introduced by #12

See here:

return ((value.getFraction() >= OutputType(0.5).getFraction()) != 0) ? ceilFixed(value) : floorFixed(value);

The expression:

((value.getFraction() <= OutputType(0.5).getFraction()) != 0)

Should be:

(value.getFraction() <= OutputType(0.5).getFraction())

This bug is also present here:

? ((value.getFraction() <= OutputType(0.5).getFraction()) != 0) ? floorFixed(value) : ceilFixed(value)

: ((value.getFraction() >= OutputType(0.5).getFraction()) != 0) ? ceilFixed(value) : floorFixed(value);


Note:
This bug was introduced due to implicit boolean conversion.
If true != 0 weren't a valid expression, this bug would have been a compliation error.

Remove unnecessary constructors

The constructors UFixed::UFixed(const IntegerType &) and SFixed::SFixed(const IntegerType &) appear to be functionally equivalent to the constructors that they inherit from UFixedBase and SFixedBase.

Thus, these constructors are superfluous and should be removed.

Make casting negative numbers more predictable.

Brief Description

Casting between different signed types can have unintuitive (incorrect?) results. Some examples of how to do this correctly would be welcomed.

Observed Behaviour

When casting negative Q1.14 values to Q3.12, the results are very unexpected. The fractional bit of the number seems to be within ~1 of the true value, but the integer part can be quite a bit off. What should be -1 ends up being 3. Or positive 2 instead of -2.

Expected Behaviour

Casting a fixed point number from one format to another should maintain a roughly equivalent value.

Source Code

#include <FixedPoints.h>

void setup() {
	Serial.begin(115200);
	
	SFixed<1,14> a = -0.05;
	SFixed<3, 12> b = static_cast<SFixed<3, 12>>(a);
	SFixed<3, 12> c = -0.05;
	Serial.print("Initial Q1.14 value: "); Serial.println((float)a);
	Serial.print("Value cast to Q3.12: "); Serial.println((float)b);
	
	Serial.println();
	
	Serial.print("Internal representation of Q1.14: "); Serial.println(a.getInternal(), BIN);
	Serial.print("Expected representation of Q3.12: "); Serial.println(c.getInternal(), BIN);
	Serial.print("Actual representation of Q3.12: "); Serial.println(b.getInternal(), BIN);
	
	Serial.println();
	
	Serial.print("Q1.14 integer bit: "); Serial.println(a.getInteger());
	Serial.print("Q1.14 fraction bit: "); Serial.println(a.getFraction());
	Serial.print("Wonky Q3.12 integer bit: "); Serial.println(b.getInteger());
	Serial.print("Wonky Q3.12 fraction bit: "); Serial.println(b.getFraction());
	Serial.print("Correct Q3.12 integer bit: "); Serial.println(c.getInteger());
	Serial.print("Correct Q3.12 fraction bit: "); Serial.println(c.getFraction());
}

void loop() {}

Output:

Initial Q1.14 value: -0.05
Value cast to Q3.12: 3.95

Internal representation of Q1.14: 11111111111111111111110011001101
Expected representation of Q3.12: 11111111111111111111111100110100
Actual representation of Q3.12: 11111100110011

Q1.14 integer bit: -1
Q1.14 fraction bit: 15565
Wonky Q3.12 integer bit: 3
Wonky Q3.12 fraction bit: 3891
Correct Q3.12 integer bit: -1
Correct Q3.12 fraction bit: 3892

Extra Information

  • Target Board: Arduino Uno (R3?)
  • Compiler Version: avr-g++ 7.3.0
  • Arduino IDE Version: 1.8.10
  • OS: Debian Sid

Fix potential bug with Scale definition

Currently Scale is defined as 1ULL << FractionSize, but has a type of uintmax_t.
This means that for systems where the width of uintmax_t is larger than that of unsigned long long,
this could cause a semantic error at runtime due to the lack of sufficient bits.

To fix this, 1ULL should be replaced with UINTMAX_C(1),
to form the expression UINTMAX_C(1) << FractionSize.
(For more information on UINTMAX_C, see here.)

Add special handling of 0 bit integer parts

At the moment:

  • using 0 as an integer part for UFixed results in warnings
  • using 0 as an integer part for SFixed results in errors

Thus the cases of 0 bit integer parts should be handled specially,
through template specialisation, a new type or through some other means.

Trying to use SQ31x32

Hello Pharap,
Firstly, I would like to congratulate you for this work. :)
Maybe you can help me with a issue! I'm trying to use the SQ31x32 fixed point type, however I could not compile my code without errors. If I use the SQ15x16 type, I can compile and run my code but if I change to SQ31x32 I can't compile it.
I'm trying to do a simple multiplication:

SQ31x32 x = 1;
SQ31x32 y = 0.000200;
SQ31x32 z = 0.0;
z= x * y;

Could you help me or give me some feedback?
Thank you so much.
Nuno Vilhena


Arduino: 1.8.5 (Windows Store 1.8.10.0) (Windows 10), Placa:"Arduino Due (Programming Port)"

In file included from sketch\FixedPoints/FixedPoints.h:15:0,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/Details.h: In instantiation of 'struct FixedPointsDetails::LeastIntDef<127u>':

sketch\FixedPoints/Details.h:116:51:   required by substitution of 'template<unsigned int Bits> using LeastInt = typename FixedPointsDetails::LeastIntDef::Type [with unsigned int Bits = 127u]'

sketch\FixedPoints/SFixedBase.h:28:80:   required from 'class FixedPointsDetails::SFixedBase<62u, 64u>'

sketch\FixedPoints/SFixed.h:27:7:   required from 'class SFixed<62u, 64u>'

sketch\FixedPoints/SFixedFreeFunctions.h:173:80:   required from 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]'

C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:27:14:   required from here

sketch\FixedPoints/Details.h:110:3: error: static assertion failed: No type large enough

   static_assert(Bits <= BitSize<intmax_t>::Value, "No type large enough");

   ^

In file included from sketch\FixedPoints/FixedPoints.h:18:0,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/SFixedBase.h: In instantiation of 'class FixedPointsDetails::SFixedBase<62u, 64u>':

sketch\FixedPoints/SFixed.h:27:7:   required from 'class SFixed<62u, 64u>'

sketch\FixedPoints/SFixedFreeFunctions.h:173:80:   required from 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]'

C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:27:14:   required from here

sketch\FixedPoints/SFixedBase.h:44:16: error: instantiation of 'FixedPointsDetails::SFixedBase<Integer, Fraction>::value' as type 'FixedPointsDetails::SFixedBase<62u, 64u>::InternalType {aka void}'

   InternalType value;

                ^

sketch\FixedPoints/SFixedBase.h:44:16: error: 'FixedPointsDetails::SFixedBase<Integer, Fraction>::value' has incomplete type

sketch\FixedPoints/SFixedBase.h:44:16: error: invalid use of 'FixedPointsDetails::SFixedBase<62u, 64u>::InternalType {aka void}'

In file included from sketch\FixedPoints/FixedPoints.h:21:0,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/SFixed.h: In instantiation of 'class SFixed<62u, 64u>':

sketch\FixedPoints/SFixedFreeFunctions.h:173:80:   required from 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]'

C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:27:14:   required from here

sketch\FixedPoints/SFixed.h:30:2: error: static assertion failed: Platform does not have a native type large enough for SFixed.

  static_assert(((Integer + 1) + Fraction) <= FIXED_POINTS_DETAILS::BitSize<intmax_t>::Value, "Platform does not have a native type large enough for SFixed.");

  ^

In file included from sketch\FixedPoints/FixedPoints.h:15:0,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/Details.h: In instantiation of 'struct FixedPointsDetails::LeastUIntDef<127u>':

sketch\FixedPoints/Details.h:105:53:   required by substitution of 'template<unsigned int Bits> using LeastUInt = typename FixedPointsDetails::LeastUIntDef::Type [with unsigned int Bits = 127u]'

sketch\FixedPoints/SFixed.h:37:77:   required from 'class SFixed<62u, 64u>'

sketch\FixedPoints/SFixedFreeFunctions.h:173:80:   required from 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]'

C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:27:14:   required from here

sketch\FixedPoints/Details.h:98:3: error: static assertion failed: No type large enough

   static_assert(Bits <= BitSize<uintmax_t>::Value, "No type large enough");

   ^

In file included from sketch\FixedPoints/FixedPoints.h:21:0,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/SFixed.h: In instantiation of 'class SFixed<62u, 64u>':

sketch\FixedPoints/SFixedFreeFunctions.h:173:80:   required from 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]'

C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:27:14:   required from here

sketch\FixedPoints/SFixed.h:48:35: error: variable or field 'IntegerShift' declared void

  constexpr const static ShiftType IntegerShift = FractionSize;

                                   ^

sketch\FixedPoints/SFixed.h:49:35: error: variable or field 'FractionShift' declared void

  constexpr const static ShiftType FractionShift = 0;

                                   ^

sketch\FixedPoints/SFixed.h:51:34: error: variable or field 'IntegerMask' declared void

  constexpr const static MaskType IntegerMask = FIXED_POINTS_DETAILS::IdentityMask<IntegerSize>::Value;

                                  ^

sketch\FixedPoints/SFixed.h:52:34: error: variable or field 'FractionMask' declared void

  constexpr const static MaskType FractionMask = FIXED_POINTS_DETAILS::IdentityMask<FractionSize>::Value;

                                  ^

sketch\FixedPoints/SFixed.h:54:34: error: variable or field 'IdentityMask' declared void

  constexpr const static MaskType IdentityMask = (IntegerMask << IntegerShift) | (FractionMask << FractionShift);

                                  ^

sketch\FixedPoints/SFixed.h:56:34: error: variable or field 'MidpointMask' declared void

  constexpr const static MaskType MidpointMask = FIXED_POINTS_DETAILS::MsbMask<FractionSize>::Value;

                                  ^

sketch\FixedPoints/SFixed.h:57:34: error: variable or field 'LesserMidpointMask' declared void

  constexpr const static MaskType LesserMidpointMask = MidpointMask - 1;

                                  ^

In file included from sketch\FixedPoints/SFixed.h:193:0,

                 from sketch\FixedPoints/FixedPoints.h:21,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/SFixedMemberFunctions.h:113:37: error: forming reference to void

 constexpr SFixed<Integer, Fraction> SFixed<Integer, Fraction>::fromInternal(const typename SFixed<Integer, Fraction>::InternalType & value)

                                     ^

In file included from sketch\FixedPoints/FixedPoints.h:18:0,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/SFixedBase.h: In instantiation of 'class FixedPointsDetails::SFixedBase<62u, 64u>::RawType':

sketch\FixedPoints/SFixed.h:64:14:   required from 'class SFixed<62u, 64u>'

sketch\FixedPoints/SFixedFreeFunctions.h:173:80:   required from 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]'

C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:27:14:   required from here

sketch\FixedPoints/SFixedBase.h:36:23: error: instantiation of 'FixedPointsDetails::SFixedBase<Integer, Fraction>::RawType::value' as type 'const InternalType {aka const void}'

    const InternalType value;

                       ^

sketch\FixedPoints/SFixedBase.h:36:23: error: 'FixedPointsDetails::SFixedBase<Integer, Fraction>::RawType::value' has incomplete type

sketch\FixedPoints/SFixedBase.h:36:23: error: invalid use of 'const InternalType {aka const void}'

sketch\FixedPoints/SFixedBase.h:39:30: error: forming reference to void

    constexpr inline explicit RawType(const InternalType & value) : value(value) {}

                              ^

In file included from sketch\FixedPoints/SFixed.h:194:0,

                 from sketch\FixedPoints/FixedPoints.h:21,

                 from sketch\FixedPoints.h:15,

                 from C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:1:

sketch\FixedPoints/SFixedFreeFunctions.h: In instantiation of 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]':

C:\Users\FCTUNL\Documents\Arduino\Test_Floating2\Test_Floating2.ino:27:14:   required from here

sketch\FixedPoints/SFixedFreeFunctions.h:174:123: error: invalid operands of types 'void' and 'void' to binary 'operator*'

  return SFixed<Integer, Fraction>::fromInternal(static_cast<InternalType>((static_cast<PrecisionType>(left.getInternal()) * static_cast<PrecisionType>(right.getInternal())) >> Fraction));

                                                                                                                           ^

sketch\FixedPoints/SFixedFreeFunctions.h:175:1: error: body of constexpr function 'constexpr SFixed<Integer, Fraction> operator*(const SFixed<Integer, Fraction>&, const SFixed<Integer, Fraction>&) [with unsigned int Integer = 31u; unsigned int Fraction = 32u]' not a return-statement

 }

 ^

exit status 1
Erro ao compilar para a placa Arduino Due (Programming Port).

Este relatório teria mais informação com a
opção «Mostrar mensagens detalhadas durante a
compilação» seleccionada nas Preferências.

Fix shift overflow bug in preincrement and predecrement operators

At the moment the preincrement and predecrement operations are creating the following warnings:

SFixedMemberFunctions.h:224:20: warning: left shift count >= width of type [-Wshift-count-overflow]

this->value += (1 << FractionSize);

SFixedMemberFunctions.h:231:20: warning: left shift count >= width of type [-Wshift-count-overflow]

this->value -= (1 << FractionSize);

In the code generating these warnings I am using SQ15x16 and targetting an AVR architecture (Arduino Uno R3) so the likely cause is that 1 is interpreted as being an int, which is 16 bits wide on this architecture, and FractionSize has a value of 16 for SQ15x16 which means the integer is being shifted past its full width.

The optimal solution is probably to cast the 1 to InternalType before the shift.

Support the use of volatile

the keyword volatile doesn't seem to work which makes things like benchmarking quite difficult.
minimal example:
#include <FixedPoints.h>
#include <FixedPointsCommon.h>

void setup() {
volatile SFixed<7, 8> f1, f2, f3;
f3=f1+f2;
}

void loop() {
}

this produces the error:

no match for 'operator+' (operand types are 'volatile SFixed<7u, 8u>' and 'volatile SFixed<7u, 8u>')

Or am I just doing it wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.