Floating point - important info

This is the best forum I could think of to post this... mods please move it if necessary.

Anyway, a lot of us know about the linker options to allow using native floating point support in Arduino sketches.

The method is to add this to the compiler/linker command line string:

-Wl,-u,vfscanf,-lscanf_flt,-u,vfprintf,-lprintf_flt

On a hunch, I wondered if the entire thing was needed for simply using printf with floats. For example, I always do this:

    fprintf (stdout, "Output: %6.2f volts DC\n", volts);

which might print something like this:

[b]Output:  7.45 volts DC[/b]

but, I rarely, if ever, use the SCANF/FSCANF functions, so I wondered if the linker option line could be simply:

-Wl,-u,vfprintf,-lprintf_flt

Turns out, it can and it works and it uses a lot less memory.

Testing it with this sketch:

int main (void)
{
    init ();

    char buffer [32];
    Serial.begin (115200);

    sprintf (buffer, "Output: %6.2f Volts DC\n", 123.45);
    Serial.print (buffer);

    while (1);
}

With both printf and scanf floating point options turned off, the sketch uses 8008 bytes and, of course, prints this:

** **Output:       ? Volts DC** **

With the SCANF option only enabled, it uses 10758 bytes and also prints this:

[b]
Output:       ? Volts DC[/b]

With the PRINTF option only enabled, it uses 9596 bytes and prints this:

** **Output:  123.45 Volts DC** **

With BOTH enabled, it uses 12348 bytes and prints this:

** **Output:  123.45 Volts DC** **

So, if you ONLY need floating point PRINTING, you can enable only the "fprintf" part and save quite a bit of flash memory since it seems that the FSCANF support part uses the most memory!

The printf support uses about 1588 extra bytes, the scanf support uses about 2750 more bytes (!!!) and together they use 4338 more bytes.

1588 bytes versus 4338 bytes is a heck of a savings!

For what it's worth.......

Um, when you search a library, it shouldn't pull in functions that you don't actually use.
What was your full compile line?

westfw:
Um, when you search a library, it shouldn't pull in functions that you don't actually use.
What was your full compile line?

Normally, that's true. But, when you link in the floating point support, it takes up memory whether it's used or not.

Here's another example... here I am including the floating point stuff, but NOT using it at all in the code. The Makefile normally has the compiler command lines hidden (the "@" prefix) but I removed them so that it all prints, and here it is:

No float support, and no floats used:

[b]root@michael:/usr/share/arduino-1.0.6/sketches/hexdump# make

------------- 1: hexdump.cpp -> hexdump.o --------------------
avr-g++  -Wall -mcall-prologues -ffunction-sections -fdata-sections -c -g -O3 -mmcu=atmega328p -DF_CPU=16000000UL  -c -MMD -MP -o hexdump.o hexdump.cpp
   text    data     bss     dec     hex         filename
   1409       0       0    1409     581         hexdump.o
--------------------------------------------------------------

------------- 3: hexdump.o -> hexdump.elf --------------------
avr-gcc -O3 -mmcu=atmega328p -lm  -Wl,--gc-sections,--relax -Wl,--section-start=.text=0x0000 -o hexdump.elf hexdump.o
   text    data     bss     dec     hex         filename
   4304     128      10    4442    115a         hexdump.elf
--------------------------------------------------------------

------------- 4: hexdump.elf -> hexdump.hex ------------------
avr-objcopy -O ihex -R .eeprom hexdump.elf hexdump.hex
   text    data     bss     dec     hex         filename
      [color=red]0    4432       0    4432    1150         hexdump.hex[/color]
--------------------------------------------------------------

------------- 5: hexdump.elf -> hexdump.eep ------------------
avr-objcopy -O ihex -j .eeprom \
        --set-section-flags=.eeprom=alloc,load \
        --no-change-warnings \
        --change-section-lma .eeprom=0 hexdump.elf hexdump.eep
   text    data     bss     dec     hex         filename
      0       0       0       0       0         hexdump.eep
--------------------------------------------------------------

------------- 6: hexdump.elf -> hexdump.lss ------------------

avr-objdump -d -f -w -C -S >hexdump.lss hexdump.elf
Build of hexdump complete.[/b]

Same, but with SCANF float support ONLY:

[b]root@michael:/usr/share/arduino-1.0.6/sketches/hexdump# make

------------- 1: hexdump.cpp -> hexdump.o --------------------
avr-g++  -Wall -mcall-prologues -ffunction-sections -fdata-sections -c -g -O3 -mmcu=atmega328p -DF_CPU=16000000UL  -c -MMD -MP -o hexdump.o hexdump.cpp
   text    data     bss     dec     hex         filename
   1409       0       0    1409     581         hexdump.o
--------------------------------------------------------------

