dtostrf not found and #include <stdlib.h> not working

I’m trying to concatenate a floating point value into a string.

The forums recommend the dtostrf function but the compiler can’t find it:

ConcatenateFloat.ino: In function 'void loop()':
ConcatenateFloat:16: error: 'dtostrf' was not declared in this scope
'dtostrf' was not declared in this scope

dtostrf is in stdlib.h, which should be automatically included by the compiler. I manually added #include <stdlib.h> and the compiler gives the same error.

Any ideas on how to use stdlib functions on the Zero?

Here is some source code that tries to use dtostrf in a meaningless way, just to get it to compile. It doesn’t.

// Concatenate a Floating Point Number
#include <stdlib.h>

void setup() {
  Serial.begin(115200);
  delay(100);
  Serial.println("Starting ....");
}

void loop() {
  String outputstring(10);
  outputstring = String();
  float floatingnumber;
  floatingnumber = 1.6445;
  dtostrf(floatingnumber,6,4,outputstring);
  Serial.println(outputstring);
}

I’m running into the same problem.

There is a function in the avr section of the SAMD code but it doesn’t work.

Accessed by adding #include <avr/dtostrf.h>

This is what I used to do with a DUE.

My code will compile without errors but it does not work!

Problem is that the Zero’s libraries do not allow formatting of floats. The function dtostrf uses an sprintf with a %f in it and that is not a supported format for some reason.

Some how the DUE libs allow formatting of floats.

I’m still trying to figure out how to configure the Zero libs to support formatting of floats.

Perhaps this will give you a clue and maybe you can help solve this.

A few internet searches found several resources for “bit-banging” a float-to-string concatenate. These routines multiply the floating point by factors of 10, thereby shifting the decimal into integer space. The algorithm converts the integer to string. The final step is parsing the string and inserting a decimal character in the correct location.

Example:

4.6287 x 1000 = 4628.7
convert to integer = 4628
convert to string = “4628”
parse string and add decimal in correct location = “4.628”

Cumbersome, but it should work. Thus far, I’ve had to reverse-engineer the Zero when translating any significant line of Uno code, requiring combing the forums and hours of trial-and-error with each step. IMHO, the Zero is too incompatible with the core Arduino platform to be considered a viable upgrade path for the Uno.

Well, you could simply add dtostrf.c to your project. Or use sprintf().
Untested.

Note that the regular Arduino print() will use a default precision.
Any traditional printf() function performs rounding. So the example above would produce 4.629

Memory should never be much of a problem for Due or Zero. It is only the smaller Arduinos that are tight for flash.

Surely it is easier to just use the regular print() methods:

// print a Floating Point Number with different precision
//#include <stdlib.h>

void setup() {
    Serial.begin(9600);
    delay(100);
    Serial.println("Starting ....");
}

void loop() {
    String outputstring(10);
    outputstring = String();
    float floatingnumber;
    floatingnumber = 4.6287;
    //  dtostrf(floatingnumber,6,4,outputstring);
    //  Serial.println(outputstring);
    Serial.println(floatingnumber);     //default precision
    Serial.println(floatingnumber, 2);  //specify precision
    Serial.println(floatingnumber, 3);
    Serial.println(floatingnumber, 4);
    Serial.println(floatingnumber, 5);
    Serial.println();
    delay(5000);
}

Note that you still have the perennial problem of no left padding. So you would have to add any leading spaces by testing the “range” of the float value. Much like you do with hexadecimal printing.

David.

David,

I did test this as I mentioned above :)

The problem is the dtostrf uses sprintf, and sprintf does not support a %f format. So you just end up with a null string.

I've read that the "normal" libraries omit the %f due to memory constraints. This should not be a problem with the Zero and the library needs to be configured to allow float formatting.

This is the part that I have not figured out, yet.

Later,

James

dtostrf() has never used printf(). It was always a special function that was found in the AVR library to avoid using the full-fat printf()

None of the Arduino print() methods allow for padding or right justification. Regular C++ can do some extensive formatting. It is never as simple or as powerful as the variadic printf(). I think you will find that most ARM libraries will give you a full printf() as a default. It is only AVR or other small chips that worry about the flash size.

In practice, it is not too difficult to do your own padding when you need it.

David.

David,

I begs to differ, but you're only 50% correct :)

Take a look at the compatibility code in avr/dtostrf.h and avr/dtostrf.c in the SAMD code where you've loaded the Zero's board files.

It uses sprintf (okay, not printf so I give you the 50% mark :) )

It works correctly except that it generates a "%f" which as I've tested, is not supported.

Oh and the Zero is an ARM... :)

Later,

James

As I thought, dtostrf.c is an "avr-libc" function. Source code at http://avr-libc.sourcearchive.com/documentation/1.6.2/dtostrf_8c-source.html It looks as if the dtostrf.c in the SAM folder is a rather swift kludge by using sprintf(). Especially because the AVR code is dependent on other special functions.

The sprintf() kludge will be dependent on the SAM build linking with the full f-p vfprintf library for %f to work. I do not have a DUE (hopefully it will arrive this afternoon). So I can not test a DUE build yet.

The same arrangement would be required for SAMD because it uses the same kludge. i.e. vfprintf

Seriously, the overloaded print() method from Arduino copes with the precision. I would agree that right justification would make print() a lot more pleasant to use. For integers and f-p.

Yes, of course SAM and SAMD are both ARM. The original (and dominant) Arduinos are AVR. Most code is written for AVR originally. Yes, much has been upgraded for the SAM (Due). Hardly anything has been upgraded for the SAMD (Zero and M0_Pro). Since the warring Italians are behaving like children, most libraries are ignoring the SAMD completely. As are the buying public.

David.

Hi, I have just updated my PrintEx library because of this thread. It is now compatible with the Zero & Due.

