Search code examples
c++floating-pointscientific-notation

How to convert decimal scientific notation float/double to normal string


I know there are many questions solving this problem when the number is not very large, but when it comes to large numbers (while still in range of float/double), the output will lose precision :

#include <stdio.h>
#include <iostream>
#include <sstream>

int main() {
    double d = -1e307, d2 = 1e307;
    std::cout << std::fixed << d << std::endl;

    printf("d2 is %lf\n", d2);
    return 0;
}

This code will get output like this:

-9999999999999999860310597602564577717002641838126363875249660735883565852672743849064846414228960666786379280392654615393353172850252103336275952370615397010730691664689375178569039851073146339641623266071126720011020169553304018596457812688561947201171488461172921822139066929851282122002676667750021070848.000000

d2 is 9999999999999999860310597602564577717002641838126363875249660735883565852672743849064846414228960666786379280392654615393353172850252103336275952370615397010730691664689375178569039851073146339641623266071126720011020169553304018596457812688561947201171488461172921822139066929851282122002676667750021070848.000000

But this is obviously not correct! How to make real conversion?!


Solution

  • Actually I handled this my self, here is my code, the function translates arbitrary float string to a normal one.

    This function inspired by this question.

    // DW: remove e
    std::string trim_real(std::string str) {
        // DW: all to lowercase
        std::transform(str.begin(), str.end(), str.begin(), ::tolower);
    
        // DW: find "e" and "."
        size_t pe = str.find_first_of("e");
        std::string se = str.substr(pe + 1, str.size() - (pe + 1));
        int de = std::stoi(se);
    
        // DW: get string without "e"
        std::string sc = str.substr(0, pe);
        std::cout << "sc is " << sc << std::endl;
    
        // DW: remove uneccessary 0
        size_t pf0 = sc.find_first_not_of("0");
        size_t pl0 = sc.find_last_not_of("0");
        std::string sc_trim = sc.substr(pf0, pl0 - pf0 + 1);
        std::cout << "sc_trim is " << sc_trim << std::endl;
    
        // DW: move "." according to e
        size_t pp = sc_trim.find_first_of(".");
        if (pp == std::string::npos) {
            // DW: this means there is no "."
            pp = 0;
        }
        int pp_tobe = pp + de;
    
        sc_trim.erase(pp, 1);
        if (de > 0) {
            // DW: should move "." to the right
            if (pp_tobe < sc_trim.size()) {
                sc_trim.insert(pp_tobe, ".");
            } else {
                sc_trim += std::string(pp_tobe - sc_trim.size() + 1, '0');
                sc_trim += ".0";
            }
        } else {
            // DW: should move "." to the left
            if (pp_tobe >= 0) {
                sc_trim.insert(pp_tobe, ".");
            } else {
                sc_trim = "." + std::string(0 - pp_tobe, '0') + sc_trim;
            }
        }
    
        if (sc_trim == ".") {
            // DW: this means sc_trim is actually 0 now
            sc_trim = "0.0";
        }
        return sc_trim;
    }