12

We have a StreamBuffer class in which we haven't implemented std::fixed operations and I am trying to prevent number showing up in scientific notations. With my below code some numbers are getting shown in scientific notations. We want to avoid doing any allocations so that's why we implemented StreamBuffer class because of performance reason.

Below is the code:

T value = 0;

template<typename U> void process(U& buf, DataOption holder) const {
    if (holder == DataOption::TYPES) {
        switch (type_) {
        case teck::PROC_FLOAT:
            buf << "{\"float\":" << value << "}";
            break;
        case teck::PROC_DOUBLE:
            buf << "{\"double\":" << value << "}";
            break;
        default:
            buf << "{\"" << type_ << "\":" << value << "}";
        }
    }
}

And this is the way it is being called:

void HolderProcess::dump(std::ostream& os, DataOption holder) const 
{
    process<std::ostream>(os, holder);
}

void HolderProcess::dump(StreamBuffer& buffer, DataOption holder) const
{
    process<StreamBuffer>(buffer, holder);
}

I tried using like as shown below and I got an error by which I understood we cannot use std::fixed on my StreamBuffer class.

case teck::PROC_DOUBLE:
    buf << "{\"double\":" << std::fixed << value << "}";

What is the alternative to std::fixed I can use here which doesn't do any allocations at all. I was thinking of converting number to string and then apply std::fixed on it but that will do some allocations as well which I want to avoid that.

What is the best way to do this which is performance efficient and doesn't do any allocations? I have a below solution but it will do some allocations as it uses string. I can call below method from my above code.

template <typename T> string str(T number)
   {
       std::ostringstream ss;
       ss << std::fixed << number;
       return ss.str();
   }

Is there any other optimized and efficient way?

user1950349
  • 4,738
  • 19
  • 67
  • 119
  • What error did you get? – Alan Stokes Feb 07 '16 at 23:15
  • 1
    Does `StreamBuffer` inherit from `std::streambuf`? Because `fixed` is implemented at the formatted I/O level, well above `streambuf`. – Alan Stokes Feb 07 '16 at 23:18
  • It doesn't inherit from `streambuf` and I cannot change that class to do that either since it is being used from a long time and it will break other places where it is being used so trying to see some alternative and efficient way – user1950349 Feb 14 '16 at 18:17
  • You haven't really made clear what you want. What numbers are coming out in exponent form? If you put in 1.23e+20, do you really want a fixed number out? – Dov Feb 15 '16 at 03:34
  • Yes that's what I meant.. Sorry for the confusion.. Some numbers are in exponential form and I want to have a fixed number for those. In each of the above case block, value is coming in exponential form and I want that to be fixed. – user1950349 Feb 15 '16 at 04:01

3 Answers3

8

StreamBuffer class must inherit from std::ios_base (or some of it's derivatives such as std::ostream) for your expected behaviour. std::fixed can only work with derivative implementations of what is available as part of the STL.

Additionally, if you have access to an std::ios_base, you can also play around with std::ios_base::precision.

If you are stuck in a situation where you cannot update the class then the most commonly used and traditional way is by way of scaling floats. In the interest of reducing duplication please have a look at the already answered question here. For example, for a 3rd degree of precision, I'd replace all 'value' instances with:

// case teck::PROC_FLOAT:
static_cast<float>( static_cast<int>(value*1000) ) / 1000
// case techk::PROC_DOUBLE:
static_cast<double>( static_cast<long long>(value*1000) ) / 1000

Having better understood the questioner's requirements. I have realised that the above would not work with exponents. In order to get around this I propose doing the following:

case teck::PROC_FLOAT:
        std::stringstream ss;

        ss << std::fixed << value;
        buf << "{\"float\":" << ss.str() << "}";
        break;

This, however, will most certainly allocate more memory.


Community
  • 1
  • 1
siphr
  • 421
  • 4
  • 12
  • I don't think so our StreamBuffer class is inheriting from `std::ios_base` or `std::ostream`. I work mainly with Java so don't have that much idea with C++ but it is for sure doesn't inherit those. – user1950349 Feb 11 '16 at 22:03
  • So, there is your problem. If you just change StreamBuffer to inherit from either one of those you should be ok. – siphr Feb 12 '16 at 09:13
  • are you still around? I cannot change this class at all now. Since it is like this from a long time and it is working fine so I need to keep it like this and make scientific notation thing work without this looks like. Is there any other way I can use here or add some methods in StreamBuffer class which will do this? – user1950349 Feb 12 '16 at 17:09
  • I understand. I have added a third option to my answer, please have a look. – siphr Feb 14 '16 at 10:04
  • I tried using in normal main method by providing number in scientific notation but it doesn't got converted to number without scientific notation? If possible can you provide an example with my question so that I can understand how will this work? – user1950349 Feb 14 '16 at 16:55
  • Again, as requested I've provided you with a concrete example for your code, with the updated answer. Hope that is helpful. – siphr Feb 14 '16 at 17:07
  • Just to add a note, I tried with exponents but somehow it doesn't work with exponents. – user1950349 Feb 14 '16 at 18:07
  • Forgot to post that I have now updated the answer with a possible solution that works for exponents. – siphr Feb 16 '16 at 08:41
  • `std::stringstream` is expensive to allocate. If you can initialize it just once and re-use it (correctly clearing the string buffer each time) I recommend that. – caps Mar 23 '16 at 04:12
6

Just use snprintf:

#include <cstdio>
#include <limits>
#include <iostream>

int main() {
    double value = 0.1234567890123456789;
    const int Precision = std::numeric_limits<double>::digits10;
    const std::size_t StringBufferSize = Precision + 3; // sign, dot and terminating zero.
    char str[StringBufferSize];
    std::snprintf(str, StringBufferSize - 1, "%.*f", Precision, value);
    str[StringBufferSize - 1] = 0;
    // buf << "{\"double\":" << str << "}";
    std::cout << str << '\n';
}
  • Thanks for your suggestion. I just need to do this only for `double` case in my switch block? Or for `float`, `long`, `int` as well. In my question, I showed only two case block but I will have more like `long`, `int`, `float` and `double`. Also if I print just value, I don't see it is getting printed in form of scientific notation. – user1950349 Feb 11 '16 at 22:13
  • You might use this for any type that fits into a double, but also you might consider different formats for the different types (look at the documentation for the format string of the printf-family) –  Feb 12 '16 at 12:05
  • I tried this with exponents but it doesn't work somehow. Any idea what's wrong? For example try with this value `1.23e+006`. – user1950349 Feb 14 '16 at 17:21
0

It looks to me like maybe you should try CppFormat. There are some examples of how to use it for formatting here.

caps
  • 1,225
  • 14
  • 24