"map" function and large numbers

Hi all Can someone please help me with this… I have a sensor with an output of 2 – 1667771. Now – I want this output to be mapped to 1 – 100000 instead. My intention was to use the “map” function:

AD_Result = map(AD_Result, 2, 167771, 1, 100000);

I am using Arduino v.0.22 and “AD_Result” is an unsigned long.

My problem is that it seems that the “map” function have problems with large numbers. It simply does not calculate correct. Does anyone know what the problem could be, and how to solve it?

Please let me know if I an unclear in the information provided.

Regards Lemme

I have a sensor with an output of 2 – 1667771

map(AD_Result, 2, 167771, 1, 100000);

What is the correct value? What's wrong with division?

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

I suspect the problem is for large values of x:

map(167771, 2, 167771, 1, 100000);

return (167771 - 2) * (100000 - 1) / (167771 - 2) + 1;

return (167769 * 99999) / 167769 + 1;

return 16776732231 / 167769 + 1;

Unfortunately for you, 16776732231 won't fit in a 32-bit long integer so you get a math overflow.

I guess it only works reliably when the upper limits fit in an integer (+/-32767).

How did you get such a strange upper limit for your sensor?

Hi johnwasser and AWOL Many thanks for the quick replys.

I am sorry to hear that I hit the wall with the math overflow, but glad to learn the reason for this. Do you by any chance have a suggestion for a solution, another way of doing it?

The strange upper limit comes from a 24bit AD measuring on a POT. Due to noise the first two digits are removed, and I end up with a min-max of 2 to 167771.

Regards Lemme

The strange upper limit comes from a 24bit AD measuring on a POT. Due to noise the first two digits are removed, and I end up with a min-max of 2 to 167771.

I think you’re missing some digits again. 224 = 16 777 216.

As I said earlier, what’s wrong with simple division?

Do you really need 100,000 different positions on your pot? I'd be tempted to throw out 9 bits (divide by 512) to get the upper limit to fit in a signed long integer: 0-32767. Then you can map it safely to whatever integer range you need.

Hi johnwasser and AWOL

@ AWOL You are right about 2^24 = 16 777 216. But I removed 2 digits and end up with 167 771. Simple divison – OK, I could just multiply my reading with 0,59605. I will try that out.

@ johnwasser I am building a signal generator with the ability to do sweeps within the 100KHz range. For this I need a POT with 100.000 divisions.

Regards Lemme

Except you wont be able to set it to 1 part in 100,000! unless you have a 1000 turn pot. 8^)

I suggest multiple pots, or even up and down switches to set each digit.

OR, even a set of these: http://www.digikey.com/product-search/en/switches/thumbwheel-switches/1115394

Hi Keith I did not explain the whole story!

I am going to select between different ranges for the sweep (1Hz-100Hz, 100Hz-1KHz, 1KHz-10KHz, 10KHz-100KHz, 100KHz-1GHz). The sweep resolution is not so critical on the higher frequencies. I might choose to have a seep set within the 100KHz range for example for a sweep between 700KHz-800KHz.

For at fixed set of frequencies I use your mentioned up and down switches. Above 1KHz I cannot use the POT as it is way too sensitive. Thanks for the link!

Regards Lemme

You could put your own version of map() using long long 64 bit integers in your sketch.

long long map_ll(long long x, long long in_min, long long in_max, long long out_min, long long out_max)
{
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

long long operations are long long in time ;) be carefull with them...

The mapping function y = map(x, 2, 167771, 1, 100000); overflows so you should rewrite (and rethink) it.

This mapping function may or may not be reduced to the following function:

y = x * (out_max/GCD(out_max, in_max) ) / (in_max /GCD(out_max, in_max)) + out_min;  // greatest common divider ==> e.g.  8/12 == (8/4) / (12/4)

which will simplify the numbers in the formula [u]unless[/u] the GCD() == 1 // in_max and out_max have no common divisor. in this case they have no common divider so it cannot be simplified!

[think] How to simplify it with a minimal error?

The first that comes in mind is y = map(x, 0, 167771, 0, 999999); note there are 2 changes. By a remapping the lower values to 0 for both in and out the formula becomes much more simple (they already were close to zero). Be aware this introduces a small error than can be "fixed" by using the constrain() function afterwards

With the two zeros the function becomes:

y = x * out_max / in_max ;

as GCD(999999, 167771) = 1, the next best thing is an approximation: 999999/167771 = 5.960499.... so that is almost 6 (with less than 1% error!)

that info would lead to the following approximation function:

y = x * 6;

range of your pot was [2 - 167771] that would map to [12 - 1006626] // no overflow!!

so to adjust the lower border we subtract 11 and the upper border is fixed with constrain(), This gives a new "mapping" function:

long mymap(long x)
{
  long y = 6 * x - 11;                // [1 - 1006615] 
  if (y > 100000L) y = 100000L;  // [1 - 100000]
  return y;
}

give it a try

Hi Cookies and robtillaart

Thanks for the very good suggestions! I will try them both out tomorrow as it is getting a bit late now.

Regards Lemme