Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping a numeric range onto another

Tags:

c

math

arduino

Math was never my strong suit in school :(

int input_start = 0;    // The lowest number of the range input. int input_end = 254;    // The largest number of the range input. int output_start = 500; // The lowest number of the range output. int output_end = 5500;  // The largest number of the range output.  int input = 127; // Input value. int output = 0; 

How can I convert the input value to the corresponding output value of that range?

For example, an input value of "0" would equal an output value of "500", an input value of "254" would equal an output value of "5500". I can't figure out how to calculate an output value if an input value is say 50 or 101.

I'm sure it's simple, I can't think right now :)

Edit: I just need whole numbers, no fractions or anything.

like image 687
Joe Avatar asked Apr 20 '11 14:04

Joe


2 Answers

Let's forget the math and try to solve this intuitively.

First, if we want to map input numbers in the range [0, x] to output range [0, y], we just need to scale by an appropriate amount. 0 goes to 0, x goes to y, and a number t will go to (y/x)*t.

So, let's reduce your problem to the above simpler problem.

An input range of [input_start, input_end] has input_end - input_start + 1 numbers. So it's equivalent to a range of [0, r], where r = input_end - input_start.

Similarly, the output range is equivalent to [0, R], where R = output_end - output_start.

An input of input is equivalent to x = input - input_start. This, from the first paragraph will translate to y = (R/r)*x. Then, we can translate the y value back to the original output range by adding output_start: output = output_start + y.

This gives us:

output = output_start + ((output_end - output_start) / (input_end - input_start)) * (input - input_start) 

Or, another way:

/* Note, "slope" below is a constant for given numbers, so if you are calculating    a lot of output values, it makes sense to calculate it once.  It also makes    understanding the code easier */ slope = (output_end - output_start) / (input_end - input_start) output = output_start + slope * (input - input_start) 

Now, this being C, and division in C truncates, you should try to get a more accurate answer by calculating things in floating-point:

double slope = 1.0 * (output_end - output_start) / (input_end - input_start) output = output_start + slope * (input - input_start) 

If wanted to be even more correct, you would do a rounding instead of truncation in the final step. You can do this by writing a simple round function:

#include <math.h> double round(double d) {     return floor(d + 0.5); } 

Then:

output = output_start + round(slope * (input - input_start)) 
like image 150
Alok Singhal Avatar answered Sep 28 '22 19:09

Alok Singhal


Arduino has this built-in as map.

Example:

/* Map an analog value to 8 bits (0 to 255) */ void setup() {}  void loop() {   int val = analogRead(0);   val = map(val, 0, 1023, 0, 255);   analogWrite(9, val); } 

It also has the implementation on that page:

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; } 
like image 27
Dustin Avatar answered Sep 28 '22 18:09

Dustin