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

With dtostrf you get to specify how many digits to display.

I don't think that is true. If I remember correctly, you can only specify minimum size or digits. There is no maximum. I'm pretty sure I've run into what he's found and its not obvious how to shield yourself from this problem. 999--99.0 is one thing, but another is the 1.0000000009 kind of thing. That also will blow up your string buffer.

Granted I was coding a teensy when I ran into this, so maybe the Arduino itself handles this better?

-jim lee

No, apparently you don't understand how numbers work. There are no such thing as numbers that have more or less digits than other numbers. All numbers have an infinite number of digits. If you say 1 then I can say 1.0 and someone else can say 1.00. Whether or not you write all the digits when you write the number back to the human doesn't matter. The math isn't being done with the output of dtostrf. The math is being done with the actual floating point number. So you're not losing on your math. Your math in your program is safe. The only thing that String is for is showing the human what the value is. And if you try to show him 35 digits then you are lying to your user about the value.

If the result is some number with 35 digits why in the world would a user want you to output that as a 35 digit number with only 6 that matter and 29 that are basically just made up when you can give him scientific notation and only the part that's accurate and actually conveys the value.

This problem isn't unique to computers. Big numbers come up in science all the time and with propagation of error from measurements we know how many significant digits we actually have. And we don't ever write out 35 digit numbers there. At least not unless we actually have 35 digits of precision.

Sure, big numbers might come up and they come up all the time. But that doesn't mean it makes sense to write them all the way out without scientific notation.

All numbers have an infinite number of digits.

No, not the counting numbers (i.e. integers).

BulldogLowell:
No, not the counting numbers (i.e. integers).

Yes, even them. You have to separate the abstract concept of a number, from the concrete representation of the number. For the abstract concept pretend I have 3 apples. Would you write that I have 3, 3.0, 3.00, 3.000, 3.0000. How many apples do I have there?

Now the concrete is different. If someone wrote that I had 3 apples, you aren't entirely sure I didn't have a little piece of an extra apple or that I hadn't taken a bite out of one of them and the writer rounded off. But if he said that I had 3.0 apples, you could be sure that I hadn't bitten off more than a tenth of one. If he said I had 3.00 apples then you're sure I haven't bitten off more than a hundredth of one.

The key concept that the OP needs to grasp is the concept of significant digits. Many a student has messed up their grade in chemistry class over sig figs.

You are not correct regarding the counting numbers.

You are mixing oranges with your apple analogy.

Decimals do not exist in the set of integers, for example.

BulldogLowell:
You are not correct regarding the counting numbers.

You are mixing oranges with your apple analogy.

Decimals do not exist in the set of integers, for example.

You're talking about representations of quantities.

I am talking about numbers in the abstract. Not the representation of quantity but the actual quantity itself. I don't mean the part that you read and write, but the part that you actually do math with.

If I have 3 apples and you have 3.0 apples, sure you represented yours with an integer, but we both have the same actual quantity of apples. If we traded, it would still be an even trade. Unless, of course, you were rounding.

You are not correct.

There are things that may be counted that may not be represented in decimal form.

I’m not typing into a 64.127894 bit computer.

There isn’t even a need for a decimal when using the set of integers.

BulldogLowell:
There isn’t even a need for a decimal when using the set of integers.

There isn't a need sure, but that doesn't mean that the number isn't that exact. Again, you're talking about how you would represent that number. Whether or not you should WRITE a decimal point. But the number itself is the same number even if you do.

Are you telling me that 64 != 64.00?

In the set of integers, 64.00 does not exist. You are comparing a number against a non-existent/undefined thing.

Just like 1.0/0 does not equal infinity.

Take a freshman level number theory class or just read the text and you’ll understand.

Not withstanding all of this, OP is really off the reservation with his/her expectations of a floating point number on a 16bit platform (even a 64 bit platform!).

In the science world we actually do treat the counting numbers differently from measured numbers. So if we measure with a ruler 3.1 cm then we say that number has 2 significant figures and when you do math with it you have to count those two sig figs and no more.

