Skip to content

Commit 5c89d6b

Browse files
committed
fix #15, support decreasing unsigned int output array
1 parent 5e8ad68 commit 5c89d6b

10 files changed

Lines changed: 251 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88

9+
## [0.4.0] - 2026-05-25
10+
- fix #15, decreasing unsigned out array's cause underflow
11+
- add multimap_decr_unsigned_test.ino test for #15
12+
- minor edits
13+
14+
----
15+
916
## [0.3.0] - 2026-02-23
1017
- fix #13, allow size to be 65535 (uint16_t)
11-
- add patch for possible interpolation under/overflows.
18+
- add patch for possible interpolation under/overflows.
1219
- add multi type **multiMapCache<T1, T2>** as it was missing.
1320
- update readme.md
1421
- update GitHub actions

MultiMap.h

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
//
33
// FILE: MultiMap.h
44
// AUTHOR: Rob Tillaart
5-
// VERSION: 0.3.0
5+
// VERSION: 0.4.0
66
// DATE: 2011-01-26
77
// PURPOSE: Arduino library for fast non-linear mapping or interpolation of values
88
// URL: https://github.com/RobTillaart/MultiMap
99
// URL: http://playground.arduino.cc/Main/MultiMap
1010

1111

1212

13-
#define MULTIMAP_LIB_VERSION (F("0.3.0"))
13+
#define MULTIMAP_LIB_VERSION (F("0.4.0"))
1414

1515

1616
#include "Arduino.h"
@@ -36,7 +36,16 @@ T multiMap(T value, T* _in, T* _out, uint16_t size)
3636
if (value == _in[pos]) return _out[pos];
3737

3838
// interpolate in the right segment for the rest
39-
return (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
39+
// use a modified formula for decreasing output array segment
40+
// to prevent unsigned "underflow/overflow" (issue #15)
41+
if (_out[pos] >= _out[pos-1])
42+
{
43+
return _out[pos-1] + (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]);
44+
}
45+
else
46+
{
47+
return _out[pos-1] - (value - _in[pos-1]) * (_out[pos-1] - _out[pos]) / (_in[pos] - _in[pos-1]);
48+
}
4049
// if interpolation overflows use this line
4150
// return T(float(value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1])) + _out[pos-1];
4251
}
@@ -85,9 +94,16 @@ T multiMapCache(T value, T* _in, T* _out, uint16_t size)
8594
else
8695
{
8796
// interpolate in the right segment for the rest
88-
cache = (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
89-
// if interpolation overflows use this line
90-
// cache = T(float(value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1])) + _out[pos-1];
97+
// use a modified formula for decreasing output array segment
98+
// to prevent unsigned "underflow/overflow" (issue #15)
99+
if (_out[pos] >= _out[pos-1])
100+
{
101+
cache = _out[pos-1] + (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]);
102+
}
103+
else
104+
{
105+
cache = _out[pos-1] - (value - _in[pos-1]) * (_out[pos-1] - _out[pos]) / (_in[pos] - _in[pos-1]);
106+
}
91107
}
92108
}
93109
return cache;
@@ -119,7 +135,16 @@ T multiMapBS(T value, T* _in, T* _out, uint16_t size)
119135
else upper = mid;
120136
}
121137
// interpolate in the right segment for the rest
122-
return (value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower]) + _out[lower];
138+
// use a modified formula for decreasing output array segment
139+
// to prevent unsigned "underflow/overflow" (issue #15)
140+
if (_out[upper] >= _out[lower])
141+
{
142+
return _out[lower] + (value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower]);
143+
}
144+
else
145+
{
146+
return _out[lower] - (value - _in[lower]) * (_out[lower] - _out[upper]) / (_in[upper] - _in[lower]);
147+
}
123148
// if interpolation overflows use this line
124149
// return T(float(value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower])) + _out[lower];
125150
}
@@ -145,7 +170,16 @@ T2 multiMap(T1 value, T1* _in, T2* _out, uint16_t size)
145170
if (value == _in[pos]) return _out[pos];
146171

