2

I am trying to convert a float number to a char array. sprintf() will not work for me and neither will dtostrf. Since I have a limit in the decimal number (it is 5) I tried this:

    int num = f;
    int decimales = (f-num)*10000;

Everything worked fine until I typed the number 123.050. Instead of giving me the decimal part as "0.50" it gives me a 5000, because it does not count the 0 now that my "decimal" variable is an int.

Is there any other way to convert it?

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 2
    You have no floating point number. You have two `int` values... Declare your float and use `int chars_reqd = snprintf (NULL, 0, "%.5f", fvalue);` then allocate `chars_reqd + 1` characters or use a fixed array such as `char floatstr[64];` and `sprintf (floatstr, "%.5f", fvalue);` – David C. Rankin May 16 '20 at 03:43
  • using sprintf(cad,"%.5f", f) doesn't work for you either? – Eduardo Pascual Aseff May 16 '20 at 03:43
  • 1
    (a) Explain why `sprintf` will not work for you. (b) Explain completely the output you want. Should the fraction part always have exactly five digits? At most 5? Is there a limit on the integer part? What about very large (1e300) or very small (1e-300) numbers. What about negative numbers? (c) 10000 times .05 is 500, not 5000. Is the error in your example in the 10000, the 5000, or something else? (d) Should numbers be rounded or truncated to five decimal digits? Do small errors in rounding or truncation matter? – Eric Postpischil May 16 '20 at 08:13
  • 1
    Please confirm you want 4 decimals, as the `10000` suggests. – Déjà vu May 16 '20 at 10:15

2 Answers2

0

Use %04d in printf()

 const int D4 = 10000;

 double f = 123.050;
 int i = round(f*D4);
 int num = i / D4;
 int decimales = i % D4;
 printf("%d.%04d\n", num, decimales);

gives

123.0500

If you worry about an int overflow (since we multiply the number by 10000), use long, or long long instead...

To save decimals in a string including the leading zero(es)

char sdec[5];
sprintf(sdec, "%04d", decimales);

sdec contains the decimals, including the leading zero.

(see also Is floating point math broken? )

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • ok.. but is there any way that my decimal variable saves that leading 0? – Daniel Mendez May 16 '20 at 04:08
  • This answer is premature. There are errors and omissions in the question regarding the number of digits wanted, the precise formatting, and the rounding desired. Additionally, this answer is sloppy: Multiplying by rounding 10000 introduces rounding errors, and the code in this answer truncates where it may be desired to round. Note that `(f-num + 0.0000001)` can cause the fraction part to exceed 1, causing an incorrect display. – Eric Postpischil May 16 '20 at 10:03
  • `round((f-num)*10000)` will cause 123.99996… to be displayed as “123.10000”. – Eric Postpischil May 16 '20 at 10:23
  • Which I would want for this program ... usually you want to round.... – Déjà vu May 16 '20 at 10:31
  • 1
    No, if rounding is being used, 123.99996… should display as “124.0000”. “123.10000” is wrong. – Eric Postpischil May 16 '20 at 10:44
0

You want a quick and dirty way to produce a string with the decimal representation of your float without linking sprintf because of space constraints on an embedded system. The number of decimals is fixed. Here is a proposal:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *ftoa(char *dest, size_t size, double val, int dec) {
    char *p = dest;
    char *q = dest + size;
    long long mul = 1;
    long long num;
    int i;
    if (size == 0)
        return NULL;
    *--q = '\0';
    if (size == 1)
        goto fail;
    if (val < 0) {
        val = -val;
        if (p >= q)
            goto fail;
        *p++ = '-';
    }
    for (i = 0; i < dec; i++) {
        mul *= 10;
    }
    num = (long long)(val * mul + 0.5);
    for (i = 1; i < dec + 2 || num > 0; i++) {
        if (p >= q)
            goto fail;
        *--q = '0' + (num % 10);
        num = num / 10;
        if (i == dec) {
            if (p >= q)
                goto fail;
            *--q = '.';
        }
    }
    memmove(p, q, dest + size - q);
    return dest;
fail:
    return memset(dest, '*', size - 1);
}

int main(int argc, char *argv[]) {
    char buf[24];

    for (int i = 1; i < argc; i++) {
        double d = strtod(argv[i], NULL);
        int dec = 5;
        if (i + 1 < argc)
            dec = atoi(argv[++i]);
        printf("%s\n", ftoa(buf, sizeof buf, d, dec));
    }
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • `num = (long long)(val * mul + 0.5);` can fail to provide the best result when `val * mul + 0.5` is inexact. – chux - Reinstate Monica May 17 '20 at 02:56
  • @chux-ReinstateMonica: you are correct, the above is a very simplistic approach to the problem. A complete solution is to say the least *non trivial*. I should refine the answer with limitations for this function. – chqrlie May 17 '20 at 14:43