Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: Map function teensy

  1. #1
    Junior Member
    Join Date
    Feb 2017
    Posts
    3

    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.
    Last edited by microsurfer; 05-30-2019 at 10:55 PM. Reason: Typing misstaken

  2. #2
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,080
    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. #3
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,080
    and it also works with this:
    Code:
      I = 0;
      Y = map(I,0,127,R,P);
    Pete

  4. #4
    Junior Member
    Join Date
    Feb 2017
    Posts
    3
    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



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

  5. #5
    Junior Member
    Join Date
    Feb 2017
    Posts
    3
    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.

  6. #6
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    4,677
    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.

  7. #7
    Quote Originally Posted by KurtE View Post
    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 by Nominal Animal; 06-04-2019 at 06:01 PM. Reason: Fixed the bug in the nested ifs.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •