将float转换为字符串

SIV*_*IVA 18 c c++ floating-point

如何在没有库函数的情况下将浮点整数转换为C/C++中的字符串sprintf

我正在寻找一个函数,例如char *ftoa(float num)转换num为字符串并返回它.

ftoa(3.1415)应该回来"3.1415".

and*_*der 18

根据Sophy Pal的回答,这是一个稍微更完整的解决方案,它考虑了数字零,NaN,无限,负数和科学记数法.虽然sprintf仍然提供更准确的字符串表示.

/* 
   Double to ASCII Conversion without sprintf.
   Roughly equivalent to: sprintf(s, "%.14g", n);
*/

#include <math.h>
#include <string.h>
// For printf
#include <stdio.h>

static double PRECISION = 0.00000000000001;
static int MAX_NUMBER_STRING_SIZE = 32;

/**
 * Double to ASCII
 */
char * dtoa(char *s, double n) {
    // handle special cases
    if (isnan(n)) {
        strcpy(s, "nan");
    } else if (isinf(n)) {
        strcpy(s, "inf");
    } else if (n == 0.0) {
        strcpy(s, "0");
    } else {
        int digit, m, m1;
        char *c = s;
        int neg = (n < 0);
        if (neg)
            n = -n;
        // calculate magnitude
        m = log10(n);
        int useExp = (m >= 14 || (neg && m >= 9) || m <= -9);
        if (neg)
            *(c++) = '-';
        // set up for scientific notation
        if (useExp) {
            if (m < 0)
               m -= 1.0;
            n = n / pow(10.0, m);
            m1 = m;
            m = 0;
        }
        if (m < 1.0) {
            m = 0;
        }
        // convert the number
        while (n > PRECISION || m >= 0) {
            double weight = pow(10.0, m);
            if (weight > 0 && !isinf(weight)) {
                digit = floor(n / weight);
                n -= (digit * weight);
                *(c++) = '0' + digit;
            }
            if (m == 0 && n > 0)
                *(c++) = '.';
            m--;
        }
        if (useExp) {
            // convert the exponent
            int i, j;
            *(c++) = 'e';
            if (m1 > 0) {
                *(c++) = '+';
            } else {
                *(c++) = '-';
                m1 = -m1;
            }
            m = 0;
            while (m1 > 0) {
                *(c++) = '0' + m1 % 10;
                m1 /= 10;
                m++;
            }
            c -= m;
            for (i = 0, j = m-1; i<j; i++, j--) {
                // swap without temporary
                c[i] ^= c[j];
                c[j] ^= c[i];
                c[i] ^= c[j];
            }
            c += m;
        }
        *(c) = '\0';
    }
    return s;
}

int main(int argc, char** argv) {

    int i;
    char s[MAX_NUMBER_STRING_SIZE];
    double d[] = {
        0.0,
        42.0,
        1234567.89012345,
        0.000000000000018,
        555555.55555555555555555,
        -888888888888888.8888888,
        111111111111111111111111.2222222222
    };
    for (i = 0; i < 7; i++) {
        printf("%d: printf: %.14g, dtoa: %s\n", i+1, d[i], dtoa(s, d[i]));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

  1. printf:0,dtoa:0
  2. printf:42,dtoa:42
  3. printf:1234567.8901234,dtoa:1234567.89012344996444
  4. printf:1.8e-14,dtoa:1.79999999999999e-14
  5. printf:555555.55555556,dtoa:555555.55555555550381
  6. printf:-8.8888888888889e + 14,dtoa:-8.88888888888888e + 14
  7. printf:1.1111111111111e + 23,dtoa:1.11111111111111e + 23

  • 负无穷大打印 `inf` 对吗?它不应该检查符号位并在负数时返回`-inf`吗? (2认同)

小智 11

当你处理fp数字时,它可以得到非常复杂但算法简单,类似于edgar holleis的答案; 荣誉!它很复杂,因为当你处理浮点数时,根据你选择的精度,计算会略微偏离.这就是为什么将浮点数与零进行比较不是很好的编程习惯.

但是有一个答案,这是我尝试实现它.在这里,我使用了容差值,因此您最终不会计算太多小数位,从而导致无限循环.我相信可能会有更好的解决方案,但这应该有助于您更好地了解如何做到这一点.

char fstr[80];
float num = 2.55f;
int m = log10(num);
int digit;
float tolerance = .0001f;

while (num > 0 + precision)
{
    float weight = pow(10.0f, m);
    digit = floor(num / weight);
    num -= (digit*weight);
    *(fstr++)= '0' + digit;
    if (m == 0)
        *(fstr++) = '.';
    m--;
}
*(fstr) = '\0';
Run Code Online (Sandbox Code Playgroud)

  • "容忍"未被使用. (3认同)

edg*_*eis 6

  1. 使用log-function查找m数字的大小.如果幅度是负打印"0."和适当的零.
  2. 连续除以10^m并将结果转换为int以获取十进制数字.m--为下一个数字.
  3. 如果你来了m==0,不要忘记打印小数点".".
  4. 几位后断开.如果m>0你打破了,不要忘记打印"E"itoa(m).

log您也可以通过比特移位和校正指数的偏移量来直接提取指数,而不是函数(参见IEEE 754).Java有一个双位到函数来获得二进制表示.


vit*_*aut 6

您可以使用 C++20std::format或基于的{fmt} 库std::format将浮点数转换为字符串,例如:

std::string s = std::format("{}", M_PI);
Run Code Online (Sandbox Code Playgroud)

与此方法相比,此方法的优点sprintf是为std::format您提供最短的十进制表示形式并保证往返。