c++ - 无需任何库函数即可将 float 转换为字符串的高效方法

标签 c++ string ieee-754

我正在编写将 float 转换为等效字符串的代码。例如,如果数字是:2.3456,那么字符串也应该是 2.3456(没有尾随零)。

我在 stackoverflow 上搜索了这两个链接:

C++ convert floating point number to string

Convert Double/Float to string

但这两者都略微偏离主题,因为它们倾向于要求以 1eX 格式或 xE+0 格式表示。

这是我的尝试:

#include<cstdio>
#include<cstdlib>
#include<vector>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
   vector<char> V;
   string S;
   int i=0;
   float f=3.14156;
   float x=f*1e6;
   long long int y=(long long int)(x);
   while(y)
   {
        V.push_back(y%10+'0');
        y/=10;
   }
   reverse(V.begin(),V.end());
   for(i=0;i<V.size()-6;i++)
   {
        S.push_back(V[i]);
   }
   S.push_back('.');
   for(;i<V.size();i++)
        S.push_back(V[i]);

   i=S.size();
   while(i--)
   {
        if(S[i]=='0')
        S.erase(S.begin()+i);
        else break;
   }
cout<<S<<"\n";
//system("pause");
return 0;
}

ideone 链接:http://ideone.com/Z8wBD7

我想知道如何有效地利用 IEEE 754 浮点表示标准(使用类型化字符指针或任何其他方法)并实现这样的转换,而不使用任何预定义的库函数/从文件扫描。

最佳答案

这是我的解决方案。为方便起见,我使用 <string> ,但没​​有使用其他库。 to_string(float x)/to_string(double x) 函数返回数字作为 -1.23456789E-12 格式的字符串完全精确

to_string(float x, const uint decimals)/to_string(double x, const uint decimals) 函数返回具有指定小数位数的字符串形式的数字:例如 -12345.68 有 2 位小数。这会降低精度,但对于控制台输出来说更方便。这里的数字限制在 1.844E19 的数量级。

这两种方法都正确处理了舍入。

#define to_string to_string_old // use own to_string methods
#include <string>
#undef to_string // use own to_string methods
using namespace std;

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef int64_t slong;
typedef uint64_t ulong;
#define max_ulong 18446744073709551615ull

float pow(const float x, const uint n) {
    float r = 1.0f;
    for(uint i=0; i<n; i++) {
        r *= x;
    }
    return r;
}
double pow(const double x, const uint n) {
    double r = 1.0;
    for(uint i=0; i<n; i++) {
        r *= x;
    }
    return r;
}

void split_float(float x, uint& integral, uint& decimal, int& exponent) {
    if(x>=10.0f) { // convert to base 10
        if(x>=1E32f) { x *= 1E-32f; exponent += 32; }
        if(x>=1E16f) { x *= 1E-16f; exponent += 16; }
        if(x>= 1E8f) { x *=  1E-8f; exponent +=  8; }
        if(x>= 1E4f) { x *=  1E-4f; exponent +=  4; }
        if(x>= 1E2f) { x *=  1E-2f; exponent +=  2; }
        if(x>= 1E1f) { x *=  1E-1f; exponent +=  1; }
    }
    if(x>0.0f && x<=1.0f) {
        if(x<1E-31f) { x *=  1E32f; exponent -= 32; }
        if(x<1E-15f) { x *=  1E16f; exponent -= 16; }
        if(x< 1E-7f) { x *=   1E8f; exponent -=  8; }
        if(x< 1E-3f) { x *=   1E4f; exponent -=  4; }
        if(x< 1E-1f) { x *=   1E2f; exponent -=  2; }
        if(x<  1E0f) { x *=   1E1f; exponent -=  1; }
    }
    integral = (uint)x;
    float remainder = (x-integral)*1E8f; // 8 decimal digits
    decimal = (uint)remainder;
    if(remainder-(float)decimal>=0.5f) { // correct rounding of last decimal digit
        decimal++;
        if(decimal>=100000000u) { // decimal overflow
            decimal = 0;
            integral++;
            if(integral>=10u) { // decimal overflow causes integral overflow
                integral = 1;
                exponent++;
            }
        }
    }
}
void split_double(double x, uint& integral, ulong& decimal, int& exponent) {
    if(x>=10.0) { // convert to base 10
        if(x>=1E256) { x *= 1E-256; exponent += 256; }
        if(x>=1E128) { x *= 1E-128; exponent += 128; }
        if(x>= 1E64) { x *=  1E-64; exponent +=  64; }
        if(x>= 1E32) { x *=  1E-32; exponent +=  32; }
        if(x>= 1E16) { x *=  1E-16; exponent +=  16; }
        if(x>=  1E8) { x *=   1E-8; exponent +=   8; }
        if(x>=  1E4) { x *=   1E-4; exponent +=   4; }
        if(x>=  1E2) { x *=   1E-2; exponent +=   2; }
        if(x>=  1E1) { x *=   1E-1; exponent +=   1; }
    }
    if(x>0.0 && x<=1.0) {
        if(x<1E-255) { x *=  1E256; exponent -= 256; }
        if(x<1E-127) { x *=  1E128; exponent -= 128; }
        if(x< 1E-63) { x *=   1E64; exponent -=  64; }
        if(x< 1E-31) { x *=   1E32; exponent -=  32; }
        if(x< 1E-15) { x *=   1E16; exponent -=  16; }
        if(x<  1E-7) { x *=    1E8; exponent -=   8; }
        if(x<  1E-3) { x *=    1E4; exponent -=   4; }
        if(x<  1E-1) { x *=    1E2; exponent -=   2; }
        if(x<   1E0) { x *=    1E1; exponent -=   1; }
    }
    integral = (uint)x;
    double remainder = (x-integral)*1E16; // 16 decimal digits
    decimal = (ulong)remainder;
    if(remainder-(double)decimal>=0.5) { // correct rounding of last decimal digit
        decimal++;
        if(decimal>=10000000000000000ull) { // decimal overflow
            decimal = 0;
            integral++;
            if(integral>=10u) { // decimal overflow causes integral overflow
                integral = 1;
                exponent++;
            }
        }
    }
}
string decimal_to_string_float(uint x, int digits) {
    string r = "";
    while((digits--)>0) {
        r = (char)(x%10+48)+r;
        x /= 10;
    }
    return r;
}
string decimal_to_string_double(ulong x, int digits) {
    string r = "";
    while((digits--)>0) {
        r = (char)(x%10+48)+r;
        x /= 10;
    }
    return r;
}

string to_string(const string& s){
    return s;
}
string to_string(const char& c) {
    return string(1, c);
}
string to_string(ulong x) {
    string r = "";
    do {
        r = (char)(x%10+48)+r;
        x /= 10;
    } while(x);
    return r;
}
string to_string(slong x) {
    return x>=0 ? to_string((ulong)x) : "-"+to_string((ulong)(-x));
}
string to_string(uint x) {
    string r = "";
    do {
        r = (char)(x%10+48)+r;
        x /= 10;
    } while(x);
    return r;
}
string to_string(int x) {
    return x>=0 ? to_string((uint)x) : "-"+to_string((uint)(-x));
}
string to_string(float x) { // convert float to string with full precision (<string> to_string() prints only 6 decimals)
    string s = "";
    if(x<0.0f) { s += "-"; x = -x; }
    if(isnan(x)) return s+"NaN";
    if(isinf(x)) return s+"Inf";
    uint integral, decimal;
    int exponent = 0;
    split_float(x, integral, decimal, exponent);
    return s+to_string(integral)+"."+decimal_to_string_float(decimal, 8)+(exponent!=0?"E"+to_string(exponent):"");
}
string to_string(double x) { // convert double to string with full precision (<string> to_string() prints only 6 decimals)
    string s = "";
    if(x<0.0) { s += "-"; x = -x; }
    if(isnan(x)) return s+"NaN";
    if(isinf(x)) return s+"Inf";
    uint integral;
    ulong decimal;
    int exponent = 0;
    split_double(x, integral, decimal, exponent);
    return s+to_string(integral)+"."+decimal_to_string_double(decimal, 16)+(exponent!=0?"E"+to_string(exponent):"");
}
string to_string(float x, const uint decimals) { // convert float to string with specified number of decimals
    string s = "";
    if(x<0.0f) { s += "-"; x = -x; }
    if(isnan(x)) return s+"NaN";
    if(isinf(x)||x>(float)max_ulong) return s+"Inf";
    const float power = pow(10.0f, min(decimals, 8u));
    x += 0.5f/power; // rounding
    const ulong integral = (ulong)x;
    const uint decimal = (uint)((x-(float)integral)*power);
    return s+to_string(integral)+(decimals==0 ? "" : "."+decimal_to_string_float(decimal, min((int)decimals, 8)));
}
string to_string(double x, const uint decimals) { // convert float to string with specified number of decimals
    string s = "";
    if(x<0.0) { s += "-"; x = -x; }
    if(isnan(x)) return s+"NaN";
    if(isinf(x)||x>(double)max_ulong) return s+"Inf";
    const double power = pow(10.0, min(decimals, 16u));
    x += 0.5/power; // rounding
    const ulong integral = (ulong)x;
    const ulong decimal = (ulong)((x-(double)integral)*power);
    return s+to_string(integral)+(decimals==0 ? "" : "."+decimal_to_string_double(decimal, min((int)decimals, 16)));
}

关于c++ - 无需任何库函数即可将 float 转换为字符串的高效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18546877/

相关文章:

c++ - 未使用的 asm() 在不受支持的架构上的行为

c++ - 输入验证以过滤掉字符、字符串和一系列整数

c++ - 如何摆脱 C++ 中的枚举不匹配错误?

string - Lua中如何将字符编码转换为字符串字符?

matlab - matlab中32位十六进制到32位浮点(IEEE 754)的转换

c++ - 返回非静态本地对象时,选择复制构造函数而不是 move 构造函数

python - 如何确保用户输入的日期格式正确

c - AIX 相当于 ieee754.h

c - IEEE 754 : is v *= -1 always guaranteed to be the same as v = -v?

java - 什么时候创建/销毁常量字符串?