how to prevent numbers from showing up in scientific notations
Asked Answered
S

3

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?

Sinotibetan answered 7/2, 2016 at 19:40 Comment(5)
What error did you get?Detrital
Does StreamBuffer inherit from std::streambuf? Because fixed is implemented at the formatted I/O level, well above streambuf.Detrital
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 waySinotibetan
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?Ephemeron
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.Sinotibetan
A
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.


Ascription answered 11/2, 2016 at 16:40 Comment(9)
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.Sinotibetan
So, there is your problem. If you just change StreamBuffer to inherit from either one of those you should be ok.Ascription
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?Sinotibetan
I understand. I have added a third option to my answer, please have a look.Ascription
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?Sinotibetan
Again, as requested I've provided you with a concrete example for your code, with the updated answer. Hope that is helpful.Ascription
Just to add a note, I tried with exponents but somehow it doesn't work with exponents.Sinotibetan
Forgot to post that I have now updated the answer with a possible solution that works for exponents.Ascription
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.Separate
T
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';
}
Titanothere answered 11/2, 2016 at 18:31 Comment(3)
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.Sinotibetan
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)Titanothere
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.Sinotibetan
S
0

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

Separate answered 16/2, 2016 at 23:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.