4.1.1. 为什么printf无法输出浮点数

为了缩减代码体积。目前大部分SDK中,并没有支持%f格式化的输出。也就是并不会打印浮点数。为了处理这个问题,可有两个办法:

  1. 打印浮点数的内存。例如,float a; printf("%x\n", (uint32_t *)&a);

  2. 或者使用put_float函数单独打印浮点数。参考实现如下:

#include <math.h>
#include <string.h>
#include <stdio.h>
#define PAD_RIGHT 1
#define PAD_ZERO 2
#define SIGN    4               // Unsigned/signed long
#define SPACE   8               // Space if plus
#define LEFT    16              // Left justified
#define SPECIAL 32              // 0x
#define LARGE   64              // Use 'ABCDEF' instead of 'abcdef'
#define PLUS    128               // Show plus
#define CVTBUFSIZE  128
static char *flt(char **str, double num, int size, int precision, char fmt, int flags);
static char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf);
static char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf);
void put_float(double fv)
{
    if (__builtin_isnan(fv)) {
        puts("nan");
    } else if (__builtin_isinf(fv)) {
        puts("inf");
    } else {
        flt((void *)0, fv, 10, 3, 'f', ' ' | SIGN);
    }
}
extern void putbyte(char a);
static void printchar(char **str, int c)
{
    if (str) {
        **str = c;
        ++(*str);
    } else {
        putbyte(c);
    }
}
static void cfltcvt(double value, char *buffer, char fmt, int precision)
{
    int decpt, sign, exp, pos;
    char *digits = (char *)0;
    char cvtbuf[CVTBUFSIZE + 1];
    int capexp = 0;
    int magnitude;
    if (fmt == 'G' || fmt == 'E') {
        capexp = 1;
        fmt += 'a' - 'A';
    }
    if (fmt == 'g') {
        digits = ecvtbuf(value, precision, & decpt, & sign, cvtbuf);
        magnitude = decpt - 1;
        if (magnitude < - 4  ||  magnitude > precision - 1) {
            fmt = 'e';
            precision -= 1;
        } else {
            fmt = 'f';
            precision -= decpt;
        }
    }
    if (fmt == 'e') {
        digits = ecvtbuf(value, precision + 1, & decpt, & sign, cvtbuf);
        if (sign) {
            * buffer ++ = '-';
        }
        * buffer ++ = * digits;
        if (precision > 0) {
            * buffer ++ = '.';
        }
        memcpy(buffer, digits + 1, precision);
        buffer += precision;
        * buffer ++ = capexp ? 'E' : 'e';
        if (decpt == 0) {
            if (value == 0.0) {
                exp = 0;
            } else {
                exp = - 1;
            }
        } else {
            exp = decpt - 1;
        }
        if (exp < 0) {
            * buffer ++ = '-';
            exp = - exp;
        } else {
            * buffer ++ = '+';
        }
        buffer[2] = (exp % 10) + '0';
        exp = exp / 10;
        buffer[1] = (exp % 10) + '0';
        exp = exp / 10;
        buffer[0] = (exp % 10) + '0';
        buffer += 3;
    } else if (fmt == 'f') {
        digits = fcvtbuf(value, precision, & decpt, & sign, cvtbuf);
        if (sign) {
            * buffer ++ = '-';
        }
        if (* digits) {
            if (decpt <= 0) {
                * buffer ++ = '0';
                * buffer ++ = '.';
                for (pos = 0; pos < - decpt; pos ++) {
                    * buffer ++ = '0';
                }
                while (* digits) {
                    * buffer ++ = * digits ++;
                }
            } else {
                pos = 0;
                while (* digits) {
                    if (pos ++ == decpt) {
                        * buffer ++ = '.';
                    }
                    * buffer ++ = * digits ++;
                }
            }
        } else {
            * buffer ++ = '0';
            if (precision > 0) {
                * buffer ++ = '.';
                for (pos = 0; pos < precision; pos ++) {
                    * buffer ++ = '0';
                }
            }
        }
    }
    * buffer = '\0';
}
static void forcdecpt(char *buffer)
{
    while (* buffer) {
        if (* buffer == '.') {
            return;
        }
        if (* buffer == 'e' || * buffer == 'E') {
            break;
        }
        buffer ++;
    }
    if (* buffer) {
        int n = strlen(buffer);
        while (n > 0) {
            buffer[n + 1] = buffer[n];
            n --;
        }
        * buffer = '.';
    } else {
        * buffer ++ = '.';
        * buffer = '\0';
    }
}
static void cropzeros(char *buffer)
{
    char *stop;
    while (* buffer && * buffer != '.') {
        buffer ++;
    }
    if (* buffer ++) {
        while (* buffer && * buffer != 'e' && * buffer != 'E') {
            buffer ++;
        }
        stop = buffer --;
        while (* buffer == '0') {
            buffer --;
        }
        if (* buffer == '.') {
            buffer --;
        }
        while ((*++ buffer = * stop ++));
    }
}
static char *flt(char **str, double num, int size, int precision, char fmt, int flags)
{
    char tmp[80];
    char c, sign;
    int n, i;
    if (flags & LEFT) {
        flags &= ~ PAD_ZERO;
    }
    // Determine padding and sign char
    c = (flags & PAD_ZERO) ? '0' : ' ';
    sign = 0;
    if (flags & SIGN) {
        if (num < 0.0) {
            sign = '-';
            num = - num;
            size --;
        } else if (flags & PLUS) {
            sign = '+';
            size --;
        } else if (flags & SPACE) {
            sign = ' ';
            size --;
        }
    }
    // Compute the precision value
    if (precision < 0) {
        precision = 6;    // Default precision: 6
    } else if (precision == 0 && fmt == 'g') {
        precision = 1;    // ANSI specified
    }
    // Convert floating point number to text
    cfltcvt(num, tmp, fmt, precision);
    // '#' and precision == 0 means force a decimal point
    if ((flags & SPECIAL) && precision == 0) {
        forcdecpt(tmp);
    }
    // 'g' format means crop zero unless '#' given
    if (fmt == 'g' && !(flags & SPECIAL)) {
        cropzeros(tmp);
    }
    n = strlen(tmp);
    // Output number with alignment and padding
    size -= n;
    if (!(flags & (PAD_ZERO | LEFT))) while (size -- > 0) {
            printchar(str, ' ');    //* str ++ = ' ';
        }
    if (sign) {
        printchar(str, sign);    //* str ++ = sign;
    }
    if (!(flags & LEFT)) while (size -- > 0) {
            printchar(str, c);    //* str ++ = c;
        }
    for (i = 0; i < n; i ++) {
        printchar(str, tmp[i]);    //* str ++ = tmp[i];
    }
    while (size -- > 0) {
        printchar(str, ' ');    //* str ++ = ' ';
    }
    return (char *)str;
}
static char *cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, int eflag)
{
    int r2;
    double fi, fj;
    char *p, * p1;
    if (ndigits < 0) {
        ndigits = 0;
    }
    if (ndigits >= CVTBUFSIZE - 1) {
        ndigits = CVTBUFSIZE - 2;
    }
    r2 = 0;
    * sign = 0;
    p = & buf[0];
    if (arg < 0) {
        * sign = 1;
        arg = - arg;
    }
    arg = modf(arg, & fi);
    p1 = & buf[CVTBUFSIZE];
    if (fi != 0) {
        p1 = & buf[CVTBUFSIZE];
        while (fi != 0) {
            fj = modf(fi / 10, & fi);
            *-- p1 = (int)((fj + .03) * 10) + '0';
            r2 ++;
        }
        while (p1 < & buf[CVTBUFSIZE]) {
            * p ++ = * p1 ++;
        }
    } else if (arg > 0) {
        while ((fj = arg * 10) < 1) {
            arg = fj;
            r2 --;
        }
    }
    p1 = & buf[ndigits];
    if (eflag == 0) {
        p1 += r2;
    }
    * decpt = r2;
    if (p1 < & buf[0]) {
        buf[0] = '\0';
        return buf;
    }
    while (p <= p1 && p < & buf[CVTBUFSIZE]) {
        arg *= 10;
        arg = modf(arg, & fj);
        * p ++ = (int) fj + '0';
    }
    if (p1 >= & buf[CVTBUFSIZE]) {
        buf[CVTBUFSIZE - 1] = '\0';
        return buf;
    }
    p = p1;
    * p1 += 5;
    while (* p1 > '9') {
        * p1 = '0';
        if (p1 > buf) {
            ++*-- p1;
        } else {
            * p1 = '1';
            (* decpt)++;
            if (eflag == 0) {
                if (p > buf) {
                    * p = '0';
                }
                p ++;
            }
        }
    }
    * p = '\0';
    return buf;
}
static char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf)
{
    return cvt(arg, ndigits, decpt, sign, buf, 1);
}
static char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf)
{
    return cvt(arg, ndigits, decpt, sign, buf, 0);
}