Changing a "result": 74.14505494505494, to one decimal place

I’m using a TMP36 and the MEASURING THE TEMPERATURE example. I would like to get the result to 74.1

// -----------------
// Read temperature
// -----------------

// Create a variable that will store the temperature value
double temperature = 0.0;

void setup()
{
  // Register a Spark variable here
  Spark.variable("temperature", &temperature, DOUBLE);

  // Connect the temperature sensor to A7 and configure it
  // to be an input
  pinMode(A7, INPUT);
}

void loop()
{
  int reading = 0;
  double voltage = 0.0;

  // Keep reading the sensor value so when we make an API
  // call to read its value, we have the latest one
  reading = analogRead(A7);

  // The returned value from the Core is going to be in the range from 0 to 4095
  // Calculate the voltage from the sensor reading
  voltage = (reading * 3.3) / 4095;

  // Calculate the temperature and update our static variable
  temperature = (voltage - 0.5) * 100 * 9.0 / 5.0 + 32; // 
  
}

C++11 comes along with some neat math functions, one of them being round.
To use it as you wished, just add following after you set the temperature:

temperature = round(temperature*10)/10;

It will shift the original value by one decimal, round it, shift it back - result: rounded double with one digit precision!

Enjoy! :sunflower:

2 Likes

Thanks that works my coding skills are little to none.

@rastapasta 's solution is perfect and can be used wherever rounding is required.

But just for completeness - if someone else should happen to drive by this thread - I’d like to mention two more solutions, depending on the respective needs.

One is the easiest mathematical approach. Just add 0.5 and then truncate away the decimal places. To achieve a round after the 0.1 place, you’d again do what @rastapasta said: multiply-divide by ten.
This is good if you are doing some mathematical operations beforehand anyway, then you can incorporate that quite easily and thus save some processing cycles.

  temperature = ((int)(((v - 0.5) * 100.0 * 9.0 / 5.0 + 32.0 + 0.5) * 10)) / 10.0;  
                          // (v - 0.5) * 100.0 to get °C
                          // 9.0 / 5.0 + 32.0 to convert into °F
                          // ((int)(... + 0.5) * 10) / 10.0 to round to 0.1°F precision
  // or mathematically reduced and streamlined for speed, 
  // but less readable/divisable into above sub calculatioins 
  temperature = ((int)(1800.0 * v -  867.5)) / 10.0;

or if you only want to print out a rounded value, but otherwise want to keep the precision in the variable you just do this (which was not the intention of the original question, but for “completeness” :wink: )

  // e.g. for Serial output
  temperature = (v - 0.5) * 100.0 * 9.0 / 5.0 + 32.0;
  Serial.print(temperature, 1);
1 Like

You cannot rely on that entirely. E.g. the decimal 0.2 (1/5th) has a recurring representation in base 2, and numbers in C (and on the Spark and Arduino, therefore) are stored base 2. 1/5th can be represented entirely accurately in decimal but it is an endlessly recurring number in binary. 0.2(base 10)=0.0011001100110011…(base 2). It matters not what you do with round() or other functions, the internal representation will never be exactly 1/5th if stored as a float or a double. What you can do is either store the value as the number of tenths, as an integer, but this is cumbersome

float temperature;
int temperature_tenths;
temperature_tenths = (int)round(temperature * 10);

or ignore the problem entirely and only worry about it during comparisons (you cannot compare for equality to 1/5th for the reason given above). Outputting the number will ordinarily cause the binary to be transformed into decimal. I use printf or sprintf

char s[22];
//printing a float to one rounded decimal digit of accuracy
sprintf( s, "%.1f", temperature);
//printing an integer representing number of tenths
sprintf( s, "%d.%d", temperature_tenths/10, abs(temperature_tenths)%10);
//comparing a float for equality to 0.2
if( fabs(temperature-0.2) < 0.0001) almostequal=true else almostequal=false;
1 Like

I had to specifically use a float/double (i.e. divide by “10.0”) to get this to work the way I expected. Otherwise it would do integer division.

temperature = round(temperature*10)/10.0;

That’s not the same kind of issue, this is standard C behaviour.

@ScruffR can you pls tell me what I'm doing wrong here....

I would like value to appear in console as eg: 50.1 % so 1 decimal place..
Currently it is printing six zero after decimal place. I have pulled my hair out playing around with the resultstr function and data types with no luck...

int TnkLvlPV_RAW = A0;
float Tx01 = 0.0;
char resultstr[64];

void setup () {
pinMode(TnkLvlPV_RAW,INPUT);
}

void loop() {
// Read alalogue value assigned to TAG "TankLevelPV_RAW"
Tx01 = analogRead(TnkLvlPV_RAW);
Tx01 = map(Tx01, 0, 4095, 0, 100);
sprintf(resultstr, "{"value":%.4f}",Tx01);

delay(100);
Spark.publish("Tx01", String(Tx01) + " %");
delay(2000);

}

You are already using snprintf() for your result string, just post that instead of String(Tx01).
And if you use "%.1f %%" as format string, you’ll only see one decimal place and the percent sign as well in it.
But since map() will only give you integer results AFAIK, you won’t even need that one decimal place.

1 Like

thanks @ScruffR

This won't compile however.. someting I have done wrong?

sprintf(resultstr, "{"value":%.1f}",Tx01);

delay(100);
Spark.publish("Tx01", sprintf(Tx01) + " %");

But since map() will only give you integer results AFAIK, you won't even need that one decimal place.

@ScruffR Oh... so is there another way to scale the analogue as percentage and include the 1 decimal place for resolution?
Cheers

This won't compile since you are not using it right.

Means

  // not like this 
  // Spark.publish("Tx01", sprintf(Tx01) + " %");
  // but
  snprintf(resultstr, sizeof(resultstr), "%.1f %%",Tx01);
  particle.publish("Tx01", resultstr, PRIVATE); // keep things private too

Yup, the normal mathematical way - linear interpolation/extrapolation.
This is the implementation for the standard map()

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

Rename the function and change all datatypes to double and you'll be fine.

float i=1.5237475984328953;

#include "math.h"
void setup()
{
 Serial.begin(9600);
}

void loop() {
 Serial.println(i,6);
 float price = i*1000;
 Serial.println(price,2);
 
 float roundNearest = roundf(i*1000) ; // x 100 for two decimal place
  roundNearest = roundNearest/1000;
 Serial.println(roundNearest,6);

 price = (roundNearest*1000);
 Serial.println(price,2);
 while(1){}
}

you u can use roundf() inside math.h library
like in this video Demo Video

To which of the previous posts are you refering?

For the original question the topic title refers to that won't help really.
Binary floating point data types cannot store every arbitrary value and hence even if you have results that would only result in a few decimal places the binary representation would sometimes only approximate that result and hence the conversion from float to string needs to take care of the number of decimal places you are interested in.

That's why you had to add the second parameter in your Serial.print() statement.

For the last, the intent is not only to round the result but also map (rescale) the original value.

If this should merely drive views on that video we'd have to consider this spam tho'

it not spam, if using Serial.print() it just round in serial print only, if you make calculation you will fail. So i advice using roundf() from math.h