Arduino's map() function has always left quite a bit to be desired. They've had a long standing bug report about the issues, and of course numerous times people have discussed using floating point.
Well, I decided it's time to put a better map() function into Teensyduino. At least I hope it's better....
For a little background, here's a simple test program:
This is supposed to map 0-7 onto 0-3. But here's what you actually get:Code:void setup() { while (!Serial) ; Serial.println("map function test"); long in_min = 0; long in_max = 7; long out_min = 0; long out_max = 3; for (int i=in_min-5; i <= in_max+5; i++) { Serial.print(i); Serial.print(" --> "); Serial.println(map(i, in_min, in_max, out_min, out_max)); } } void loop() { }
So the first big problem is integer truncation is handled poorly.map function test
-5 --> -2
-4 --> -1
-3 --> -1
-2 --> 0
-1 --> 0
0 --> 0
1 --> 0
2 --> 0
3 --> 1
4 --> 1
5 --> 2
6 --> 2
7 --> 3
8 --> 3
9 --> 3
10 --> 4
11 --> 4
12 --> 5
The other big problem is not support at all for floats. If the variable you're trying to map to a new range is a float, it gets converted to an integer and then put through this poor integer math.
After much experimenting with C++ function overloading, I finally ended up with a C++ template solution using C++11 type trairs and SFINAE. The template syntax looks horribly complex.
https://github.com/PaulStoffregen/co...58d2e438233f12
With this approach we can have st42's proposed improvement to the integer algorithm, which is:
and when the input number is a float or double, the original math gets computed as 32 or 64 bit floating point.Code:if ((in_max - in_min) > (out_max - out_min)) { return (x - in_min) * (out_max - out_min+1) / (in_max - in_min+1) + out_min; } else { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }
The integer case becomes:
Now float can finally be used for map too:map function test
-5 --> -2
-4 --> -2
-3 --> -1
-2 --> -1
-1 --> 0
0 --> 0
1 --> 0
2 --> 1
3 --> 1
4 --> 2
5 --> 2
6 --> 3
7 --> 3
8 --> 4
9 --> 4
10 --> 5
11 --> 5
12 --> 6
This change is scheduled to become part of Teensyduino 1.37.... unless you talk me out of it.map function test
-5 --> -2.14
-4 --> -1.71
-3 --> -1.29
-2 --> -0.86
-1 --> -0.43
0 --> 0.00
1 --> 0.43
2 --> 0.86
3 --> 1.29
4 --> 1.71
5 --> 2.14
6 --> 2.57
7 --> 3.00
8 --> 3.43
9 --> 3.86
10 --> 4.29
11 --> 4.71
12 --> 5.14
Or maybe there's some way the integer round off could be handled even better? Is st42's way really the best? It seems better than all the other alternatives, but it does round towards zero and gives more values at zero when the target range spans negative to positive.