1

How can I parse a const char* from a double or long?

Mainly because my code is a lot faster when I use a const char*, so i decided to create a small base string class. But my code to parse a double has some bugs.

My code only works partially. Some help would be very appreciated. I am using macos, g++ & c++17.

Code:

#include <iostream>
class bstring {
public:
    const char* characters;
    bstring(const char* c = "") { characters = c; }
    static bstring parse(const double number, int precision = 100) {

        // Convert.
        int     decimal,   sign;
        char    *buffer;
        buffer = ecvt(number, precision, &decimal, &sign);
        int n = strlen(buffer);

        // Add decimal.
        char before[decimal];
        strncpy(before, 0 + buffer, decimal);
        char after[n - decimal - 1];
        strncpy(after, decimal + buffer, n - decimal - 1);

        // Remove zero padding.
        int removed = 0;
        while (true) {
            size_t n = sizeof(after) - removed;
            size_t index_to_remove = n - 1;
            if (after[index_to_remove] == '0') {
                for (size_t i = index_to_remove; i < n - 1; ++i) { 
                    after[i] = after[i + 1]; 
                }
                removed += 1;
            } else { break; }
        }
        bool is_zero = removed == sizeof(after);
        int after_size = sizeof(after)-removed;
        char* nafter = (char*)malloc(sizeof(char) * after_size);

        // Concat.
        char* new__{ new char[strlen(before) + 1 + after_size] };
        new__ = strcpy(new__, before);
        new__ = strcat(new__, "."); 
        if (is_zero)  {
            char a[] = "0";
            new__ = strcat(new__, a);   
        } else {
            new__ = strcat(new__, after);   
        }

        // Assign.
        bstring s = new__;
        delete[] new__; new__ = NULL;
        return s;

        // 
    }
};
std::ostream& operator <<(std::ostream &s, bstring x) { return s << x.characters; }
int main() {
    std::cout << "Should be " << "-1234.39950" << ": " << bstring::parse(-1234.39950) << std::endl;
    std::cout << "Should be " << "-1.0" << ": " << bstring::parse(-1.0) << std::endl;
    std::cout << "Should be " <<"0.0" << ": " << bstring::parse(0.0) << std::endl;
    std::cout << "Should be " <<"0.3897495" << ": " << bstring::parse(0.3897495) << std::endl;
    std::cout << "Should be " <<"1.0" << ": " << bstring::parse(1.0) << std::endl;
    std::cout << "Should be " <<"100.00" << ": " << bstring::parse(1000.0) << std::endl;
    std::cout << "Should be " <<"10000.000" << ": " << bstring::parse(1000000.0) << std::endl;
    std::cout << "Should be " <<"1000000.0000" << ": " << bstring::parse(1000000000.0) << std::endl;
    std::cout << "Should be " <<"1000000000.0000" << ": " << bstring::parse(1000000000000.0) << std::endl;
    std::cout << "Should be " <<"1000000000000.0000" << ": " << bstring::parse(1000000000000000.0) << std::endl;
}

Edit:

Is this piece of code okay? Or am I doing something wrong by not deleting it / By where I assign the new__ to.

// Concat.
    bstring concat(const char* c) {
        int n = ::strlen(characters) + ::strlen(c);
        if (n == 0) { return bstring(); }
        if (::strlen(c) == 0) { return bstring(characters); }
        char* new__{ new char[n + 1] };
        new__ = strcpy(new__, characters);
        new__ = strcat(new__, c);   
        // const char* n = new__;
        // delete[] new__; new__ = NULL;
        bstring s = new__;
        return s;
    }
  • 1
    Without the `const` requirement there is [`std::to_chars`](https://en.cppreference.com/w/cpp/utility/to_chars). I'd start there and `const` the sucker later. – user4581301 Jul 02 '22 at 23:19
  • 1
    Side note: [Never put two underscores in a row](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) as in `new__`. It's one of those weird rules that rarely bites you, but when it does... Well, you ever see Jaws? – user4581301 Jul 02 '22 at 23:21
  • Thanks a lot! Why not the `__`? I have this in all of my classes... -_- – user3134709 Jul 02 '22 at 23:22
  • Just added a link explaining the details. – user4581301 Jul 02 '22 at 23:23
  • 1
    `Why not the __?` From the link: `Each name that contains a double underscore (__) .... is reserved `. `I have this in all of my classes.` Install `clang-tidy` and check for [reserved identifiers](https://releases.llvm.org/13.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/bugprone-reserved-identifier.html). | I do not understand, why not just `std::cout << -1234.39950`? `my code is a lot faster when I use` Why not `snprintf(...., "%f", -1234.39950)`? or `strfromd`? `code only works partiall` Ok, so what does not work? Could you be more specific - what "help" do you need? – KamilCuk Jul 02 '22 at 23:26
  • TL:DR version is it's one of the things the compiler and Standard Library uses for the weird-or-implementation-specific stuff lurking in the underbelly of the implementation. The language restricts the use so that you'll be less likely to have a naming collision with an undocumented library detail. – user4581301 Jul 02 '22 at 23:33
  • I would bet even money that your code is faster because you are doing it wrong. The standard libraries double to string conversion is highly optimized so I would be surprised if you can beat it and still handle all the corner cases right. It's all the little special cases that slow down the library. – Goswin von Brederlow Jul 03 '22 at 00:17
  • 3
    The reason for prohibiting names with two consecutive underscores is because back in the olden days, cfront, which translated C++ code into C code, generated names like that for member functions: class-name, two underscores, function-name. – Pete Becker Jul 03 '22 at 01:28

0 Answers0