147172
// interpolate in the right segment for the rest
148-
return (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
173+
// use a modified formula for decreasing output array segment
174+
// to prevent unsigned "underflow/overflow" (issue #15)
175+
if (_out[pos] >= _out[pos-1])
176+
{
177+
return _out[pos-1] + (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]);
178+
}
179+
else
180+
{
181+
return _out[pos-1] - (value - _in[pos-1]) * (_out[pos-1] - _out[pos]) / (_in[pos] - _in[pos-1]);
182+
}
149183
// if interpolation overflows use this line
150184
// return T2(float(value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1])) + _out[pos-1];
151185
}
@@ -194,7 +228,17 @@ T2 multiMapCache(T1 value, T1* _in, T2* _out, uint16_t size)
194228
else
195229
{
196230
// interpolate in the right segment for the rest
197-
cache = (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
231+
// use a modified formula for decreasing output array segment
232+
// to prevent unsigned "underflow/overflow" (issue #15)
233+
if (_out[pos] >= _out[pos-1])
234+
{
235+
cache = _out[pos-1] + (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]);
236+
237+
}
238+
else
239+
{
240+
cache = _out[pos-1] - (value - _in[pos-1]) * (_out[pos-1] - _out[pos]) / (_in[pos] - _in[pos-1]);
241+
}
198242
// if interpolation overflows use this line
199243
// return T2(float(value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1])) + _out[pos-1];
200244
}
@@ -227,7 +271,16 @@ T2 multiMapBS(T1 value, T1* _in, T2* _out, uint16_t size)
227271
else upper = mid;
228272
}
229273
// interpolate in the right segment for the rest
230-
return (value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower]) + _out[lower];
274+
// use a modified formula for decreasing output array segment
275+
// to prevent unsigned "underflow/overflow" (issue #15)
276+
if (_out[upper] >= _out[lower])
277+
{
278+
return _out[lower] + (value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower]);
279+
}
280+
else
281+
{
282+
return _out[lower] - (value - _in[lower]) * (_out[lower] - _out[upper]) / (_in[upper] - _in[lower]);
283+
}
231284
// if interpolation overflows use this line
232285
// return T2(float(value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower])) + _out[lower];
233286
}

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ multiple linear line segments.
4343
Of course this approximation introduces an error.
4444
By increasing the number of points and choose their position strategically the average error
4545
can and will be reduced.
46-
An important feature of the **multiMap()** is that the points do not need to have the same
47-
distance (non-equidistant). This allows to have more pointe where needed (curvy line) and
48-
less point where possible (straight lines).
46+
An important feature of the **multiMap()** is that the input points do not need to have the same
47+
distance (non-equidistant). This allows to have more data pointe where needed (curvy segments) and
48+
less point where possible (straight segments).
4949

5050
Note: some functions are hard to approximate even with **multiMap()** as they go to infinity
5151
or have a singularity.
@@ -57,24 +57,24 @@ However there might be more than one input value mapping onto the same output va
5757
See - https://en.wikipedia.org/wiki/Bijection,_injection_and_surjection
5858

5959

60-
### Math problems in interpolation (rare).
60+
### Math problems in interpolation. (rare)
6161

6262
In multiMap the interpolation math can fail. This happens when integer types are used
63-
that are too small to handle the multiplication in the interpolation (e.g. 8 or 16 bit)
64-
Also when a mixed signed / unsigned types are used this can happen.
63+
that are **too small** to handle the multiplication in the interpolation (e.g. 8 or 16 bit)
64+
Also when a **mixed signed / unsigned** types are used failure can happen.
6565

