How to safely and reasonably convert a float or double to string or char array?

I do not have too much experience with AVR assembly and considering the IDE support for assembly I gave up on that immediately.

"I don't understand" is a poor excuse for giving up.

"__ftoa_engine" does most of the hard work and probably more efficient then what I could write. However I do not know how to include it or use it. If I learn to do that, the rest is done in few minutes. But I do not know how to include this file.

Figuring it out would be a better use of time than re-writing it. As you say, adding a "max field width" to the ftoa_engine should be relatively trivial.

#include <avr/io.h>
I could not find this one though. I thought this is supposed to be the easiest one.

That's because this is Atmel-provided code rather than avr-libc, or something like that.
it shows up in the installed toolchain in .../avr/include/avr/io.h

Also, __ftoa_engine should certainly be in libc. Make sure to declare it as a C function, in c++ or ino files:

extern "C" int __ftoa_engine (double val, char *buf, unsigned char prec, unsigned char maxdgs);

@westfw

"I don't understand" is a poor excuse for giving up.

Well there is no way at least based on the replies I got over hear to write assembly code which takes variables from outside in Arduino IDE. let's say I have "uint8_t a;" how do I use this in my assembly code? This is why I gave up on it. If there is no way around that, nothing can be done as far I am concerned and in replies here and outside I could not find any solution for this problem.

Figuring it out would be a better use of time than re-writing it. As you say, adding a "max field width" to the ftoa_engine should be relatively trivial

Actually __ftoa_engine returns the exponent in decimal and the number digits as much as it is requested by the user (max) in a buffer. It is like scientific form but with exponent part being an integer. That is why I was hoping to find a way to use that in arduino IDE. But everything I tried failed. I even tried to copy paste the assembly code to get it working but due to the problem in previous paragraph it is not possible.

for more info on __ftoa_engine check:
https://sourcecodebrowser.com/avr-libc/1.8.0/ftoa__engine_8h.html#a0700887e129ad889bdff83eac78c8797

#include <avr/io.h>

I faced other problems before getting to io.h as stated above.

"__ftoa_engine" is an internal function which IDE does not let me use it. It says it is not defined in this scope. I tried to add the related .h files but that also did not work. Unless I have made a mistake.

I searched for arduin __ftoa_engine and google has less than 20 pages showing up. 2 of it being this post.

"__ftoa_engine" is an internal function which IDE does not let me use it.

I just showed you how to use it...

extern "C" int __ftoa_engine (double val, char *buf, unsigned char prec, unsigned char maxdgs);

there is no way over hear to write assembly code which takes variables from outside in Arduino IDE. let's say I have "uint8_t a;" how do I use this in my assembly code?

Of course there is. This (along with dtostr() itself) is avr-gcc and C compiler stuff, and not "arduino stuff", so you have to expand your search. Dealing with "uint8_t a" depends on exactly where it is. If it's a function parameter, it will show up in one of the AVR registers (which one is based on its position in the parameter list.) If it's global, it accessible via name.
See Frequently Asked Questions
Try this for a starter (test.ino)

#include <stdio.h>
FILE* ser;
extern "C" int __ftoa_engine (double val, char *buf, unsigned char prec, unsigned char maxdgs);

int fput(char c, FILE* f) {
  Serial.write( c);
  return 0;
}

void setup() {
  Serial.begin(9600);
  stdout = fdevopen(fput, NULL);
}

char buffer[20];
void ftoa_eng_study(float f)
{
  memset(buffer, 0, sizeof(buffer));
  int8_t exponent = __ftoa_engine(f, buffer, 8, 19);
  printf("Floating input according to Serial.print(): ");
  Serial.println(f, 8);
  printf("   engine Exponent: %d, flags 0x%02x Digits: ", exponent, buffer[0]);
  for (byte i = 1; i < 20; i++) {
    if (buffer[i] == 0) break;
    Serial.write(buffer[i]);
  }
  Serial.println();
  Serial.println();
}

void loop ()
{
  ftoa_eng_study(PI);
  ftoa_eng_study(987.654e20);
  ftoa_eng_study(42.0);
  ftoa_eng_study(6.626e-34);

  Serial.println();
  delay(10000);
}

(Code modified. Hmm. I don't know that I understand what happened to Plank's constant!)

@westfw Thank you so much, it is time for me to sleep, but I appreciate all the inputs. Sorry that I did not understand the fact that you are showing me how to use it.

alirezasafdari:
@westfw Thank you so much, it is time for me to sleep, but I appreciate all the inputs. Sorry that I did not understand the fact that you are showing me how to use it.

You have been shown many different ways of accomplishing your goal and you have simply written them off with lame statements such as "I don't understand it" or "it will take too many resources" while not even trying to actually do it.

We are all wasting our time with you and, as for me I'm done with this thread.

@westfw thank you, I used the tip you gave me for __ftoa_engine. It was a new concept and I read about in here (http://www.geeksforgeeks.org/extern-c-in-c/). Thank you again, without your guidance I would never learn that.

@krupski, sorry to let you down. I did not mean to. Anyways later if I release the libraries which I am working on right now, I think more people will be benefited if no tweaks are needed on the IDE side. Again I am sorry if I have made you upset.

Things I have done which might be useful for future users:

  1. I documented the dtoa_prf function which is the actual function when dtostrf is called. The code does not have any documentation and I think it has been implemented in a very smart and efficient way and without documentation it is hard to understand it.
  2. I made a safe version and tried to test it as much as I could in few aspects.
    i) dtosrf and myDtostrf produce the same exact results when size is not an issue. (it was tested for hours by generating random float numbers)
    ii) NAN, INF, -INF are tested with different sizes to make sure correct result is obtained
    iii) size was tested on some variables (I have not seen any suspicious behavior but I think more testing is required which hopefully happens over time when I am testing other parts of the project)
  3. finding out that another person has faced the same issue but the whole thread is in german :slight_smile:

My overall code is in here (well, it was too long so had to attach it)

myDtoa_prf part of this code is exactly same as dtoa_prf and buy reading the comments you can easily understand the original dtoa_prf too.

There is only one part which I did not understand and that is

if ((signed char)nDigits < 1) 
	nDigits = 1;
else if (nDigits > 8) 
	nDigits = 8;

I tested with

if ((signed char)nDigits < 0) 
	nDigits = 1;
else if (nDigits > 8) 
	nDigits = 8;

and it produce exact same results after testing with random float number, but I think it is faster because it can be branch if minus. (I am not very familiar with AVR assembly instruction yet but from what I recall from 6800 instruction set, branch if minus was faster than comparing and then branching)
I would be happy if anyone can point out why there is a 1 there instead of 0. I am afraid it is for a very special case and the random number generator has not generated that.

The code I have attached contain the testing section. Let me know if you think the testing part is not accurate, I think it is fairly accurate but just to be sure.

And lastly,
https://www.mikrocontroller.net/topic/301125
shows folks who solved the same problem. However my version has one difference. Their version does not print the number at all if it does not fit. My version it prints as much as it can and then set the bool argument which passed by reference to false.

I would like to thank you all for the support, suggestions and your efforts.

floatEngineTest.ino (22.2 KB)