------------- 3: hexdump.o -> hexdump.elf --------------------
avr-gcc -O3 -mmcu=atmega328p -lm -Wl,-u,vfscanf,-lscanf_flt -Wl,--gc-sections,--relax -Wl,--section-start=.text=0x0000 -o hexdump.elf hexdump.o
   text    data     bss     dec     hex         filename
   6624     128      10    6762    1a6a         hexdump.elf
--------------------------------------------------------------

------------- 4: hexdump.elf -> hexdump.hex ------------------
avr-objcopy -O ihex -R .eeprom hexdump.elf hexdump.hex
   text    data     bss     dec     hex         filename
      [color=red]0    6752       0    6752    1a60         hexdump.hex[/color]
--------------------------------------------------------------

------------- 5: hexdump.elf -> hexdump.eep ------------------
avr-objcopy -O ihex -j .eeprom \
        --set-section-flags=.eeprom=alloc,load \
        --no-change-warnings \
        --change-section-lma .eeprom=0 hexdump.elf hexdump.eep
   text    data     bss     dec     hex         filename
      0       0       0       0       0         hexdump.eep
--------------------------------------------------------------

------------- 6: hexdump.elf -> hexdump.lss ------------------

avr-objdump -d -f -w -C -S >hexdump.lss hexdump.elf
Build of hexdump complete.[/b]

Now, PRINTF float only:

[b]root@michael:/usr/share/arduino-1.0.6/sketches/hexdump# make

------------- 1: hexdump.cpp -> hexdump.o --------------------
avr-g++  -Wall -mcall-prologues -ffunction-sections -fdata-sections -c -g -O3 -mmcu=atmega328p -DF_CPU=16000000UL  -c -MMD -MP -o hexdump.o hexdump.cpp
   text    data     bss     dec     hex         filename
   1409       0       0    1409     581         hexdump.o
--------------------------------------------------------------

------------- 3: hexdump.o -> hexdump.elf --------------------
avr-gcc -O3 -mmcu=atmega328p -lm -Wl,-u,vfprintf,-lprintf_flt -Wl,--gc-sections,--relax -Wl,--section-start=.text=0x0000 -o hexdump.elf hexdump.o
   text    data     bss     dec     hex         filename
   5818     128      10    5956    1744         hexdump.elf
--------------------------------------------------------------

------------- 4: hexdump.elf -> hexdump.hex ------------------
avr-objcopy -O ihex -R .eeprom hexdump.elf hexdump.hex
   text    data     bss     dec     hex         filename
      [color=red]0    5946       0    5946    173a         hexdump.hex[/color]
--------------------------------------------------------------

------------- 5: hexdump.elf -> hexdump.eep ------------------
avr-objcopy -O ihex -j .eeprom \
        --set-section-flags=.eeprom=alloc,load \
        --no-change-warnings \
        --change-section-lma .eeprom=0 hexdump.elf hexdump.eep
   text    data     bss     dec     hex         filename
      0       0       0       0       0         hexdump.eep
--------------------------------------------------------------

------------- 6: hexdump.elf -> hexdump.lss ------------------

avr-objdump -d -f -w -C -S >hexdump.lss hexdump.elf
Build of hexdump complete.[/b]

Now, both PRINTF and SCANF:

[b]root@michael:/usr/share/arduino-1.0.6/sketches/hexdump# make

------------- 1: hexdump.cpp -> hexdump.o --------------------
avr-g++  -Wall -mcall-prologues -ffunction-sections -fdata-sections -c -g -O3 -mmcu=atmega328p -DF_CPU=16000000UL  -c -MMD -MP -o hexdump.o hexdump.cpp
   text    data     bss     dec     hex         filename
   1409       0       0    1409     581         hexdump.o
--------------------------------------------------------------

------------- 3: hexdump.o -> hexdump.elf --------------------
avr-gcc -O3 -mmcu=atmega328p -lm -Wl,-u,vfscanf,-lscanf_flt,-u,vfprintf,-lprintf_flt -Wl,--gc-sections,--relax -Wl,--section-start=.text=0x0000 -o hexdump.elf hexdump.o
   text    data     bss     dec     hex         filename
   8202     128      10    8340    2094         hexdump.elf
--------------------------------------------------------------

------------- 4: hexdump.elf -> hexdump.hex ------------------
avr-objcopy -O ihex -R .eeprom hexdump.elf hexdump.hex
   text    data     bss     dec     hex         filename
      [color=red]0    8330       0    8330    208a         hexdump.hex[/color]
--------------------------------------------------------------

------------- 5: hexdump.elf -> hexdump.eep ------------------
avr-objcopy -O ihex -j .eeprom \
        --set-section-flags=.eeprom=alloc,load \
        --no-change-warnings \
        --change-section-lma .eeprom=0 hexdump.elf hexdump.eep
   text    data     bss     dec     hex         filename
      0       0       0       0       0         hexdump.eep
