4

I am working on a code to convert a floating point number to it's equivalent string. For example if the number is : 2.3456 then the string should also be 2.3456 (without trailing zeros).

I searched on stackoverflow on these 2 links:

C++ convert floating point number to string

Convert Double/Float to string

but both these are slightly off topic as they tend to ask for representation in 1eX format or xE+0 format.

This is my attempt:

#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;
}

Link to ideone: http://ideone.com/Z8wBD7

I want to know how can I efficiently exploit the IEEE 754 floating point representation standard (using typecesting a char pointer or any other method) and acheive such a conversion , without using any predefined library function / scanning from a file.

Community
  • 1
  • 1
Dref D
  • 257
  • 2
  • 17
  • 2
    This is a Really Hard problem. Have you solved any similar, easier problems yet? The Dragon4 algorithm is one of the popular solutions, if you want to peek ahead. – Kerrek SB Aug 31 '13 at 10:06
  • And the naive solution is of course to multiply by 10 and convert to integer repeatedly, but it's *slow*. – Kerrek SB Aug 31 '13 at 10:08
  • yes, that is the naive way. Is there any wikipedia entry for dragon4 algorithm ? – Dref D Aug 31 '13 at 10:09
  • I'm sure you can bing that yourself...? – Kerrek SB Aug 31 '13 at 10:42
  • @ Kerrek SB , I didn't find any entry on wikipedia regarding the same! http://en.wikipedia.org/wiki/Dragon4_algorithm , where could i find more about it? – Dref D Aug 31 '13 at 11:23
  • Just wondering why you cannot use library calls. I suspect it'll be for some GPGPU core (CUDA or OpenCL) or for some ultra embedded device? – Csaba Toth Sep 03 '13 at 17:00
  • No, nothing of this sort, just for the fun of learning! – Dref D Sep 04 '13 at 16:22

2 Answers2

1

Here is my solution. For convenience, I use <string>, but no other libraries. The to_string(float x) / to_string(double x) functions return the number as string in the -1.23456789E-12 format with full precision.

The to_string(float x, const uint decimals) / to_string(double x, const uint decimals) functions return the number as string with the specified number of decimals: -12345.68 with 2 decimals for example. This reduces precision, but is more convenient for console outputs. Numbers here are limited to a magnitude of 1.844E19.

Rounding is handled correctly in both methods.

#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)));
}
ProjectPhysX
  • 4,535
  • 2
  • 14
  • 34
-1
#include<sstream>
#include<string>
#include<iostream>
using namespace std;

int main()
{
    float val = 2.3456;
    std::stringstream sstr;
    sstr<<val;
    string val_str;
    sstr >> val_str;
    cout << val_str;
}
cpp
  • 3,743
  • 3
  • 24
  • 38
  • `istream& operator>>(istream&, float&)` counts as a library function. :P I agree it's a rather messed-up requirement, but eh. – cHao Sep 03 '13 at 14:30
  • 1
    If he aims for an OpenCL/CUDE core, he cannot use STL. Unfortunately. – Csaba Toth Sep 03 '13 at 17:00