Printing a float to the USART with at least 5 decimals

Go To Last Post
17 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi All, I am trying to print a float to the serial monitor and am having so much trouble.

ATMEGA644PA@ 5V,16MHz

Baud rate is 115200

transmit_USART0 works fine but I want to print a float.

void init_USART0() {
  unsigned int number = (F_CPU)/(16*(BAUD0)-1);
  UBRR0H = (unsigned char)(number>>8);
  UBRR0L = (unsigned char)number;
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);// Enable receiver and transmitter 
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);// Set frame format: 8data, 2stop bit 
}

void transmit_USART0(char c) { 
  while (!(UCSR0A & (1<<UDRE0))); //Wait for empty transmit buffer 
  UDR0 = c;
}

void printNum_USART0(float data, int presc, bool ln){
  char buffer [43];
  dtostrf(data,presc+4,presc,buffer);
  for (int i=0; i<43; i++) {
   transmit_USART0(buffer[i]);
 }
 if (ln) transmit_USART0('\n');
}

dtostrf(data,presc+4,presc,buffer); does the heavy lifting here and is probably the problem

 

When I call:

float i =-123456.12345;
    printNum_USART0(i,4,true);

I get -123456.1300 in the serial port.

float i =-1234567.12345;
    printNum_USART0(i,4,true);

I get -1234567.1000 in the serial port.

float i =-12345678.12345;
    printNum_USART0(i,4,true);

I get -12345678.0000

It's like every time I increase the size of the number it rounds the decimal points no matter the precision or character width.

 

I've tried using sprintf( and its family AND the floating point version, but can never get past 2 decimals

 

 

Additionally my buffer is 43 in size. so it prints garbage like: -12345678.0000 ╟╟

 

How can I remove that?

This topic has a solution.
Last Edited: Fri. Jun 28, 2019 - 08:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

32bit IEEE754 float can only convey 7.5 digits of decimal accuracy. You are expecting too much.Maybe consider a compiler with 64 bit double.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using avr-gcc/++

 

Is that not enough?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joshagirgis wrote:
Is that not enough?
joshagirgis wrote:

float i =-123456.12345;

clawson wrote:
32bit IEEE754 float can only convey 7.5 digits of decimal accuracy. You are expecting too much.Maybe consider a compiler with 64 bit double.

 

Does your example number have more than 7 significant digits?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Fri. Jun 28, 2019 - 06:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I understand the 32bit limitation. Wondering if there is anyway to get a 64bit double.

Judging from your post I don't suppose avr-gcc would do that.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To answer your other question

joshagirgis wrote:

Additionally my buffer is 43 in size. so it prints garbage like: -12345678.0000 ╟╟

 

How can I remove that?

dtostrf creates a null terminated string in buffer.

You could do

for (int i = 0; i < 43 && buffer[i] != 0; i++) {
   transmit_USART0(buffer[i]);

but it would be more normal to create a 'transmit_string' function and call that  instead eg.

void transmit_string_USART0(const char *buf)
{
    while (*buf)
    {
        transmit_USART0(*buf++);
    }
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a good way of doing it. Thank you

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Reason I mentioned "other compilers" is that Imagecraft Pro and IAR bother offer 64 bit double. Make sure you are sitting down when you read the price tags!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Make sure you are sitting down when you read the price tags!

 

IAR is too embarrassed to even put the price on their website.

 

--Mike

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

double in avr-gcc is 32 bits

 

$ avr-gcc -dM -E - </dev/null | grep DOUBLE
#define __SIZEOF_LONG_DOUBLE__ 4
#define __SIZEOF_DOUBLE__ 4

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is a 64-bit FP library that can be used with AVR GCC, but it is not fast, and it is not small.

I'm on a phone, don't have the link.

I always start with the question "do you >>really<< need FP? Really?"

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I always start with the question "do you >>really<< need FP? Really?"

Probably not, but I was too stupid to realize that :/

 

I'd love to look at that link though!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Too many users don't look carefully at the problem, and just jump into using bloating point, when integers would do a much better job.

You pay the price in performance & code space.  Keep bloating calculations as the backup plan, but try integer methods first.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd love to look at that link though!

https://www.avrfreaks.net/comment/2299006#comment-2299006

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rowley has always had 64-bit doubles.    However they lost interest in their C compiler several years ago.    So you are stuck with with regular Tiny, Mega, Xmega and associated tools.

 

Quite honestly,  you can get good enough accuracy if you do your float calculations in the right order.

Or use integer maths and scale afterwards.

 

The common scenario is GPS calculations.     There is plenty published about how to do the maths with float.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll play around with this!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joshagirgis wrote:
I'll play around with this!
Fair enough.

 

But your time might be better spent re-examining your approach, this time with integers.  As mentioned, one of the very few applications which genuinely benefit from FP is GPS, and even that can avoid FP.

 

Still, always fun to play with a new library ;-)

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]