A Fixed Point Arithmetic Library

Can you also add it to PlatformIO please? :heart:

1 Like

I’ll look into doing that at some point.
I haven’t used PlatformIO before so I’ll have to investigate it first.

It probably won’t be for at least a couple of days.
I’m going on/have gone on a short C++ programming hiatus because this project has burnt me out somewhat (though I don’t know how long it will last, I’m a bit of a programmaholic).

The FixedPoint library has a library.json file for PlatformIO, so adding it should be easy.
http://docs.platformio.org/en/latest/userguide/lib/cmd_register.html

The manifest URL for library registration:
https://raw.githubusercontent.com/Pharap/FixedPointsArduino/master/library.json

1 Like

Thanks @MLXXXp - the 1943 game was a test bed for some of the functionality in the FixedPoints library during my testing. I actually released the game before @Pharap was ready to release his code so packaged the library as it existed then with it. The next steps for my game are to update the library to the released version which will allow me to not package it.

1 Like

Thank you for your patience, PlatformIO support has been added as of version 1.0.2.

2 Likes

I decided to experiment with @Pharap’s FixedPoints library in my jam entry.
The optimal format in my case is SQ23.8 and I need to do a lot of multiplication.

Basically, multiplying two 23.8 numbers looks something like this:
result = A * B >> 8;

Unfortunately, the right shift is done by dividing by 256 instead of simply discarding the lower byte.
In my game, this results in a hex file with 22370 bytes and a cpuLoad of over 110. Too big, too slow.

I added the following template specialization and the hex file dropped to 18118 bytes (4250 bytes smaller), with a cpuLoad of 80.

template<>
SFixed<23,8> operator *(const SFixed<23,8> & left, const SFixed<23,8> & right)
{
  int64_t r = left.getInternal() * right.getInternal();
  uint8_t *b = reinterpret_cast<uint8_t *>(&r);
  return SFixed<23, 8>::fromInternal( *reinterpret_cast<int32_t*>(b+1) );
}
3 Likes

This is one of the reasons I made the library template heavy, it’s very easy to specialise.

The library is designed more with the power of two cases in mind (4.4, 8.8, 16.16, 32.32),
I haven’t spent much time on the in between cases, in part due to apparent lack of interest.

The larger types will always be somewhat costly on the AVR chips - no barrel shifter, no division opcode, the need to chain addition/subtraction opcodes, the multiplication opcode only working on 8 bit quantities.

I was hoping the compiler was smart enough to realise that >> 8 means it can just discard the lower byte.
It has realised that in previous cases, but maybe that’s because smaller types were being used.

I also want to point out that pointer arithmetic on void * is in fact illegal and the only reason that b + 1 works here is because of GCC’s compiler extensions.
Using uint8_t * would be a more portable solution.

Either way, it will be good to see another game using FixedPoints.

I hoped so too and I didn’t even bother checking, but it showed up when I profiled the code.
Here’s a specialization for SQ15.16.

template<>
SFixed<15,16> operator *(const SFixed<15,16> & left, const SFixed<15,16> & right)
{
  int64_t r = left.getInternal() * right.getInternal();
  uint8_t *b = reinterpret_cast<uint8_t *>(&r);
  return SFixed<15,16>::fromInternal( *reinterpret_cast<int32_t*>(b+2) );
}

I’ll edit the previous post to use uint8_t. I’m still rusty from all the javascript I’ve been writing. :stuck_out_tongue:

1 Like

I’ve got an issue ready:

I’ll look into actually editing them in at a later date,
I’ve already got a bit of a backlog of issues.

I’ll have to investigate what the best way to do it is though.
I’m aware that type punning is sometimes frowned upon because it can have some unexpected implications.
(Also endianness could be an issue.)


Sometimes I use void main(void) despite knowing the standard forbids it,
but otherwise I try to avoid extensions and stick to the standard.

You’re probably not going to be able to find a very good cross-platform solution. The ideal implementation is what you already have, especially on other processors like ARM. Forcing it to drop bytes is getting around a specific platform+compiler oddity (or a bug?), so I’d use #ifdef __AVR__ around the specializations and would not have to worry about endianness.

I’ll think about it more when I get round to handling it.
It probably will come down to handling different processors specifically, but preliminary research indicates that unions might be a better solution than pointers as a precaution (inlining is somewhat likely, and statement rearrangement may follow).

(I’d have to check whether that only applies to C, but chances are C++ inherited it.)

I tried this:

template<>
SFixed<23,8> operator *(const SFixed<23,8> & left, const SFixed<23,8> & right)
{
  union {
    struct {
      uint8_t pad;
      int32_t i32;
    };
    int64_t i64;
  } u;
  u.i64 = left.getInternal() * right.getInternal();
  return SFixed<23, 8>::fromInternal( u.i32 );
}

It works, but produces slightly larger code. I’m not sure I’d prefer this, both are ugly code smells to get around a compiler derp.

Like I say, I’ll cross that bridge when I get to it.
I like to do research and testing before adding features because it’s better to delay a new feature in an attempt to pre-empt problems than it is to retroactively remove a feature.

I’ve already learnt my lesson from the random number facility
(which I plan to either remove or migrate in future).

Sure. I’m at that bridge though, so I’m sharing what I’ve come across. :slight_smile:

1 Like

Sorry to revive such an old topic, but those of you who sometimes use this library for games might be interested to know that I’ve recently been fixing some old bugs and adding some new features.

About two weeks ago I released the first new release in over two years (about two months shy of three years), v1.0.8, and I’ve just finished a v1.1.0 release.
(Hopefully these haven’t broken any games.)

I hope to add some more features in the future, and eventually make a breaking v2.0.0 release, but we’ll see how things go.

For now the most interesting change for Arduboy is the addition of bitshifting operators, since in some cases that might be cheaper than multiplying or dividing by a power of two.

4 Likes

I am currently developing a new game which uses this library. I will need to upgrade!

2 Likes

Hi,

I tried the library and found the casted value is not the same as my assigned -0.9000854 but instead, it is -0.4000854015, it is a big difference.

Any idea?

Ray.

It appears to be an issue for SQ1x30, SQ1x62, UQ1x31, UQ1x63 (with positive numbers) but works for SQ1x14 and UQ1x15.

While the issue is being fixed, do you need such precision?

I tracked down the problem, fixed it, and made a new release.

The root of the problem was actually a missing static_cast, which lead to a bit getting dropped from a calculation, which had the knock-on effect of breaking casting to float for certain types such as SQ1x30.

Oddly enough, the problem only affected conversion to floating point types (float, double and long double), everything else worked fine, including conversion from float which means that despite -0.4000854015 being printed, internally e still had a value of -0.900854 (or as close as an SQ1x30 can get to that value), and all other calculations would have worked as intended.

1 Like

Hi Pharap, the library is great to me. thanks for your update

I had checked this library can accept the maths operations of arrays
(and hence, matrix operations) which is quit important for making faster games and some acceptable real time applications to test some dsp and adaptive control applications based on fixed point arithematics in Arduino.
for the latter case, the accuracy is more important to me.

So, I also check the accuracy of the library with some web-based Q-format Converter, it is pretty near the calculated result based on bit by bit arithmetics but not accurately equal, I don’t know why, as I find the library source codes of the FixedPointsArduino are quite a bit complex to me …

the last question is, whether there exist some overflow indicator in the add or mult. operation?
Anyway, it is quite good a library and help me a lots, thanks!!