Now when we are counting that is different from measuring. If I measure with a ruler then I can say it was real close to the 3 line, but I can never say it was exactly that. We could always zoom in more until we have a microscope that can see the Planck length.

But when we are counting we know exactly. That isn't like measuring. If I count 3 cows in the field, looking closer isn't going to reveal 0.001 cows that we just couldn't see before. So when you're doing calculations in chemistry class and you're counting your sig figs, you actually count the counting numbers as infinite accuracy to keep them from interfering with the number of sig figs coming from the measured numbers. So in that treatment you are pretending that the integer has an infinite number of digits. Not that you would ever write it that way, but for the purposes of the calculation you treat it as that exact.

BulldogLowell:
Take a freshman level number theory class or just read the text and you’ll understand.

I have. And I'm talking about quantity, not number set theory. To say that I have a certain quantity does not limit me to any particular set of numbers narrower than the set of real numbers. Perhaps I should concede that I should have used the word quantity instead of number in the original line that set this off.

Either way, I think we're both arguing true points about two completely different things.

Here's another point for the OP if he is still with us. We work with a number all the time that has a whole bunch of digits. Think about pi. If you are working with pi, do you need to make sure you capture ALL of the digits (an impossibility) or do you round it at some point where it is accurate enough for your particular needs?

For solution number 2, you gotta be kidding.

I would never kid anyone who can't be bothered to read basic documentation, and is actually surprised to learn about buffer overflow!

Honestly, I am really sad to realize that non of you try to answer the question and instead try to find off topic stuff that are not even related.

As you clearly see in the title, I have asked for a safe way. I totally understand the concerns you guys highlight with significant digits and all but that is totally unrelated to the question.

My interest is not in showing the user a huge number, my interest is in making sure if such a number for any reason was placed in the float (which is possible, even though it may be stupid or whatever) and the typical dtostrf function is called, it does not change the memory content of adjacent locations .

I seriously do not understand why so much focused is put on the meaning of the number rather than the possibility of dtosrf being called with a huge number. Also take note it can be possible that the user misses a very rare case when a number which is supposed to be one digit, become 2 digits and the buffer is only allocating enough room for one digit. the current dtostrf is going to write in another memory location which is not supposed to.

One solution is to check every float before calling the dtostrf but as I have mentioned before that would take so much time since you have to make sure each number is within the positive and negative boundary. That is 2 float comparison for each time dtostrf is called and the boundary changes
based on formatting. I really do not consider this a solution, if you do; then you may just stick to this solution and hopefully you will have enough time for everything else in the project.

Yeah, I may have misread the documentation but that is because the dtostrf is very unsafe and I had higher expectation from the general dtostrf. This implementation is ok for when you have an operating system or something to stop you from screwing the memory but in arduino we clearly do not have such a guard and you can try all you want talking about significant digit and all the off topic stuff you have written here but you do not change the fact that this function is very unsafe and making it safe is actually very very easy. As I stated I will do it myself and share the code here withing next few days, so probably folks who understand code safety can be benefited.

After all if you have suggestion on topic, I will be more than happy to hear it.

The thing you don't understand is that there's no difference in how that huge number is represented and how 1.0 is represented insofar as number of digits is concerned. They're both going to be 32bit floating point which has about 6 or 7 digits of precision. When you call dtostrf you get to specify the precision so you get to specify how many digits get printed.

If you're really worried about it, re-enable floating point support for format strings and use snprintf

Delta_G Please read the last part first.

"how that huge number is represented and how 1.0 is represented"
You are wrong!

{
float magicNumber = 1.0;
....
float a = magicNumber;
char buffer[3];
dtostrf(a, 0, 1, buffer);
}

[1]- [0]
works perfectly fine but

{
float magicNumber = 1000.0;
....
float a =magicNumber;
char buffer[3];
dtostrf(a, 0, 1, buffer);
}

