Forum Rule: Always post complete source code & details to reproduce any issue!

1. ## Map function teensy

Hi All,
I'm using the map function on a teensy 3.2 & 3.5 but it's a littlebit confusing.
What I do:

//declare
byte R=3; // for example
byte P=0; // for example
byte I;
byte Y;

//in void loop
Y = map(I,0,127,R,P);

.////////
When i put in : I = 0 , Y only gets 2 or 1, (I forgot and Im not with the setup now) instead of getting 3 I was expecting.

When I do :
Y= map(I,127,0,P,R); and I= 0, Y Gets 3 as expected.
So I just swapped the low & high on both ranges. It works but I just don't understand it. Is this the right behaviour ?
Can anybody explain this?

I am using arduino 1.8.7 and 4+ teensduino.

2. Works for me on a T3.6
Code:
```void setup(void)
{
byte R=3; // for example
byte P=0; // for example
byte I;
byte Y;

Serial.begin(9600);
while(!Serial);
delay(1000);

Y = map(0,0,127,R,P);
Serial.println(Y);
}

void loop(void)
{
}```
Pete

3. and it also works with this:
Code:
```  I = 0;
Y = map(I,0,127,R,P);```
Pete

4. Thanks for your quick help Pete!

I tried your code also, and I still have strange behavior.
I also did a fresh install for arduino 1.8.9 and teensy (1.46) and have no other copies on my mac (mojave) and Teensy 3.5 board.

//////////////////
void setup(void)
{
byte R=3; // for example
byte P=0; // for example
byte I=127;
byte Y;

Serial.begin(9600);
while(!Serial);
delay(1000);

Y = map(I,0,127,R,P);
Serial.println(Y);
}

void loop(void)
{
}
////////////

Now Y gets only 2 instead of 0! So there is the problem.

When I change I to 0 :
Y gets 3 as expected.

Then again:
When I change the code to :
Y = map(I,127,0,P,R);
It all works as expected, so when I = 0 then Y gets 3, when I =127 Y gets 0 !
So strange I have to swap the low/high order to have expected output.

Do you get the same results like me ?

Kind regards
Jeroen

Originally Posted by el_supremo
and it also works with this:
Code:
```  I = 0;
Y = map(I,0,127,R,P);```
Pete

5. To keep it more simple, so you can forget my first posts :

Y = map(127,0,127,3,0); >> Y = 2 instead of 0! Why? Using an arduino mega for example you will get 0!

Y = map(127,127,0,0,3); >> Y = 0 as expected, but I changed the order in the map function as you can see.

Does anybody get the same results? Is this normal?

p.s.
fresh installed arduino 1.8.9 and teensy (1.46) and no other copies on my mac (mojave) using Teensy 3.5.

6. Maybe try the math: Arduino typical code for map: return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
return )

Y = map(127,0,127,3,0)
return (127-0)*(0-3)/(127-0) + 3 = 127*-3/127 +3 = 0.

Teensy version:
Code:
```template <class T, class A, class B, class C, class D>
long map(T _x, A _in_min, B _in_max, C _out_min, D _out_max, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
long x = _x, in_min = _in_min, in_max = _in_max, out_min = _out_min, out_max = _out_max;
//return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
// st42's suggestion: https://github.com/arduino/Arduino/issues/2466#issuecomment-69873889
// more conversation:
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;
}
}
// when the input is a float or double, do all math using the input's type```
in_max-in_min = 127-0 = 127 out_max-out_min = 0-3 = -3
(127-0)*(0-3+1)/(127-0+1) + 3 = 127*-2/128 +3 = -1 +3 = 2
Which is wrong...

I believe the last change to this code here was done 2 years ago... @Paul - not sure of the best solution for this.

7. Originally Posted by KurtE
not sure of the best solution for this
The range expansion (+1's) should have the same sign as the range itself, i.e.
Code:
```template <class T, class A, class B, class C, class D>
long map(T _x, A _in_min, B _in_max, C _out_min, D _out_max, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
long x = _x, in_min = _in_min, in_max = _in_max, out_min = _out_min, out_max = _out_max;
long in_range = in_max - in_min, out_range = out_max - out_min;
if (in_range < 0) {
out_range = -out_range;
in_range = -in_range;
} else
if (in_range == 0)
return (out_min + out_max) / 2;

if (in_range <= out_range || in_range <= -out_range)
return (x - in_min) * out_range / in_range + out_min;

in_range++;
if (out_range > 0)
out_range++;
else
if (out_range < 0)
out_range--;

return (x - in_min) * out_range / in_range + out_min;
}```
This maps 0..127 to 3..0 yielding 0, 1, 2, and 3 each 32 times.

However, I prefer a more mathematically robust argument, based on y = ((x - xmin) * (ymax - ymin) + r) / (xmax - xmin), where r has the same sign as ymax - ymin, but has magnitude just less than half that of xmax - xmin.

Essentially, 1 - abs(r/(xmax-xmin)) indicates the smallest real that is rounded upwards to one; this is because integer division discards the fraction.

Code-wise, that would be something like
Code:
```template <class T, class A, class B, class C, class D>
long map(T _x, A _x_min, B _x_max, C _out_from, D _out_to, typename std::enable_if<std::is_integral<T>::value >::type* = 0)
{
long x = _x, x_min = _x_min, x_max = _x_max, out_from = _out_from, out_to = _out_to;
long x_range = x_max - x_min, out_range = out_to - out_from, r;

// (1 - abs(r/x_range)) is the smallest fraction rounded away from zero.
// The sign of r is the same as the sign of x_range*out_range.
if (x_range > 0) {
if (out_range > 0) {
r = (x_range - 1) / 2;
} else
if (out_range < 0) {
r = (1 - x_range) / 2;
} else {
r = 0;
}
} else
if (x_range < 0) {
if (out_range > 0) {
r = (x_range + 1) / 2;
} else
if (out_range < 0) {
r = (-1 - x_range) / 2;
} else {
r = 0;
}
} else {
r = 0;
}

return out_from + ((x - x_min) * out_range + r) / x_range;
}```
I don't like the ifs, but if you assume two's complement for signed integers and that signed right shift can copy the sign bit to all significant bits, you can calculate r without conditionals. I'm not sure if the obfuscation that causes is warranted.

Note that this version maps 0..127 to 3..0 yielding 3 22 times, 2 42 times, 1 42 times, and 0 22 times; yielding the same as round(map((float)x, 0.0f, 127.0f, 3.0f, 0.0f))would.

(@Paul: feel free to use. I consider the above to be in public domain, if they are even copyrightable, which I doubt.)