Подтвердить что ты не робот

Как "cout" правильное количество десятичных знаков двойного значения?

Мне нужна помощь в сохранении точности double. Если я присваиваю литерал двойному, фактическое значение было усечено.

int main() {
    double x = 7.40200133400;
    std::cout << x << "\n";
}

Для приведенного выше фрагмента кода вывод был 7.402
Есть ли способ предотвратить этот тип усечения? Или есть способ точно рассчитать, сколько плавающих точек для double? Например, number_of_decimal(x) даст 11, так как вход неизвестен во время выполнения, поэтому я не могу использовать setprecision().


Думаю, я должен изменить свой вопрос: Как преобразовать double в строку без усечения плавающих точек. т.е.

#include <iostream>
#include <string>
#include <sstream>

template<typename T>
std::string type_to_string( T data ) {
    std::ostringstream o;
    o << data;
    return o.str();
}

int main() {
    double x = 7.40200;
    std::cout << type_to_string( x ) << "\n";
}

Ожидаемый результат должен быть 7.40200, но фактический результат был 7.402. Итак, как я могу обойти эту проблему? Любая идея?

4b9b3361

Ответ 1

В связи с тем, что float и double внутренне хранятся в двоичном формате, буквальный 7.40200133400 фактически обозначает номер 7.40200133400000037653398976544849574565887451171875

... так насколько точно вы хотите?: -)

#include <iomanip>    
int main()
{
    double x = 7.40200133400;
    std::cout << std::setprecision(51) << x << "\n";
}

И да, эта программа действительно печатает 7.40200133400000037653398976544849574565887451171875!

Ответ 2

Вы должны использовать setiosflags(ios::fixed) и setprecision(x).

Например, cout << setiosflags(ios::fixed) << setprecision(4) << myNumber << endl;

Кроме того, не забудьте #include <iomanip.h>.

Ответ 3

std::cout << std::setprecision(8) << x;

Обратите внимание, что setprecision является постоянным, и все последующие поплавки, которые вы печатаете, будут напечатаны с такой точностью, пока вы не измените его на другое значение. Если это проблема и вы хотите обойти это, вы можете использовать объект прокси stringstream:

std::stringstream s;
s << std::setprecision(8) << x;
std::cout << s.str();

Для получения дополнительной информации о форматировании iostream ознакомьтесь с блоками ввода/вывода в cppreference.

Ответ 4

Решение с использованием Boost.Format:

#include <boost/format.hpp>
#include <iostream>

int main() {
    double x = 7.40200133400;
    std::cout << boost::format("%1$.16f") % x << "\n";
}

Это выводит 7.4020013340000004.

Надеюсь, это поможет!

Ответ 5

Единственный ответ на этот вопрос, который я придумал, заключается в том, что нет никакого способа сделать это (как в вычислении десятичных знаков) правильно! Основной причиной этого является то, что представление числа может быть не таким, как вы ожидаете, например, 128.82, кажется достаточно безобидным, однако фактическое его представление составляет 128.8199999999... как вы вычисляете количество десятичных знаков там?/p >

Ответ 6

Реагирование на ответ-редактирование: нет способа сделать это. Как только вы присвоите значение double, любые конечные нули будут потеряны (для компилятора/компьютера 0,402, 0,4020 и 0,40200 - ИДЕННЫЙ НОМЕР). Единственный способ сохранить конечные нули, как вы указали, - это сохранить значения в виде строк (или сделать обман, где вы отслеживаете количество цифр, которое вам нужно, и отформатировать его до такой длины).

Ответ 7

У парных разрядов нет десятичных знаков. У них есть бинарные места. А бинарные места и десятичные знаки несоизмеримы (потому что log2(10) не является целым числом).

То, о чем вы просите, не существует.

Ответ 8

Пусть s сделает аналогичный запрос: после инициализации целого числа с 001 вы хотели бы напечатать его с ведущими нулями. Эта информация о форматировании просто не сохранялась.

Для дальнейшего понимания хранилища с плавающей запятой с двойной точностью просмотрите стандарт IEEE 754.

Ответ 9

Вторая часть вопроса о том, как сохранить конечные нули в значении с плавающей запятой от спецификации значения до результата вывода, не имеет решения. Значение с плавающей запятой не сохраняет первоначальную спецификацию значения. Кажется, что эта бессмысленная часть была добавлена модератором SO.

Что касается первой и исходной части вопроса, которую я интерпретирую как то, как представить все значащие цифры 7.40200133400, т. 7.40200133400 С выводом наподобие 7.402001334, вы можете просто удалить конечные нули из результата вывода, который включает только достоверные цифры в double значении:

#include <assert.h>         // assert
#include <limits>           // std::(numeric_limits)
#include <string>           // std::(string)
#include <sstream>          // std::(ostringstream)

namespace my{
    // Visual C++2017 doesn't support comma-separated list for 'using':
    using std::fixed; using std::numeric_limits; using std::string;
    using std::ostringstream;

    auto max_fractional_digits_for_positive( double value )
        -> int
    {
        int result = numeric_limits<double>::digits10 - 1;
        while( value < 1 ) { ++result; value *= 10; }
        return result;
    }

    auto string_from_positive( double const value )
        -> string
    {
        ostringstream stream;
        stream << fixed;
        stream.precision( max_fractional_digits_for_positive( value ) );
        stream << value;
        string result = stream.str();
        while( result.back() == '0' )
        {
            result.resize( result.size() - 1 );
        }
        return result;
    }

    auto string_from( double const value )
        -> string
    {
        return (0?""
            : value == 0?   "0"
            : value < 0?    "-" + string_from_positive( -value )
            :               string_from_positive( value )
            );
    }
}

#include<iostream>
auto main()
    -> int
{
    using std::cout;
    cout << my::string_from( 7.40200133400 ) << "\n";
    cout << my::string_from( 0.00000000000740200133400 ) << "\n";
    cout << my::string_from( 128.82 ) << "\n";
}

Выход:

7.402001334
0.000000000007402001334
128.81999999999999

Вы можете добавить логику для округления, чтобы избежать длинных последовательностей 9, как в последнем результате.