--------------------------------------------------------------

------------- 6: hexdump.elf -> hexdump.lss ------------------

avr-objdump -d -f -w -C -S >hexdump.lss hexdump.elf
Build of hexdump complete.
[/b]

Summary (numbers in decimal):
No float: 4432 bytes
Scanf only: 6752 bytes (2320 bytes extra)
Printf only: 5946 bytes (1514 bytes extra)
Both: 8330 bytes (4398 bytes extra)

Interesting that the sum of "none" plus "scanf" plus "printf" is 8266 rather than 8330 - indicating some kind of overlap or something???

Want any more data, let me know.

westfw:
Um, when you search a library, it shouldn't pull in functions that you don't actually use.
What was your full compile line?

Here's the whole Makefile if you're interested:

[b]## project specific
PART = atmega328p
F_CPU = 16000000UL
TARGET = $(notdir $(CURDIR))
FORMAT = ihex
CODEADDR = 0x0000
# comment these out if floating point not needed
#FLOAT_SUPPORT = -Wl,-u,vfscanf,-lscanf_flt
#FLOAT_SUPPORT = -Wl,-u,vfprintf,-lprintf_flt
#FLOAT_SUPPORT = -Wl,-u,vfscanf,-lscanf_flt,-u,vfprintf,-lprintf_flt
OPT = O3

## fuse settings
LFUSE  = 0xF7
HFUSE  = 0xDF
EFUSE  = 0xFF
UNLOCK = 0x3F
LOCK   = 0x3F

################### no changes beyond this point ################

CPPFLAGS += -Wall
CPPFLAGS += -mcall-prologues
CPPFLAGS += -ffunction-sections
CPPFLAGS += -fdata-sections
CPPFLAGS += -c
CPPFLAGS += -g
CPPFLAGS += -$(OPT)
CPPFLAGS += -mmcu=$(PART)
CPPFLAGS += -DF_CPU=$(F_CPU)

LINKFLAGS += -$(OPT)
LINKFLAGS += -mmcu=$(PART)
LINKFLAGS += -lm
LINKFLAGS += $(FLOAT_SUPPORT)
LINKFLAGS += -Wl,--gc-sections,--relax
LINKFLAGS += -Wl,--section-start=.text=$(CODEADDR)

AVRDUDE_CMD = $(AVRDUDE)
AVRDUDE_CMD += -p $(PART)
AVRDUDE_CMD += -b 115200
AVRDUDE_CMD += -c avrispmkii
AVRDUDE_CMD += -P usb
AVRDUDE_CMD += -e
AVRDUDE_CMD += -s
AVRDUDE_CMD += -qq
AVRDUDE_CMD += -U lock:w:$(UNLOCK):m
AVRDUDE_CMD += -U lfuse:w:$(LFUSE):m
AVRDUDE_CMD += -U hfuse:w:$(HFUSE):m
AVRDUDE_CMD += -U efuse:w:$(EFUSE):m
AVRDUDE_CMD += -U flash:w:$(TARGET).hex:i
AVRDUDE_CMD += -U eeprom:w:$(TARGET).eep:i
AVRDUDE_CMD += -U lock:w:$(LOCK):m

CC = avr-gcc
CXX = avr-g++
LD = avr-ld
AR = avr-ar
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
NM = avr-nm
AVRSTRIP = avr-strip
AVRSIZE = avr-size
AVRDUDE = avrdude

# default rule
.DEFAULT_GOAL = target

#_______________________________________________________________________________
# BUILD RULES

.PHONY:    all target upload clean
.SECONDARY:

all: clean target upload

target: $(TARGET).o $(TARGET).elf $(TARGET).hex $(TARGET).eep $(TARGET).lss
    @echo "Build of $(TARGET) complete.\n"

upload:
    @echo "\nUploading $(TARGET)...\n"
    @$(AVRDUDE_CMD)
    @echo "\nCommand line: $(AVRDUDE_CMD)\n"
    @echo "$(PART) erased, $(TARGET) uploaded.\n"

clean:
    @rm -f $(TARGET).d
    @rm -f $(TARGET).eep
    @rm -f $(TARGET).elf
    @rm -f $(TARGET).hex
    @rm -f $(TARGET).lss
    @rm -f $(TARGET).o

#_______________________________________________________________________________
# BUILD THE TARGET

%.o: %.cpp
    @echo "\n------------- 1: $< -> $@ --------------------"
    @$(COMPILE.cpp) -MMD -MP -o $@ $<
    @$(AVRSIZE) $@
    @echo "--------------------------------------------------------------"

%.o: %.c
    @echo "\n------------- 2: $< -> $@ --------------"
    @$(COMPILE.c) -MMD -MP -o $@ $<
    @$(AVRSIZE) $@
    @echo "--------------------------------------------------------"

