Search code examples
cfloating-pointscanfavravr-gcc

Problem using `scanf()` with `%f` (avr-gcc)


(Note: In an early version of this question I assumed that I used references the wrong way. It turned out to be a different problem and I made changes to the title/question accordingly.)

My code is intended to be compiled with avr-gcc and runs on an ATmega168p micocontroller.

I have the below function in C that is supposed to parse a float value from a buffer (char array) and assign it to a corresponsing float variable:

void read_float_from_buffer(float *var, char *buffer, uint8_t start, uint8_t end) {
    char val_buf[8];

    // transfer characters representing float to a temporary buffer
    uint8_t p=0;
    for (uint8_t i=start;i<=end && p<7;i++) {
        val_buf[p++]=buffer[i];
    }
    val_buf[p]='\0';

    // perform sscanf on this temporary buffer
    float buf=-1;
    if(sscanf(val_buf, "%.2f", &buf)==1) // this fails, sscanf returns 0
      *var=buf;
}

I have another function find_float_in_buffer() which searches a char buffer for the start and end indices of a float value. The function read_float_from_buffer() is called within this function:

float a_float_variable=0.;
void find_float_in_buffer(char *buffer) {
    uint8_t start,end=0;
    /*
    I do some magic here to find out where my float string starts and ends
    */
    read_buffer_float(&a_float_variable,buffer,start,end);
}

From my understanding this should work, but for some reason, scanning the val_buf temporary buffer fails (sscanf returns 0).

My question: is the way I'm passing the reference to buffer to read_buffer_float in find_float_in_buffer correct? I thought I finally understood how to pass pointers or references to functions, but I'm not sure if this is how you would pass a pointer in a function on to another function.

Edit: I'm running this code on a Atmel microcontroller and compiling using avr-gcc.

Edit 2: I removed my entire firmware and came up with the following MWE:

#define F_CPU 8000000UL

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <util/delay.h>
#include "src/peripheral-core/uart.c"

int main() {
  _delay_ms(200);
  uart_init();
  uartWriteString("application started\n\r\0");

  char test[]="12.34\0";
  float testfloat=-1;
  sscanf(test, "%f", &testfloat);

  uartWriteString("teststring is:\t\0");
  uartWriteString(test);
  uartWriteString("\n\rscanned float is:\t\0");
  uartWriteFloat(testfloat);
  uartWriteString("\n\0");

  for (;;) {
    _delay_ms(1000);
  }
}

For me, this prints:

application started

teststring is:  12.34

scanned float is:   -1.000

To build the .hex file I run this command:

avr-gcc -std=gnu99 -c -Os -Wall -mmcu=atmega168p main.c -o main.o
avr-gcc -std=gnu99 -Wl,-u,vfprintf -lprintf_flt -lm -mmcu=atmega168p main.o -o main.elf
avr-objcopy -O ihex -j .data -j .text main.elf main.hex

AVR-GCC version:

avr-gcc --version gives avr-gcc.exe (GCC) 10.1.0

When I run the below equivalent on my developement system, it works.

#include <stdio.h>

int main() {
  printf("application started\n\r\0");

  char test[]="12.34\0";
  float testfloat=-1;
  sscanf(test, "%f", &testfloat);

  printf("teststring is:\t\0");
  printf(test);
  printf("\n\rscanned float is:\t\0");
  printf("%.2f",testfloat);
  printf("\n\0");
}

But: When I pass "%.2f" to sscanf() as a format specifier (which is invalid as pointed out in the comments), this version fails with the same behaviour.


Solution

  • Since you already force the compiler to use the printf library with support for float, you know about this limitation of the standard library.

    You need to do this for scanf(), too. See the documentation.

    avr-gcc -std=gnu99 -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm -mmcu=atmega168p main.o -o main.elf