You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/FixMath.h
+79-67Lines changed: 79 additions & 67 deletions
Original file line number
Diff line number
Diff line change
@@ -16,73 +16,85 @@ This file implements two fixed point number classes. These numbers can have a fr
16
16
signed (SFix) or unsigned (UFix).
17
17
18
18
A fixed point number has its range defined by the number of bits encoding the integer part (NI
19
-
in the following) and its precision by the number of bits encoding the fractional part (NF). For UFix types, the integral part can hold values in [0,2^NI-1], for SFix types, the integral part can hold values in [-2^NI,2^NI-1]. The number of bits encoding the fractional can be considered as the precision of the number: given NF, the number of possible values in the [0,1[ range will 2^NF. Hence, given NF, the resolution will be 1/(2^NF).
20
-
21
-
Under the hood, these types will keep track of the maximum possible value they might hold (this is the RANGE template parameter), and, if only *SAFE* operations (see below) are used, will automatically adjust there NI and NF to accomodate the result of a operation. It will also try not to promote there internal type when possible, assuFM_ming that you use the complete range of a given type.
22
-
23
-
The operations possible with these types can be divided into two categories:
24
-
- the operations between FixMath types are all safe (aka won't overflow) and are the only one included by default
25
-
- the operations between a FixMath and a native C type (int, float) are NOT safe and are not included by default. In order to activate them, you need to `#define FIXMATH_UNSAFE` before including FixMath.h.
26
-
27
-
28
-
Like standard C(++) types, the fixed point numbers defined here are following some rules:
29
-
- any fixed type can be converted to another *as long as the value can be represented in the destination type*. Casting to a bigger type in term of NI and NF is safe, but reducing NI can lead to an overflow if the new type cannot hold the integer value and reducing NF leads to a loss of precision.
30
-
- Fixed types can be constructed from and converted to standard C types.
31
-
- all operations between fixed point number is safe (it won't overflow) and preserve the precision. In particular:
32
-
- only addition, subtraction and multiplication are implemented (this is a design choice, see below)
33
-
- any operation between a signed and an unsigned leads to a signed number
34
-
- resulting numbers will be casted to a type big enough to store the expected values. It follows that it is worth starting with types that are as small as possible to hold the initial value.
35
-
- all operations between a fixed point number and a native type (int, float, uint) are *not* safe. If the resulting value cannot be represented in the fixed point type it will overflow. Only addition, subtraction, multiplication and right/left shift are implemented. These are only accessible activating the `FIXMATH_UNSAFE` set.
36
-
- safe right/left shifts, which return the correct value in the correct type are implemented as .sR<shift>() and .sL<shift>() respectively, shift being the shifting amount.
37
-
38
-
More specifically on the returned types of the operations between fixed point math types:
39
-
- Additions:
40
-
- UFix<NI,NF> + UFix<_NI,_NF> returns UFix<MAX(NI,_NI)+1,MAX(NF,_NF)> at worse
41
-
- SFix<NI,NF> + SFix<_NI,_NF> returns SFix<MAX(NI,_NI)+1,MAX(NF,_NF)> at worse
42
-
- UFix<NI,NF> + SFix<_NI,_NF> returns SFix<MAX(NI,_NI)+1,MAX(NF,_NF)> at worse
43
-
- UFix<NI,NF> + anything_else (signed or not) returns UFix<NI,NF> (only available with `FIXMATH_UNSAFE`)
44
-
- SFix<NI,NF> + anything_else (signed or not) returns SFix<NI,NF> (only available with `FIXMATH_UNSAFE`)
45
-
- Subtractions:
46
-
- UFix<NI,NF> - UFix<_NI,_NF> returns SFix<MAX(NI,_NI),MAX(NF,_NF)> at worse
47
-
- SFix<NI,NF> - SFix<_NI,_NF> returns SFix<MAX(NI,_NI)+1,MAX(NF,_NF)> at worse
48
-
- SFix<NI,NF> - UFix<_NI,_NF> returns SFix<MAX(NI,_NI)+1,MAX(NF,_NF)> at worse
49
-
- UFix<NI,NF> - anything_else (signed or not) returns UFix<NI,NF> (only available with `FIXMATH_UNSAFE`)
50
-
- SFix<NI,NF> - anything_else (signed or not) returns SFix<NI,NF> (only available with `FIXMATH_UNSAFE`)
51
-
- (-)SFix<NI,NF> return SFix<NI,NF>
52
-
- (-)UFix<NI,NF> return SFix<NI,NF>
53
-
- Multiplications:
54
-
- UFix<NI,NF> * UFix<_NI,_NF> returns UFix<NI+_NI,NF+_NF> at worse
55
-
- UFix<NI,NF> * SFix<_NI,_NF> returns SFix<NI+_NI,NF+_NF> at worse
56
-
- SFix<NI,NF> * SFix<_NI,_NF> returns SFix<NI+_NI,NF+_NF> at worse
57
-
- UFix<NI,NF> * anything_else (signed or not) returns UFix<NI,NF> (only available with `FIXMATH_UNSAFE`)
58
-
- SFix<NI,NF> * anything_else (signed or not) returns SFix<NI,NF> (only available with `FIXMATH_UNSAFE`)
59
-
- Shifts:
60
-
- UFix<NI,NF> .sR<NS> returns UFix<NI-NS,NF+NS>
61
-
- UFix<NI,NF> .sL<NS> returns UFix<NI+NS,NF-NS>
62
-
- same for SFix.
63
-
- Inverse:
64
-
- UFix<NI,NF>.invFast() returns the inverse of the number as UFix<NF,NI>
65
-
- SFix<NI,NF>.invFast() returns the inverse of the number as SFix<NF,NI>
66
-
- UFix<NI,NF>.invAccurate() returns the inverse of the number as UFix<NF,2*NI+NF>
67
-
- SFix<NI,NF>.invAccurate() returns the inverse of the number as SFix<NF,2*NI+NF>
68
-
- UFix<NI,NF>.inv<_NF>() returns the inverse of the number as UFix<NF,_NF>
69
-
- SFix<NI,NF>.inv<_NF>() returns the inverse of the number as SFix<NF,_NF>
70
-
- Conversion (should be preferred over casting, when possible):
71
-
- UFix<NI,NF>.asSFix() returns SFix<NI,NF>
72
-
- SFix<NI,NF>.asUFix() returns UFix<NI,NF>
73
-
- UFix<NI,NF>.asFloat() returns the value as a float.
74
-
- SFix<NI,NF>.asFloat() returns the value as a float.
75
-
- UFix<NI,NF>.asRaw() returns the internal value.
76
-
- SFix<NI,NF>.asRaw() returns the internal value.
77
-
- T.toUFraction() returns UFix<0,NF> with NF the number of bits of T (uint8_t leads to NF=8bits).
78
-
- T.toSFraction() returns SFix<0,NF> with NF the number of bits of T (int8_t leads to NF=7bits).
79
-
- T.toUInt() returns UFix<NI,0> with NI the number of bits of T (uint8_t leads to NI=8bits).
80
-
- T.toSInt() returns SFix<NI,> with NI the number of bits of T (int8_t leads to NI=7bits).
81
-
82
-
Note on division:
83
-
The division is not implemented. This is a deliberate choice made for two reasons:
84
-
- in contrast with all the other fundamental operations, it is not possible to guarantee that precision will be kept (other operations returns *exact* results whenever the operands were also exactly represented. Note that this is actually not the case when using normal floating point numbers. The inverse functions can be used to fake a division, by multiplying by the inverse of a number.
85
-
- division are usually very slow operations on MCU, hence there usage is discouraged. The ideal way of doing it is to compute the inverse whenever needed and only when needed. In the context of Mozzi for instance, a good way to do it would be to compute needed inverses in updateControl(), and use them in updateAudio().
19
+
in the following) and its precision by the number of bits encoding the fractional part (NF). For UFix types, the integral part can hold values in [0,2^NI-1], for SFix types, the integral part can hold values in [-2^NI,2^NI-1]. The number of bits encoding the fractional can be considered as the precision of the number: given NF, the number of possible values in the [0,1[ range will 2^NF. Hence, given NF, the resolution will be 1/(2^NF).
20
+
21
+
Under the hood, these types will keep track of the maximum possible value they might hold (this is the RANGE template parameter), and, if only *SAFE* operations (see below) are used, will automatically adjust there NI and NF to accomodate the result of a operation. It will also try not to promote there internal type when possible, assuFM_ming that you use the complete range of a given type.
22
+
23
+
The operations possible with these types can be divided into two categories:
24
+
- the operations between FixMath types are all safe (aka won't overflow) and are the only one included by default
25
+
- the operations between a FixMath and a native C type (int, float) are NOT safe and are not included by default. In order to activate them, you need to `#define FIXMATH_UNSAFE` before including FixMath.h.
26
+
27
+
28
+
Like standard C(++) types, the fixed point numbers defined here are following some rules:
29
+
- any fixed type can be converted to another *as long as the value can be represented in the destination type*. Casting to a bigger type in term of NI and NF is safe, but reducing NI can lead to an overflow if the new type cannot hold the integer value and reducing NF leads to a loss of precision.
30
+
- Fixed types can be constructed from and converted to standard C types:
31
+
- `UFix<NI,NF>(T value)` will convert the `value` to a `UFix`. If T is an integer type the final number will have a fractional part equal to zero. This can be used as a standard type, for example: `UFix<8,8> a = 15;` or `UFix<8,8> b = 200.25;`
32
+
- same for `SFix`
33
+
- `UFix<NI,NF>::fromRaw(T value)` will set the *internal* value of the `UFix`. For example `UFix<7,1>::fromRaw(16);` is actually 8
34
+
- same for `SFix`
35
+
- `UFix<NI,NF>.toFloat()` returns the value as a `float`
36
+
- same for `SFix`
37
+
- `UFix<NI,NF>.asRaw()` returns the internal value
38
+
- same for `SFix`
39
+
- all operations between fixed point number is safe (it won't overflow) and preserve the precision. In particular:
40
+
- only addition, subtraction and multiplication are implemented (this is a design choice, see below)
41
+
- any operation between a signed and an unsigned leads to a signed number
42
+
- resulting numbers will be casted to a type big enough to store the expected values. It follows that it is worth starting with types that are as small as possible to hold the initial value.
43
+
- all operations between a fixed point number and a native type (int, float, uint) are *not* safe. If the resulting value cannot be represented in the fixed point type it will overflow. Only addition, subtraction, multiplication and right/left shift are implemented. These are only accessible activating the `FIXMATH_UNSAFE` set.
44
+
- safe right/left shifts, which return the correct value in the correct type are implemented as .sR<shift>() and .sL<shift>() respectively, shift being the shifting amount.
45
+
46
+
More specifically on the returned types of the operations between fixed point math types:
47
+
- Additions:
48
+
- `UFix<NI,NF> + UFix<_NI,_NF>` returns `UFix<MAX(NI,_NI)+1,MAX(NF,_NF)>` at worse
49
+
- `SFix<NI,NF> + SFix<_NI,_NF>` returns `SFix<MAX(NI,_NI)+1,MAX(NF,_NF)>` at worse
50
+
- `UFix<NI,NF> + SFix<_NI,_NF>` returns `SFix<MAX(NI,_NI)+1,MAX(NF,_NF)>` at worse
51
+
- `UFix<NI,NF> + anything_else` (signed or not) returns `UFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
52
+
- `SFix<NI,NF> + anything_else` (signed or not) returns `SFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
53
+
- Subtractions:
54
+
- `UFix<NI,NF> - UFix<_NI,_NF>` returns `SFix<MAX(NI,_NI),MAX(NF,_NF)>` at worse
55
+
- `SFix<NI,NF> - SFix<_NI,_NF>` returns `SFix<MAX(NI,_NI)+1,MAX(NF,_NF)>` at worse
56
+
- `SFix<NI,NF> - UFix<_NI,_NF>` returns `SFix<MAX(NI,_NI)+1,MAX(NF,_NF)>` at worse
57
+
- `UFix<NI,NF> - anything_else` (signed or not) returns `UFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
58
+
- `SFix<NI,NF> - anything_else` (signed or not) returns `SFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
59
+
- `(-)SFix<NI,NF>` return `SFix<NI,NF>`
60
+
- `(-)UFix<NI,NF>` return `SFix<NI,NF>`
61
+
- Multiplications:
62
+
- `UFix<NI,NF> * UFix<_NI,_NF>` returns `UFix<NI+_NI,NF+_NF>` at worse
63
+
- `UFix<NI,NF> * SFix<_NI,_NF>` returns `SFix<NI+_NI,NF+_NF>` at worse
64
+
- `SFix<NI,NF> * SFix<_NI,_NF>` returns `SFix<NI+_NI,NF+_NF>` at worse
65
+
- `UFix<NI,NF> * anything_else` (signed or not) returns `UFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
66
+
- `SFix<NI,NF> * anything_else` (signed or not) returns `SFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
- `UFix<NI,NF> >> N` returns `UFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
72
+
- `UFix<NI,NF> << N` returns `UFix<NI,NF>` (only available with `FIXMATH_UNSAFE`)
73
+
- same for `SFix`
74
+
- Inverse:
75
+
- `UFix<NI,NF>.invFast()` returns the inverse of the number as `UFix<NF,NI>`
76
+
- `SFix<NI,NF>.invFast()` returns the inverse of the number as `SFix<NF,NI>`
77
+
- `UFix<NI,NF>.invAccurate()` returns the inverse of the number as `UFix<NF,2*NI+NF>`
78
+
- `SFix<NI,NF>.invAccurate()` returns the inverse of the number as `SFix<NF,2*NI+NF>`
79
+
- `UFix<NI,NF>.inv<_NF>()` returns the inverse of the number as `UFix<NF,_NF>`
80
+
- `SFix<NI,NF>.inv<_NF>()` returns the inverse of the number as `SFix<NF,_NF>`
81
+
- Conversion (should be preferred over casting, when possible):
82
+
- `UFix<NI,NF>.asSFix()` returns `SFix<NI,NF>`
83
+
- `SFix<NI,NF>.asUFix()` returns `UFix<NI,NF>`
84
+
- `UFix<NI,NF>.asFloat()` returns the value as a `float`
85
+
- `SFix<NI,NF>.asFloat()` returns the value as a `float`
86
+
- `UFix<NI,NF>.asRaw()` returns the internal value
87
+
- `SFix<NI,NF>.asRaw()` returns the internal value
88
+
- `T.toUFraction()` returns `UFix<0,NF>` with `NF` the number of bits of `T` (`uint8_t` leads to `NF=8`bits).
89
+
- `T.toSFraction()` returns `SFix<0,NF>` with `NF` the number of bits of `T` (`int8_t` leads to `NF=7`bits).
90
+
- `T.toUInt()` returns `UFix<NI,0>` with `NI` the number of bits of `T` (`uint8_t` leads to `NI=8`bits).
91
+
- `T.toSInt()` returns `SFix<NI,>` with `NI` the number of bits of `T` (`int8_t` leads to `NI=7`bits).
92
+
93
+
Note on division:
94
+
The division is not implemented. This is a deliberate choice made for two reasons:
95
+
- in contrast with all the other fundamental operations, it is not possible to guarantee that precision will be kept (other operations returns *exact* results whenever the operands were also exactly represented. Note that this is actually not the case when using normal floating point numbers. The inverse functions can be used to fake a division, by multiplying by the inverse of a number.
96
+
- division are usually very slow operations on MCU, hence there usage is discouraged. The ideal way of doing it is to compute the inverse whenever needed and only when needed. In the context of Mozzi for instance, a good way to do it would be to compute needed inverses in `updateControl()`, and use them in `updateAudio()`.
0 commit comments