[1][0][0][0]- [0]
is a disaster. I hope you understand that.

Yes this could be avoided if we could know the exact value that comes out of magicNumber. But this magicNumber may come from another unit and if that unit fails to stay in range, you have to make sure the arduino side does not explode = writing in memory location that does not belong to the buffer.

"They're both going to be 32bit floating point which has about 6 or 7 digits of precision."
I do not know how is this even related. Yes all floats and doubles take 32 bit in Arduino but does it mean when they are printed by dtostrf with zero digit after the decimal point, the take same amount of memory? definitely not. (I hope you understand this too)

"When you call dtostrf you get to specify the precision so you get to specify how many digits get printed. "
You have probably read the link below but this is not how the arduino dtostrf work. You have probably read "prec determines the number of digits after the decimal sign" but in arduino it is "prec determines the number of digits after the decimal point". You can give it a shot if you do not believe me.

I think this is the reason why there is confusion here. I also checked the stdlib.h and the description is wrong.

I do not think the rest of the sprintf over head is not worth it nor needed.

That does seem a bit annoying, doesn't it?
How about dtostre(), which prints in the "+1.2345e-06" format? You still get to specify "precision" for number of places after the decimal point, and that should yield a known maximum total width...

That's the one I was looking for. I knew you could get it out in scientific notation. That's what you do with big numbers. dtostre

alirezasafdari:
Hi

I have been using in dtosrtf() in a relatively big project and some usual stuff was happening. Long story short, I took "The caller is responsible for providing sufficient storage in s" not as serious as I should. I thought if I do not provide enough width it automatically understand not to write in the rest of the memory.

Someone in another forum suggested using 100 byte array to be safe, but I think this is not a reasonable solution on a AVR/Arduino with limited memory.

So sad that the Arduino IDE developers refuse to let users do something like this:

int main (void)
{
    init();
    Serial.begin (115200);

    STDIO.open (Serial);

    double a = 123.45678;
    double b = -0.0045;
    double c = 1.2345678;

    fprintf (stdout, "The variable 'a' has a value of %7.3f\n", a);
    fprintf (stdout, "The variable 'b' has a value of %7.3f\n", b);
    fprintf (stdout, "The variable 'b' has a value of %7.3f\n", c);

    while (1);
}

Output from this code:

[b]The variable 'a' has a value of 123.457
The variable 'b' has a value of  -0.004
The variable 'b' has a value of   1.235
[/b]

See? No "dtostrf" baloney.

krupski:
So sad that the Arduino IDE developers refuse to let users do something like this:

Is it really the IDE that stops you? I thought it was GCC that was the issue there. And they don't refuse to let you. You did it. It's open source, you can do anything you want with it.

Delta_G:
Is it really the IDE that stops you? I thought it was GCC that was the issue there. And they don't refuse to let you. You did it. It's open source, you can do anything you want with it.

GCC does everything just fine. All the IDE does is build GCC command strings and passes them to the compiler. Any limitations in programming an Arduino are due to the IDE, NOT GCC.

To use ordinary printf and floating point, all that's necessary is to "hack" the IDE to link [b]libprintf_flt.a[/b] into the compiled binary instead of [b]libprintf_min.a[/b].

Or, better yet, add a checkbox option in Preferences to choose which one to link (floating point when you need it, the non-floating point one when you don't - saves a few bytes of flash).

The "sketch" I show above was compiled by a plain old Arduino 1.0.5 IDE with a few minor differences:

(1) I turned on the floating point option checkbox (custom option in my IDE).
(2) I connected the serial port to the standard IO streams to allow using fprintf (the STDIO.open() call).
(3) I used "int main (void)" instead of that "setup/loop" nonsense (although it would work the same with setup/loop).

See? I didn't need "dtostrf". I didn't need to worry about buffer overflows. I didn't need a dozen "Serial.print()" calls just to print a few lines, I didn't need to play games checking string lengths and inserting padding to get the numbers to line up right.