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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:57:49  来源:igfitidea点击:

Convert a float to a string

c++cfloating-point

提问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 numto 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:

输出:

  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. 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

回答by edgar.holleis

  1. Use the log-function to find out the magnitude mof your number. If the magnitude is negative print "0."and an appropriate amount of zeros.
  2. Consecutively divide by 10^mand cast the result to int to get the decimal digits. m--for the next digit.
  3. If you came accross m==0, don't forget to print the decimal point ".".
  4. Break off after a couple of digits. If m>0when you break of, don't forget to print "E"and itoa(m).
  1. 使用log- 函数找出m您的数字的大小。如果大小为负打印"0."和适量的零。
  2. 连续除以10^m并将结果转换为 int 以获得十进制数字。m--对于下一个数字。
  3. 如果你遇到了m==0,不要忘记打印小数点"."
  4. 几个数字后中断。如果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:

你有两个主要问题:

  1. Converting the bit representation into a string of characters
  2. Allocating enough memory to store the characters.
  1. 将位表示转换为字符串
  2. 分配足够的内存来存储字符。

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.

##代码##

Try it online!

在线试试吧!

回答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基本思想是拆分整个部分和小数部分,然后将它们与小数连接起来。