%.elf: %.o
    @echo "\n------------- 3: $< -> $@ --------------------"
    @$(CC) $(LINKFLAGS) -o $@ $<
    @$(AVRSIZE) $@
    @echo "--------------------------------------------------------------"

%.hex: %.elf
    @echo "\n------------- 4: $< -> $@ ------------------"
    @$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
    @fixcr $@ > /dev/null
    @$(AVRSIZE) $@
    @echo "--------------------------------------------------------------"

%.eep: %.elf
    @echo "\n------------- 5: $< -> $@ ------------------"
    @$(OBJCOPY) -O $(FORMAT) -j .eeprom \
    --set-section-flags=.eeprom=alloc,load \
    --no-change-warnings \
    --change-section-lma .eeprom=0 $< $@
    @fixcr $@ > /dev/null
    @$(AVRSIZE) $@
    @echo "--------------------------------------------------------------"

%.lss: %.elf
    @echo "\n------------- 6: $< -> $@ ------------------\n"
    @$(OBJDUMP) -d -f -w -C -S >$@ $<
    @fixcr $@ > /dev/null[/b]

By the way, the "fixcr" line is just a little util I have that removes the 0x0D off the end of the line (I use Linux and hate seeing the stupid "^M" at the end of each line).

I guess the "-uxxxx" forces the symbol to be "undefined", which in turn forces the library to be added whether mentioned elsewhere or not. I'm not clear on why that part is necessary, but it looks like if you leave it out, you end up getting the wrong printf/scanf functins :frowning:

westfw:
Um, when you search a library, it shouldn't pull in functions that you don't actually use.
What was your full compile line?

Maybe it should not but a standard IDE 1.6.6 installation does. Else I can't explain the below result.

#include <Wire.h>

void setup()
{
}

void loop()
{
}
  Without wire.h included
  Sketch uses 450 bytes (1%) of program storage space. Maximum is 32,256 bytes.
  Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.

  With wire.h included
  Sketch uses 1,518 bytes (4%) of program storage space. Maximum is 32,256 bytes.
  Global variables use 216 bytes (10%) of dynamic memory, leaving 1,832 bytes for local variables. Maximum is 2,048 bytes.

westfw:
I guess the "-uxxxx" forces the symbol to be "undefined", which in turn forces the library to be added whether mentioned elsewhere or not. I'm not clear on why that part is necessary, but it looks like if you leave it out, you end up getting the wrong printf/scanf functins :frowning:

If I leave off the "-Wl" part, it compiles but the floating point support isn't there (i.e. a float prints as "?").
If I leave off the "-u" part, I get this error when the linker tries to convert the object file ("hexdump.o") to the ELF binary ("hexdump.elf"):

[b]------------- 3: multimeter.o -> multimeter.elf --------------------
/usr/local/lib/gcc/avr/4.9.3/../../../../avr/bin/ld: cannot find vfprintf: No such file or directory
collect2: error: ld returned 1 exit status
make: *** [multimeter.elf] Error 1
[/b]

I guess the whole thing is needed... and the man page for AVR-GCC says:

[quote author=AVR-GCC man page] -Wl,option
Pass option as an option to the linker. If option contains commas, it is split into multiple options at
the commas. You can use this syntax to pass an argument to the option. For example, -Wl,-Map,output.map
passes -Map output.map to the linker. When using the GNU linker, you can also get the same effect with
-Wl,-Map=output.map.

-u symbol
Pretend the symbol symbol is undefined, to force linking of library modules to define it. You can use -u
multiple times with different symbols to force loading of additional library modules.[/quote]

It would be REALLY neat if the floating point code was not loaded at all UNLESS floating point printing was used. That way, it would just be available, but not suck up any PROGMEM unless it was needed.

Maybe there is a way.... any GCC gurus here?

sterretje:
Maybe it should not but a standard IDE 1.6.6 installation does. Else I can't explain the below result.

#include <Wire.h>

void setup()
{
}

void loop()
{
}






Without wire.h included
 Sketch uses 450 bytes (1%) of program storage space. Maximum is 32,256 bytes.
 Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.

With wire.h included
 Sketch uses 1,518 bytes (4%) of program storage space. Maximum is 32,256 bytes.
 Global variables use 216 bytes (10%) of dynamic memory, leaving 1,832 bytes for local variables. Maximum is 2,048 bytes.

It doesn't matter if you use the Arduino IDE, Atmel Studio or just a command line (like I do). The end result is the same... AVR-GCC is being called to do the work.

All the Arduino IDE does is build up an AVR-GCC command line based on the user's selected options and the needs of the sketch to be compiled, then it simply executes that command line.

If you select "Show verbose compile", then cut-n-paste the displayed command line directly into a console window (Linux or Windows and probably OSX too), it will run AVR-GCC and compile the sketch again.