将float转换为字符串

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

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

ftoa(3.1415)应返回"3.1415"

当你处理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'; 

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

 /* Double to ASCII Conversion without sprintf. Roughly equivalent to: sprintf(s, "%.14g", n); */ #include  #include  // For printf #include  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 

输出:

  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
  1. 使用log -function查找数字的大小m 。 如果幅度为负,则打印"0." 和适量的零。
  2. 连续除以10^m并将结果转换为int以获得十进制数字。 m--下一个数字。
  3. 如果您来到m==0 ,请不要忘记打印小数点"."
  4. 几位后断开。 如果你打破时m>0 ,不要忘记打印"E"itoa(m)

除了log -function,您还可以通过位移和校正指数的偏移量直接提取指数(参见IEEE 754)。 Java有一个双位到函数来获得二进制表示。

  /* * Program to convert float number to string without using sprintf */ #include "iostream" #include "string" #include "math.h" # define PRECISION 5 using namespace std; char* floatToString(float num) { int whole_part = num; int digit = 0, reminder =0; int log_value = log10(num), index = log_value; long wt =0; // String containg result char* str = new char[20]; //Initilise stirng to zero memset(str, 0 ,20); //Extract the whole part from float num for(int i = 1 ; i < log_value + 2 ; i++) { wt = pow(10.0,i); reminder = whole_part % wt; digit = (reminder - digit) / (wt/10); //Store digit in string str[index--] = digit + 48; // ASCII value of digit = digit + 48 if (index == -1) break; } index = log_value + 1; str[index] = '.'; float fraction_part = num - whole_part; float tmp1 = fraction_part, tmp =0; //Extract the fraction part from num for( int i= 1; i < PRECISION; i++) { wt =10; tmp = tmp1 * wt; digit = tmp; //Store digit in string str[++index] = digit +48; // ASCII value of digit = digit + 48 tmp1 = tmp - digit; } return str; } //Main program void main() { int i; float f = 123456.789; char* str = floatToString(f); cout << endl << str; cin >> i; delete [] str; } 

刚刚在https://code.google.com/p/stringencoders/上找到了很好的实施方案

 size_t modp_dtoa(double value, char* str, int prec) { /* Hacky test for NaN * under -fast-math this won't work, but then you also won't * have correct nan values anyways. The alternative is * to link with libmath (bad) or hack IEEE double bits (bad) */ if (! (value == value)) { str[0] = 'n'; str[1] = 'a'; str[2] = 'n'; str[3] = '\0'; return (size_t)3; } /* if input is larger than thres_max, revert to exponential */ const double thres_max = (double)(0x7FFFFFFF); double diff = 0.0; char* wstr = str; if (prec < 0) { prec = 0; } else if (prec > 9) { /* precision of >= 10 can lead to overflow errors */ prec = 9; } /* we'll work in positive values and deal with the negative sign issue later */ int neg = 0; if (value < 0) { neg = 1; value = -value; } int whole = (int) value; double tmp = (value - whole) * powers_of_10[prec]; uint32_t frac = (uint32_t)(tmp); diff = tmp - frac; if (diff > 0.5) { ++frac; /* handle rollover, eg case 0.99 with prec 1 is 1.0 */ if (frac >= powers_of_10[prec]) { frac = 0; ++whole; } } else if (diff == 0.5 && ((frac == 0) || (frac & 1))) { /* if halfway, round up if odd, OR if last digit is 0. That last part is strange */ ++frac; } /* for very large numbers switch back to native sprintf for exponentials. anyone want to write code to replace this? */ /* normal printf behavior is to print EVERY whole number digit which can be 100s of characters overflowing your buffers == bad */ if (value > thres_max) { sprintf(str, "%e", neg ? -value : value); return strlen(str); } if (prec == 0) { diff = value - whole; if (diff > 0.5) { /* greater than 0.5, round up, eg 1.6 -> 2 */ ++whole; } else if (diff == 0.5 && (whole & 1)) { /* exactly 0.5 and ODD, then round up */ /* 1.5 -> 2, but 2.5 -> 2 */ ++whole; } } else { int count = prec; // now do fractional part, as an unsigned number do { --count; *wstr++ = (char)(48 + (frac % 10)); } while (frac /= 10); // add extra 0s while (count-- > 0) *wstr++ = '0'; // add decimal *wstr++ = '.'; } // do whole part // Take care of sign // Conversion. Number is reversed. do *wstr++ = (char)(48 + (whole % 10)); while (whole /= 10); if (neg) { *wstr++ = '-'; } *wstr='\0'; strreverse(str, wstr-1); return (size_t)(wstr - str); } 

你有两个主要问题:

  1. 将位表示转换为字符串
  2. 分配足够的内存来存储字符。

解决第二部分的最简单方法是为每个可能的答案分配足够大的块。 从那开始。 稍后你会想要更聪明,但在你解决问题的数字部分之前不要打扰。

您有两组工具可用于处理问题的数字部分:直接位操作(屏蔽,移位等)和算术运算(*,+,/,加上可能的数学函数链接log() )。

原则上,您可以直接处理按位表示,但如果将来浮点表示格式发生更改,则无法移植。 edgar.holleis建议的方法应该是便携式的。

这就是我想出来的; 它非常高效且非常简单。 它假定您的系统有itoa

 #include  #include  /* return decimal part of val */ int dec(float val) { int mult = floor(val); while (floor(val) != ceil(val)) { mult *= 10; val *= 10; } return floor(val) - mult; } /* convert a double to a string */ char *ftoa(float val, char *str) { if (isnan(n)) { strcpy(str, "NaN"); return str; } else if (isinf(n)) { strcpy(str, "inf"); return str; } char leading_integer[31] = {0}; // 63 instead of 31 for 64-bit systems char trailing_decimal[31] = {0}; // 63 instead of 31 for 64-bit systems /* fill string with leading integer */ itoa(floor(val), leading_integer, 10); /* fill string with the decimal part */ itoa(dec(val), trailing_decimal, 10); /* set given string to full decimal */ strcpy(str, leading_integer); strcat(str, "."); strcat(str, trailing_decimal); return str; } 

在线尝试!

这个要点可能会有所帮助: https : //gist.github.com/psych0der/6319244基本思路是将整个部分和小数部分分开,然后在它们之间连接两个小数。