I have written my own printf which uses the Print methods directly (no duplicated functionality like that found in std implementations). For instance the floating point support uses Print::print( float, prec );
It doesn’t require a buffer to build the string, it sends it directly downstream (you can use GString to print/f to RAM). There is also a sprintf provided if you like the traditional way.

Padding and precision is supported.

Here is a basic sketch:

#include <PrintEx.h>

PrintEx print = Serial;

void setup() {
  Serial.begin(9600);
  print.printf( "A floating point value: %010.6f", 12.345678f );
}

void loop() {}

This outputs:

A floating point value: 012.345679

There is also streaming functionality. It is actually more efficient than using print/ln() directly.
You can use this directly without having to create a PrintEx/StreamEx object.

#include <PrintEx.h>

using namespace ios;

void setup() {
  Serial.begin(9600);
  Serial << "A floating point value: " << precision(6) << 12.345678f;
}

void loop() {}

The library is available in the library manager & vsersion 1.1.8 (latest) contains the support for the Zero and Due.

There is some basic info here:

And there are some examples and new features coming soon. And if you’re interested, feel free to ask for help, or if you have any queries about its capabilities.

I’m trying to get this tested and in use on as many platforms as possible, so even though my library may help you, it actually helps me a lot also.

Cheers,
Chris.

Chris,

Thanks! I'll give it a whirl.

David,

Thanks as well.

I agree that the Zero seems to be ignored. It seems like a nice upgrade which is why I'm testing it out.

There is other undocumented weirdness with the board. Nowhere is it documented that I2C address 0x28 is verboten. The pull ups shown in the schematic on the I2C lines are not placed on the board (the pads are there). I can't see how the EDBG part will work without them, unless they use another channel. But if they use another channel, that begs the question of why they connected it via I2C and killed the 0x28 address. It just so happened that one of my sensor's default address conflicted with the EDBG part which caused quite a bit of pulling of hair and gnashing of teeth.

I have a Zero Pro as well and a couple of the pins are swapped vs. the Zero. Not sure if the M0-Pro is "fixed". Neither of the bootloaders seem compatible.

Let's hope this all gets settled soon.

James

david_prentice: The original (and dominant) Arduinos are AVR. Most code is written for AVR originally. Yes, much has been upgraded for the SAM (Due). Hardly anything has been upgraded for the SAMD (Zero and M0_Pro). Since the warring Italians are behaving like children, most libraries are ignoring the SAMD completely.

I second that, it's a shame :(

I ran into this same issue today.
Here is a possible fix.

It doesn’t properly support the width parameter. However, the String methods
(constructor and concat) both use a fixed value of 4 for width.

Precision is supported.

The code snippet below is for the file /cores/arduino/avr/dtostrf.c
It is the entire file less the license comments.

#include <stdio.h>
#include <stdint.h>

char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
  uint32_t iPart = (uint32_t)val;
  uint32_t dPart = (uint32_t)((val - (double)iPart) * pow(10, prec));

  sprintf(sout, "%d.%d", iPart, dPart);  
  return sout;
}

Let me know if this fix is suitable and I will then create a pull request into the core.

It looks to me like: 1) dtostrf() isn't really a standard C function, but part of the avr-specific avr-libc optimized floating point support. 2) most ARM compilers use "newlib" as their libc implementation. 3) (and probably the gnu standard (C) floating point library, rather than an optimized FP library like AVR uses.) 4) the relevant newlib function is probably "dtoa(d, mode, ndigits, decpt, sign, rve)," 5) dtoa() is a standard unixy function from a couple decades ago. it's got features, and properties, and papers to back up its correctness. It's huge :-( It also doesn't seem to be very well documented. 6) while dtoa() is in the relevant .h files (allowing code that uses it to compile), it doesn't actually seem to be in the library, so the compile fails in link anyway :-( _dtoa_r() works (reentrant version.) (about 3.5k, NOT including the math functions it pulls in.)

you could simply add dtostrf.c to your project.

The AVR dtostrf(), being optimized avr-specific, goes pretty quickly off into avr assembly language ( ftoa_engine.S )

I realised that the above code assumes at least one digit of precision.
Here is an update.

#include <stdio.h>
#include <stdint.h>

char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
  uint32_t iPart = (uint32_t)val;

  if (prec > 0) {
    uint32_t dPart = (uint32_t)((val - (double)iPart) * pow(10, prec));
    sprintf(sout, "%d.%d", iPart, dPart);
  }
  else {
    sprintf(sout, "%d", iPart);
  }

  return sout;
}

You will need to print leading zeros of dpart...

You’re right, the %d formatting doesn’t add the prefix zeros if required.

This is quickly getting messy and probably not very efficient.

There also seems to be rounding error so the least significant digit can be
off by 1.

#include <stdio.h>
#include <stdint.h>

char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
  uint32_t iPart = (uint32_t)val;
  sprintf(sout, "%d", iPart);
    
  if (prec > 0) {
    uint8_t pos = strlen(sout);
    sout[pos++] = '.';
    uint32_t dPart = (uint32_t)((val - (double)iPart) * pow(10, prec));

    for (uint8_t i = (prec - 1); i > 0; i--) {
      size_t pow10 = pow(10, i);
      if (dPart < pow10) {
        sout[pos++] = '0';
      }
      else {
        sout[pos++] = '0' + dPart / pow10;
        dPart = dPart % pow10;
      }
    }

    sout[pos++] = '0' + dPart;
    sout[pos] = '\0';
  }
  
  return sout;
}

FWIW, I dealt with dtostrf on ARM-based Teensy some time ago. It was messy. So far, it seems to work.

I'd be really interested to hear of any "stress test" applications for dtostrf(). Any know of anything?