C++, scientific notation, format number
Asked Answered
S

5

12

Is it possible to format string in scientific notation in the following ways:

  • set fixed places in exponent: 1

  • set fixed decimal places in mantissa: 0

     double number = 123456.789
    

So the number should be formated

  1e+5

I am not able to set 0 decimal points for mantissa:

cout.precision(0);
cout << scientific << number;

result:

1.234568e+005
Superincumbent answered 20/8, 2011 at 16:7 Comment(2)
Of course it's possible. Do you want code or are you looking for a way to do it using standard string formatting libraries/tools?Samples
Some code could he helpful.. :-)Superincumbent
F
3

I'm not sure what C++ compiler you're using that's giving you 3 digits for the exponent—the C and C++ standards require a minimum of 2 digits for that, and that's what g++ does. There's no way to get only one digit using the standard C or C++ I/O functions, so you'll have to roll your own solution. Since doing a floating-point to string conversion is a very tricky problem [PDF], I'd strongly recommend not doing that and postprocessing the result instead.

Here's one way to do that:

// C version; you can rewrite this to use std::string in C++ if you want
void my_print_scientific(char *dest, size_t size, double value)
{
    // First print out using scientific notation with 0 mantissa digits
    snprintf(dest, size, "%.0e", value);

    // Find the exponent and skip the "e" and the sign
    char *exponent = strchr(dest, 'e') + 2;

    // If we have an exponent starting with 0, drop it
    if(exponent != NULL && exponent[0] == '0')
    {
        exponent[0] = exponent[1];
        exponent[1] = '\0';
    }
}
Fulcrum answered 20/8, 2011 at 17:11 Comment(0)
A
10

You can do it with C++20 std::format:

double number = 123456.789;
auto s = std::format("{:.0e}\n", number); // s == "1e+05"
s.erase(s.size() - 2, 1); // s == "1e+5"

Until std::format is widely available you can use the {fmt} library, std::format is based on:

#include <fmt/core.h>
    
double number = 123456.789;
auto s = fmt::format("{:.0e}\n", number); // s == "1e+05"
s.erase(s.size() - 2, 1); // s == "1e+5"

Disclaimer: I'm the author of {fmt} and C++20 std::format.

Attenuator answered 16/12, 2018 at 2:22 Comment(0)
D
8

I can't figure out how to get a single digit in the exponent field but the following matches all your other requirements.

#include <iostream>
#include <iomanip>

int main()
{
  const double number = 123456.789;

  std::cout << std::setprecision(0) << std::scientific << number << std::endl;
}

Output:

1e+05

EDIT:
Did a quick search through the standard (N3291) and couldn't find anything that talked about the number of digits in the exponent field when using scientific notation. This might be implementation defined.

Diwan answered 20/8, 2011 at 16:44 Comment(1)
Under VS2010 if gives me the same results as my code: 1.234568e+005Superincumbent
F
3

I'm not sure what C++ compiler you're using that's giving you 3 digits for the exponent—the C and C++ standards require a minimum of 2 digits for that, and that's what g++ does. There's no way to get only one digit using the standard C or C++ I/O functions, so you'll have to roll your own solution. Since doing a floating-point to string conversion is a very tricky problem [PDF], I'd strongly recommend not doing that and postprocessing the result instead.

Here's one way to do that:

// C version; you can rewrite this to use std::string in C++ if you want
void my_print_scientific(char *dest, size_t size, double value)
{
    // First print out using scientific notation with 0 mantissa digits
    snprintf(dest, size, "%.0e", value);

    // Find the exponent and skip the "e" and the sign
    char *exponent = strchr(dest, 'e') + 2;

    // If we have an exponent starting with 0, drop it
    if(exponent != NULL && exponent[0] == '0')
    {
        exponent[0] = exponent[1];
        exponent[1] = '\0';
    }
}
Fulcrum answered 20/8, 2011 at 17:11 Comment(0)
M
2

You can actualy format anything once you have a string.. more c++ code would look like:

const double number = 123456.789;
const int expSize = 1;
std::ostringstream oss;
std::string output;
oss << std::scientific << number;
unsigned int ePos = oss.str().find("e");
unsigned int dPos = oss.str().find(".");
if(ePos == 0){
    //no exponent
}
else if(dPos == 0){
    //not decimal
}
else{
    output = oss.str().substr(0, dPos) + oss.str().substr(ePos, 2);
    if(oss.str().size()-expSize > ePos+1)
        output += oss.str().substr(oss.str().size()-expSize, oss.str().size());
    else{
        //expSize too big (or bug -> e used but no exponent?)
    }
    std::cout << output;
}

Output:

1e+5

You can set exponent size in expSize and this works for arbitrary large exponent.

Hope it helps!

Makassar answered 20/8, 2011 at 17:45 Comment(0)
S
1

Here is a stream solution:

#include <iostream>
#include <iomanip>
using namespace std;

template<typename T>
struct scientificNumberType
{
    explicit scientificNumberType(T number, int decimalPlaces) : number(number), decimalPlaces(decimalPlaces) {}

    T number;
    int decimalPlaces;
};

template<typename T>
scientificNumberType<T> scientificNumber(T t, int decimalPlaces)
{
    return scientificNumberType<T>(t, decimalPlaces);
}

template<typename T>
std::ostream& operator<<(std::ostream& os, const scientificNumberType<T>& n)
{
    double numberDouble = n.number;

    int eToThe = 0;
    for(; numberDouble > 9; ++eToThe)
    {
        numberDouble /= 10;
    }

    // memorize old state
    std::ios oldState(nullptr);
    oldState.copyfmt(os);

    os << std::fixed << std::setprecision(n.decimalPlaces) << numberDouble << "e" << eToThe;

    // restore state
    os.copyfmt(oldState);

    return os;
}

Usage sample:

int main()
{
    double e = 1.234;

    cout << scientificNumber(e, 1) << " " << scientificNumber(e, 3);

    return 0;
}

Output: 1.2e0 1.234e0

Sterigma answered 11/11, 2016 at 14:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.