将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
输出:
- printf:0,dtoa:0
- printf:42,dtoa:42
- printf:1234567.8901234,dtoa:1234567.89012344996444
- printf:1.8e-14,dtoa:1.79999999999999e-14
- printf:555555.55555556,dtoa:555555.55555555550381
- printf:-8.8888888888889e + 14,dtoa:-8.88888888888888e + 14
- printf:1.1111111111111e + 23,dtoa:1.11111111111111e + 23
- 使用
log
-function查找数字的大小m
。 如果幅度为负,则打印"0."
和适量的零。 - 连续除以
10^m
并将结果转换为int以获得十进制数字。m--
下一个数字。 - 如果您来到
m==0
,请不要忘记打印小数点"."
。 - 几位后断开。 如果你打破时
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); }
你有两个主要问题:
- 将位表示转换为字符串
- 分配足够的内存来存储字符。
解决第二部分的最简单方法是为每个可能的答案分配足够大的块。 从那开始。 稍后你会想要更聪明,但在你解决问题的数字部分之前不要打扰。
您有两组工具可用于处理问题的数字部分:直接位操作(屏蔽,移位等)和算术运算(*,+,/,加上可能的数学函数链接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基本思路是将整个部分和小数部分分开,然后在它们之间连接两个小数。