Map function only integers

Why does map only work as integers?

My arrays are 16 bit integers except for acd[z] is a float. Using the map function returns only integers where the line after the commented out map function does return two decimal points.
ardl - analog device read long
alcl - analog low calibration long
ahcl - analog high calibration
alrl - analog low range long
ahrl - analog high range long

  for (int z = 0; z < 15; z++) {
    adrl = float(adr[z]);
    alcl = float(alc[z]);
    ahcl = float(ahc[z]);
    alrl = float(alr[z]);
    ahrl = float(ahr[z]);
//    (acd[z]) = map (adrl, alcl, ahcl, alrl, ahrl);
acd[z] = (adrl - alcl) * (ahrl - alrl) / (ahcl -alcl) + alrl;    // This works
Serial.print(acd[z]);
    Serial.print("\t");

I probably spent a half a day trying to get to the bottom of this before I found a solution.
Is this a problem with map on all Arduinos?

where the line after the commented out map function does return two decimal points.

Wrong. It returns a float that you print to two decimal places.

Is this a problem with map on all Arduinos?

No. It's a problem that you didn't read the documentation. The documentation CLEARLY states that the function deals with integer values only.

Yep, map is defined as

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

Why do you need a float? I think you don't ;)

If you absolutely need to have floats, then you can use this.

template< typename T> 
T map( const T x, T in_min, T in_max, T out_min, T out_max ) 
{ 
  if(x < in_min) return out_min;
  if(x > in_max) return out_max;
  return ((x - in_min) * (out_max - out_min) / (in_max - in_min)) + out_min;
}

Put this at the top of your code and call it like this.

for(float i = 0.0; i < 21.0; i+= 0.5)
{
   Serial.print(i);
   Serial.print('F("\t"));
   Serial.println( map<float>(i, 0.0, 20.0, -10, 10) );   
}

Tested here:
http://cpp.sh/56d7

The raw data does not need to float but the end user wants to be able to see one to two decimal placed in the data that goes into the head end. Say it is scaled to read 8 to 30 pounds per gallon and their recipe calls for 13.2, they need to see the decimals. Also in monitoring a process and trying to control parameters, seeing the hundreds give an indication that you are on the rise of decline of an additive in measuring density of a solution.

I know in measuring high pressure in PSI or the weight of tons of bulk, that the precision in not needed but if I were measuring atmosphere I might want to see a hundredth.

So far I have a front end that can graph 48 channels for process monitoring.

This module I am working on measures milli-amps and converts it to something a layman can understand depending upon the device it is connected to and the current source it is receiving,

The whole reason for using 16 bits to begin with is for more precision. I'm using a series of the ADS1115 ADC's and transmitting the results via RS-458 back to the front end system.

This is why I don't like map().

Nasa:
The raw data does not need to float but the end user wants to be able to see one to two decimal placed in the data that goes into the head end. Say it is scaled to read 8 to 30 pounds per gallon and their recipe calls for 13.2, they need to see the decimals. Also in monitoring a process and trying to control parameters, seeing the hundreds give an indication that you are on the rise of decline of an additive in measuring density of a solution.

But if you want to have hundreds (1/100), why not simply save it as hundreds? The Arduino has no floating point hardware so all the floating point stuff is quite heavy. If you want to see 13,24, save it into an int as 1324. If I wnat mm precision but display it in cm I’m not going to save it as cm, I’m going to save it as mm because that’s the precision I need! You can always add the decimal later :slight_smile: This way you don’t run into weird floating point rounding errors etc and it’s wayyyyy faster. So yeay, you don’t need the float :wink:

 return ((x - in_min) * (out_max - out_min) / (in_max - in_min)) + out_min;
acd[z] = (adrl - alcl) * (ahrl       - alrl)      / (ahcl       -     alcl)   + alrl;

Is exactly what I did to make it work substituting "return" with my calculated result "adc[z]", "x" with my analog read "adrl", "out_max" with my high range limit "ahrl" , " out_min" with my low range limit "alrl", "in_max with my high calibration "ahcl" and "in_min" with my low calibration "alcl".

The float adc[z] is the converted reading sent back to the front end.

Each channel has separate low and high cals and can have separate low and high ranges that are stored in EEprom.

Nasa: Why does map only work as integers?

Because it's the most common case, and getting integers to map correctly is a bit fiddly (you get off-by-one bugs if you don't do it right).

To "map" floats, you need to multiply and add - no fiddly bits needed.

I not saving the data on the arduino, I am simply passing it on the another system as tab delimited data, standardized for use in other applications. The finished product just shoves 48 elements of an array with tabs, refills the array and shove it out but responding to request to re-calibrate or re-span the range. When that happens the new values are PUT into the EEprom for the particular channel requested. The EEprom is read in the setup and fills arrays for the computation.

I have a loader program to load the EEprom for an installation and a reader program to get the EEprom data both separate of the running program to help minimize the code using a Nano. Bank switching is done with an I2C hub and the finished product has up to 48 channels of data.

I also has the capability of plugging in to the USB to calibrate individual channels from a terminal screen plus stream the data via RS-232. The transceiver I'm using can run with as many as 120 devices up to 4000 feet.