Go Down

Topic: "map" function and large numbers (Read 910 times) previous topic - next topic

Lemme

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:

Code: [Select]

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

AWOL

Quote
I have a sensor with an output of 2 - 1667771

Code: [Select]
map(AD_Result, 2, 167771, 1, 100000);
What is the correct value?
What's wrong with division?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

johnwasser

Code: [Select]

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);

Code: [Select]

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?
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Lemme

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

AWOL

Quote
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?
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

johnwasser

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.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Lemme

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

KeithRB

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

Lemme

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

Cookies

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

Code: [Select]

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;
}

robtillaart

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


The mapping function
Code: [Select]
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:

Code: [Select]
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 unless 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
Code: [Select]
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:
Code: [Select]
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:
Code: [Select]
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:

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

give it a try
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Lemme

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

Go Up