How to convert a float to a string without sprintf in C?

Viewed 482

My code is taking a string format and compose it into a buffer (without sprintf, itoa etc).

I'm having trouble converting a float number to string, as I need to have precision of at most 7 digits with no trailing zeros. as well as convert each number in the float variable to char (but in that matter I don't need any help).

I tried several methods, all including math calculations, but nothing has brought me to the desired outcome. Here's my code so far, but it is messy and also sometimes gives incorrect outcome. I also believe there is a more clean and less-complicated way to do it. any help will be widely appreciated.

            if (*format == 'f') {
                float f = *(float*)ptrs[counter];
                char str[30];
                int b, c, m, k = 7, i = 0, l = 0;
                int a = f;
                f -= a;
                while (k >= 0) {
                    l = pow(10, k);
                    m = a / l;
                    if (m > 0) {
                        break;
                    }
                    k--;
                }
                printf("%d", k);
                for (l = k + 1; l > 0; l--) {
                    b = pow(10, l - 1);
                    c = a / b;
                    str[i++] = c + 48;
                    a %= b;
                }
                str[i++] = '.';
                for (l = 0; l < 7; l++) {
                    f *= 10.0;
                    b = f;
                    str[i++] = b + 48;
                    f -= b;
                }
                for (i = 0; i < 7; i++) {
                    *buffer = str[i];
                    buffer++;
                }
                counter++;
                str[i] = '\0';

for example:

input: float v2 =0.9372;
output: .937199
desired output: 0.9372

input: float v2 =0.25000;
output: 1.25000
desired output: 0.25

1 Answers

he's messy and also sometimes gives incorrect outcome

At some point given the base 2 nature of typical floating point, the programmer needs to make choices:

With common floating point encoding, there are also issues of

  • Infinity

  • Not-a-number.

  • Oddities like -0.0

And how portable to make the code?


Sources of inexactness

OP's use of int limits float to about [INT_MIN...INT_MAX]. Code fails for float much outside that range. Could use long long to get some more range without a great deal of code change. Better yet research float modff(float value, float *iptr).

float f = ...;
// int a=f;
long long a=f;

Repeated use of f*=10.0 below with a fractional value in f injects a possible rounding (inexactness), at each iteration.

for(l=0;l<7;l++) {
  f*=10.0;

Code makes no effort to round given that f may not be 0.0 after the fraction forming for(l=0;l<7;l++) { f*=10.0; loop. I see this as a place to improve precision. This area is also tricky as the round-up may effect many leading digits when they are 9, eventually needing a shift of the string buffer. In the 0.93721, after this loop, f was about 0.74. Since more than 0.5, a rounding would have given the answer of ".9371999" --> ".9372000".

Code aims for 7 decimal digits after the .. Values, as a decimal in code, assigned to a float match to 6 to 9 significant decimal places - which includes digits to the left of .. Expectations should not get too high.


Improvement idea

When the number of fraction digits desired is small, perform 1 scaling/round

// ASSUME f >= 0.0, (sign handling done before here)
long long a=f;
f-=a;
int int_f = round(f*pow(10, 7));
if (int_f < 10000000) {
  later_printf_the_7_digits(int_f);
} else {
  a++;
  later_printf_the_7_digits(0);
}
Related