C++, научная нотация, номер формата

можно ли форматировать строку в научной нотации следующими способами:

  • установить фиксированные места в экспоненте: 1
  • установить фиксированные десятичные знаки в mantisa: 0

    double number = 123456.789
    

таким образом, число должно быть сформировано

  1e+5

Я не могу установить 0 десятичных знаков для mantisa:

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

результат:

1.234568e+005

4 ответов


Я не уверен, какой компилятор C++ вы используете, который дает вам 3 цифры для экспоненты-стандарты C и c++ требуют минимум 2 цифр для этого, и это то, что делает g++. Невозможно получить только одну цифру, используя стандартные функции ввода-вывода C или C++, поэтому вам придется свернуть собственное решение. Поскольку преобразование с плавающей запятой в строку является очень сложная проблема [PDF], я настоятельно рекомендую не делать этого и постобработать результат вместо.

вот один из способов сделать это:

// 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] = '';
    }
}

Я не могу понять, как получить одну цифру в поле экспоненты, но следующее соответствует всем вашим другим требованиям.

#include <iostream>
#include <iomanip>

int main()
{
  const double number = 123456.789;

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

выход:

1e+05

EDIT:
Сделал быстрый поиск по стандарту (N3291) и не смог найти ничего, что говорило бы о количестве цифр в поле экспоненты при использовании научной нотации. Это может быть определено реализацией.


вы можете фактически форматировать все, что у вас есть строка.. больше кода c++ будет выглядеть так:

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;
}

выход:

1e+5

вы можете установить размер экспоненты в expSize, и это работает для произвольного большого показателя.

надеюсь, что это помогает!


вот потоковое решение:

#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;
}

пример использования:

int main()
{
    double e = 1.234;

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

    return 0;
}

выход: 1.2e0 1.234e0