{"id":120,"date":"2011-09-12T22:36:11","date_gmt":"2011-09-13T02:36:11","guid":{"rendered":"http:\/\/www.jetmore.org\/john\/blog\/?p=120"},"modified":"2012-05-11T15:15:35","modified_gmt":"2012-05-11T19:15:35","slug":"arduinos-map-function-and-numeric-distribution","status":"publish","type":"post","link":"https:\/\/www.jetmore.org\/john\/blog\/2011\/09\/arduinos-map-function-and-numeric-distribution\/","title":{"rendered":"Arduino&#8217;s map() Function and Numeric Distribution"},"content":{"rendered":"<p>The Arduino <a href=\"http:\/\/www.arduino.cc\/en\/Reference\/Map\">map() function<\/a> is an interesting beast. Very technically it works exactly as its documented to work, but not the way almost every example uses it.<\/p>\n<p>Here&#8217;s an example you can find in hundreds of sketches online, including the actual documentation for map():<\/p>\n<pre class=\"brush: cpp; light: true; title: ; notranslate\" title=\"\">\r\nval = map(val, 0, 1023, 0, 255);\r\n<\/pre>\n<p>This is a simple map, and one would expect that every four ticks on the input would map to one tick on the output (that is, {0,1,2,3} -&gt; 0, {4,5,6,7} -&gt; 1, etc). But that&#8217;s not what the function above actually does.<\/p>\n<p>To show the issue, let&#8217;s make the output range smaller (but still an even divisor):<\/p>\n<pre class=\"brush: cpp; light: true; title: ; notranslate\" title=\"\">\r\nval = map(val, 0, 1023, 0, 15);\r\n<\/pre>\n<p>This should result in an even distribution, 64 input ticks per one output tick. To test this, I wrote a quick script implementing the Arduino map logic (which they were nice enough to document):<\/p>\n<pre class=\"brush: cpp; light: true; title: ; notranslate\" title=\"\">\r\nlong map(long x, long in_min, long in_max, long out_min, long out_max)\r\n{\r\n  return (x - in_min) * (out_max - out_min) \/ (in_max - in_min) + out_min;\r\n}\r\n<\/pre>\n<p>The script prints a table of output values and the number of times that value was returned. Here&#8217;s the output for map(?, 0, 1023, 0, 15) for each value in 0..1023:<\/p>\n<pre class=\"brush: plain; highlight: [1]; light: true; title: ; notranslate\" title=\"\">\r\nmap(0..1023, 0, 1023, 0, 15);\r\n  0   69\r\n  1   68\r\n  2   68\r\n  3   68\r\n  4   68\r\n  5   69\r\n  6   68\r\n  7   68\r\n  8   68\r\n  9   68\r\n 10   69\r\n 11   68\r\n 12   68\r\n 13   68\r\n 14   68\r\n 15    1\r\n<\/pre>\n<p>That&#8217;s definitely not an even distribution. Now here&#8217;s a really stark example:<\/p>\n<pre class=\"brush: plain; highlight: [1]; light: true; title: ; notranslate\" title=\"\">\r\nmap(0..1023, 0, 1023, 0, 1);\r\n  0 1023\r\n  1    1\r\n<\/pre>\n<p>That&#8217;s a pretty egregious imbalance.<\/p>\n<p>I mentioned earlier that the function&#8217;s actually working how it&#8217;s documented to work, just not how it&#8217;s usually used in examples. The map() docs state that &#8220;[t]he map() function uses integer math so will not generate fractions, when the math might indicate that it should do so. Fractional remainders are truncated, and are not rounded or averaged.&#8221;<\/p>\n<p>This completely makes sense &#8211; if you imagine a range of 1024 values between 0 and one, all of them will be less than 1 except the last value, and since it&#8217;s integer arithmetic, all the less-than-1 values are 0.<\/p>\n<p>The solution is fairly simple &#8211; increase the in_max and out_max args by one more than the actual maximum value (and then wrap the output in constrain(), which you ought to have done anyway). It&#8217;s fairly easy to work through why this works in your head, but here are the same examples I gave above with the increased maximums:<\/p>\n<pre class=\"brush: plain; highlight: [1,4]; light: true; title: ; notranslate\" title=\"\">\r\nmap(0..1023, 0, 1024, 0, 2);\r\n  0   512\r\n  1   512\r\nmap(0..1023, 0, 1024, 0, 16);\r\n  0   64\r\n  1   64\r\n  2   64\r\n  3   64\r\n  4   64\r\n  5   64\r\n  6   64\r\n  7   64\r\n  8   64\r\n  9   64\r\n 10   64\r\n 11   64\r\n 12   64\r\n 13   64\r\n 14   64\r\n 15   64\r\n<\/pre>\n<p>I have worked through this and I now understand how to get the values I want out of constrain. What I don&#8217;t know, however, is why the docs and the examples don&#8217;t address this issue. I know I&#8217;m not the first person to find this issue because I&#8217;ve found sketches on the internet where people are doing this (and they&#8217;re doing it in a way that leads me to believe they&#8217;re doing it on purpose), but I couldn&#8217;t get Google to show me any pages where someone actually addresses this point. I wonder why?<\/p>\n<p>As for why I was looking into this, I was playing around with a couple of pots and an LCD, making a fakey etch-a-sketch using the 2-row LCD I have for such things. I could not figure out why my vertical control would only drop me down a line when I had the pot turned 100%. Now I know&#8230;<\/p>\n<p><strong>Edit 2011-09-13:<\/strong><\/p>\n<p>I <a href=\"http:\/\/arduino.cc\/forum\/index.php\/topic,72153.0.html\">posted<\/a> this in the arduino.cc forum last night. As I expected, this is an issue known in the arduino community. Among other things, I was referenced to <a href=\"http:\/\/www.arduino.cc\/cgi-bin\/yabb2\/YaBB.pl?num=1257716581\">this post<\/a> discussing the issue. My problem with all of this isn&#8217;t so much that it&#8217;s odd behavior, but that it&#8217;s been odd for at least 2 years and there&#8217;s no mention of the oddness in the map() documentation. The docs are a wiki, so I proposed an addition in the forum thread, maybe I&#8217;ll get to add it and help someone out in the future.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Arduino map() function is an interesting beast. Very technically it works exactly as its documented to work, but not the way almost every example uses it. Here&#8217;s an example you can find in hundreds of sketches online, including the &hellip; <a href=\"https:\/\/www.jetmore.org\/john\/blog\/2011\/09\/arduinos-map-function-and-numeric-distribution\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,5],"tags":[8,14],"_links":{"self":[{"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/posts\/120"}],"collection":[{"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/comments?post=120"}],"version-history":[{"count":3,"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/posts\/120\/revisions"}],"predecessor-version":[{"id":688,"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/posts\/120\/revisions\/688"}],"wp:attachment":[{"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/media?parent=120"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/categories?post=120"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.jetmore.org\/john\/blog\/wp-json\/wp\/v2\/tags?post=120"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}