66-
There are two solutions to handle this.
66+
There are solutions to handle this.
6767
- (preferred) use float (double) as either input or output type.
6868
This forces casting to float in the interpolation solving the math problem.
6969
Drawback is it might take extra memory (e.g. int16_t => float).
7070
- The multiMap.h file contains **commented** code to cast the interpolation
7171
during the interpolation only.
7272
Comment the existing interpolation and uncomment the other line.
7373
This saves memory as the arrays used do not "grow".
74+
- The use of **decreasing unsigned** variables for the output array causes overflow
75+
due to subtraction in the formula used. (issue #15, solved in 0.4.0)
7476

75-
Note both solutions have a performance penalty
76-
77-
See the sketch **multimap_demo_fail.ino** (UNO R3) to show the problem.
77+
Note some solutions have a performance penalty
7878

7979

8080
### 0.3.0 Breaking change

examples/multimap_2d/multimap_2d.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ void setup()
3232

3333
for (int i = 0; i <= 360; i++)
3434
{
35-
// 2D mapping is doen by mapping twice (not too efficient but it works sort of.
35+
// 2D mapping is done by mapping twice (not too efficient but it works sort of.
3636
float x = multiMap<float>(i, in, xc, size);
3737
float y = multiMap<float>(i, in, yc, size);
3838
// Serial.print(i);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
BOARD: UNO R3
2+
IDE: 1.8.19
3+
4+
5+
Arduino\libraries\MultiMap\examples\multimap_BS_compare\multimap_BS_compare.ino
6+
MULTIMAP_LIB_VERSION: 0.4.0
7+
8+
size t1 t2 ratio
9+
4 5732 5648 98.53
10+
5 6288 6260 99.55
11+
6 6868 6904 100.52
12+
7 7456 7540 101.13
13+
8 8064 8188 101.54
14+
9 8704 8828 101.42
15+
10 9356 9488 101.41
16+
11 10032 10152 101.20
17+
12 10720 10812 100.86
18+
13 11428 11476 100.42
19+
14 12156 12148 99.93
20+
15 12908 12804 99.19
21+
16 13680 13472 98.48
22+
17 14464 14140 97.76
23+
18 15268 14828 97.12
24+
19 16096 15508 96.35
25+
20 16944 16188 95.54
26+
21 17804 16880 94.81
27+
22 18684 17568 94.03
28+
23 19592 18256 93.18
29+
24 20516 18940 92.32
30+
25 21456 19632 91.50
31+
26 22416 20320 90.65
32+
27 23396 21008 89.79
33+
28 24400 21688 88.89
34+
29 25420 22384 88.06
35+
30 26460 23072 87.20
36+
31 27520 23756 86.32
37+
32 28600 24444 85.47
38+
33 29696 25136 84.64
39+
34 30808 25840 83.87
40+
35 31948 26548 83.10
41+
36 33100 27260 82.36
42+
37 34276 27968 81.60
43+
38 35472 28676 80.84
44+
39 36684 29380 80.09
45+
40 37916 30096 79.38
46+
41 39168 30804 78.65
47+
42 40436 31512 77.93
48+
43 41732 32216 77.20
49+
44 43040 32932 76.51
50+
45 44368 33644 75.83
51+
46 45724 34352 75.13
52+
47 47088 35060 74.46
53+
48 48476 35768 73.78
54+
49 49880 36488 73.15
55+
50 51308 37200 72.50
56+
51 52752 37904 71.85
57+
52 54220 38608 71.21
58+
53 55708 39320 70.58
59+
54 57208 40036 69.98
60+
55 58728 40748 69.38
61+
56 60272 41452 68.77
62+
57 61836 42164 68.19
63+
58 63416 42880 67.62
64+
59 65020 43584 67.03
65+
60 66636 44296 66.47
66+
61 68276 45012 65.93
67+
62 69936 45724 65.38
68+
63 71616 46432 64.83
69+
64 73308 47144 64.31
70+
65 75028 47856 63.78
71+
66 76764 48584 63.29
72+
67 78520 49316 62.81
73+
68 80300 50048 62.33
74+
69 82088 50780 61.86
75+
70 83904 51508 61.39
76+
71 85736 52236 60.93
77+
72 87592 52972 60.48
78+
73 89456 53708 60.04
79+
74 91352 54432 59.58
80+
75 93260 55168 59.16
81+
76 95192 55892 58.72
82+
77 97140 56628 58.30
83+
78 99104 57364 57.88
84+
79 101092 58096 57.47
85+
80 103104 58832 57.06
86+
81 105124 59568 56.66
87+
82 107180 60284 56.25
88+
83 109240 61028 55.87
89+
84 111332 61752 55.47
90+
85 113436 62488 55.09
91+
86 115548 63224 54.72
92+
87 117700 63956 54.34
93+
88 119860 64688 53.97
94+
89 122048 65424 53.61
95+
90 124240 66156 53.25
96+
91 126464 66888 52.89
97+
92 128704 67616 52.54
98+
93 130964 68356 52.19
99+
94 133240 69092 51.86
100+
95 133404 69180 51.86
101+
96 133404 69208 51.88
102+
97 133404 69260 51.92
103+
98 133404 69260 51.92
104+
99 133408 69280 51.93
105+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//
2+
// FILE: multimap_decr_unsigned_test.ino
3+
// AUTHOR: Rob Tillaart
4+
// PURPOSE: minimal demo
5+
// URL: https://github.com/RobTillaart/MultiMap
6+
//
7+
// test for issue #15, decreasing unsigned
8+
9+
#include "MultiMap.h"
10+
11+
// note the IN array is not equidistant.
12+
// layout is to see the points of the graph.
13+
float in[] = { 0, 20, 40, 50, 60, 80, 90, 100, 105 };
14+
uint16_t out[] = { 1000, 900, 800, 600, 300, 150, 75, 40, 20 };
15+
16+
int size = 9;
17+
18+
19+
void setup()
20+
{
21+
// while(!Serial);
22+
Serial.begin(115200);
23+
Serial.println();
24+
Serial.println(__FILE__);
25+
Serial.print("MULTIMAP_LIB_VERSION: ");
26+
Serial.println(MULTIMAP_LIB_VERSION);
27+
Serial.println();
28+
29+
Serial.println("X\tY");
30+
31+
for (int i = 0; i <= 500; i++)
32+
{
33+
float x = i * 0.2;
34+
float y = multiMap<float, uint16_t>(x, in, out, size);
35+
Serial.print(x);
36+
Serial.print("\t");
37+
Serial.println(y, 1); // 1 decimal
38+
}
39+
}
40+
41+
42+
void loop()
43+
{
44+
}
45+
46+
47+
// -- END OF FILE --

examples/multimap_demo_fail/multimap_demo_fail.ino

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
// due to a mix of signed and unsigned math.
1111
// If you need this mapping and it fails for you
1212
// the interpolation in MultiMap.h needs the casting line.
13+
//
14+
// 0.4.0 seems to have solved this bug
1315

1416

1517
#include "MultiMap.h"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
BOARD: UNO R3
3+
IDE: 1.8.19
4+
5+
...Arduino\libraries\MultiMap\examples\multimap_timing\multimap_timing.ino
6+
MULTIMAP_LIB_VERSION: 0.4.0
7+
8+
time <int>: 20
9+
121.0000
10+
time <float>: 92
11+
121.0909
12+
13+
done...

library.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"type": "git",
1616
"url": "https://github.com/RobTillaart/MultiMap.git"
1717
},
18-
"version": "0.3.0",
18+
"version": "0.4.0",
1919
"license": "MIT",
2020
"frameworks": "*",
2121
"platforms": "*",

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=MultiMap
2-
version=0.3.0
2+
version=0.4.0
33
author=Rob Tillaart <rob.tillaart@gmail.com>
44
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
55
sentence=Library for fast non-linear interpolation by means of two arrays.

0 commit comments

Comments
 (0)