Map function teensy

Status
Not open for further replies.

microsurfer

New member
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.
 
Last edited:
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
 
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.

When I use your code:
//////////////////
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



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

Pete
 
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?

Thanks in advance !

p.s.
fresh installed arduino 1.8.9 and teensy (1.46) and no other copies on my mac (mojave) using Teensy 3.5.
 
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;
	// Arduino's traditional algorithm
	//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:
	// https://forum.pjrc.com/threads/44503-map()-function-improvements
	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.
 
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.)
 
Last edited:
Status
Not open for further replies.
Back
Top