C++ 将浮点数转换为字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2302969/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert a float to a string
提问by SIVA
How can I convert a floating point integer to a string in C/C++ without the library function sprintf
?
如何在没有库函数的情况下将浮点整数转换为 C/C++ 中的字符串sprintf
?
I'm looking for a function, e.g. char *ftoa(float num)
that converts num
to a string and returns it.
我正在寻找一个函数,例如char *ftoa(float num)
转换num
为字符串并返回它的函数。
ftoa(3.1415)
should return "3.1415"
.
ftoa(3.1415)
应该返回"3.1415"
。
采纳答案by Sophy Pal
When you're dealing with fp numbers, it can get very compex but the algorithm is simplistic and similar to edgar holleis's answer; kudos! Its complex because when you're dealing with floating point numbers, the calculations will be a little off depending on the precision you've chosen. That's why its not good programming practice to compare a float to a zero.
当您处理 fp 数字时,它会变得非常复杂,但算法很简单,类似于 edgar holleis 的答案;荣誉!它很复杂,因为当您处理浮点数时,根据您选择的精度,计算会有些偏差。这就是为什么将浮点数与零进行比较不是一个好的编程习惯。
But there is an answer and this is my attempt at implementing it. Here I've used a tolerance value so you don't end up calculating too many decimal places resulting in an infinite loop. I'm sure there might be better solutions out there but this should help give you a good understanding of how to do it.
但是有一个答案,这是我实现它的尝试。在这里,我使用了一个容差值,因此您最终不会计算太多的小数位,从而导致无限循环。我相信那里可能有更好的解决方案,但这应该有助于让您很好地了解如何去做。
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) = '/*
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) = ' /*
* 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;
}
';
}
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]));
}
}
';
回答by androider
Based on Sophy Pal's answer, this is a slightly more complete solution that takes into account the number zero, NaN, infinite, negative numbers, and scientific notation. Albeit sprintf still provides a more accurate string representation.
根据 Sophy Pal 的回答,这是一个稍微更完整的解决方案,它考虑了数字零、NaN、无穷大、负数和科学记数法。尽管 sprintf 仍然提供更准确的字符串表示。
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] = '#include <math.h>
#include <string.h>
/* 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;
}
';
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, e.g. 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, e.g. 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='##代码##';
strreverse(str, wstr-1);
return (size_t)(wstr - str);
}
Outputs:
输出:
- 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
- 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
回答by edgar.holleis
- Use the
log
-function to find out the magnitudem
of your number. If the magnitude is negative print"0."
and an appropriate amount of zeros. - Consecutively divide by
10^m
and cast the result to int to get the decimal digits.m--
for the next digit. - If you came accross
m==0
, don't forget to print the decimal point"."
. - Break off after a couple of digits. If
m>0
when you break of, don't forget to print"E"
anditoa(m)
.
- 使用
log
- 函数找出m
您的数字的大小。如果大小为负打印"0."
和适量的零。 - 连续除以
10^m
并将结果转换为 int 以获得十进制数字。m--
对于下一个数字。 - 如果你遇到了
m==0
,不要忘记打印小数点"."
。 - 几个数字后中断。如果
m>0
你休息时,不要忘记打印"E"
和itoa(m)
。
Instead of the log
-function you can also directly extract the exponent by bitshifting and correcting for the exponent's offset (see IEEE 754). Java has a double-to-bits function to get at the binary representation.
log
您还可以通过位移和校正指数偏移量来直接提取指数,而不是- 函数(请参阅 IEEE 754)。Java 有一个 double-to-bits 函数来获取二进制表示。
回答by Rahul Naik
回答by shapkin
Just found realy good implementation at https://code.google.com/p/stringencoders/
刚刚在https://code.google.com/p/stringencoders/找到了非常好的实现
##代码##回答by dmckee --- ex-moderator kitten
You have two major problems:
你有两个主要问题:
- Converting the bit representation into a string of characters
- Allocating enough memory to store the characters.
- 将位表示转换为字符串
- 分配足够的内存来存储字符。
The simplest way to solve the second part is to allocate a big enough chunk for every possible answer. Start with that. Later you'll want to be more clever, but don't bother until you've solved the numeric part of the problem.
解决第二部分的最简单方法是为每个可能的答案分配足够大的块。从那开始。稍后你会想要变得更聪明,但在你解决问题的数字部分之前不要打扰。
You have two sets of tools available for dealing with the numeric part of the problem: direct bit manipulation (masking, shifting, etc) and arithmetic operation (*,+,/, plus possibly math functions link log()
).
您有两套工具可用于处理问题的数字部分:直接位操作(屏蔽、移位等)和算术运算(*、+、/,以及可能的数学函数链接log()
)。
In principle you could tackle the bitwise representation directly, but that would not be portable in the event that floating point representation formats change in the future. The method suggested by edgar.holleisshould be portable.
原则上,您可以直接处理按位表示,但如果将来浮点表示格式发生变化,这将无法移植。edgar.holleis 建议的方法应该是可移植的。
回答by MD XF
Here's what I came up with; it's very efficient and very simple. It assumes your system has itoa
.
这是我想出的;它非常有效且非常简单。它假设您的系统具有itoa
.
回答by psych0der
This gist might help : https://gist.github.com/psych0der/6319244Basic idea is split the whole part and decimal part and then concatenate both of them with decimal in between.
这个要点可能会有所帮助:https: //gist.github.com/psych0der/6319244基本思想是拆分整个部分和小数部分,然后将它们与小数连接起来。