5

I'm trying to print a max of 4 digits after the decimal point in C++ (Using streams). So if the number does not need 4 digits after the decimal I want it to use only the number of decimals it actually needs.

Examples:

1.12345    -> 1.1234
1.0        -> 1
1.12       -> 1.12
1.12345789 -> 1.1234
123.123    -> 123.123
123.123456 -> 123.1234

I tried std::setprecision(4) but that sets the number of significant digits and fails in the test case:

123.123456 gives 123.1

I also tried giving std::fixed along with std::setprecision(4) but that gives a fixed number of digits after decimal even if not needed:

1.0 gives 1.0000

It seems like std::defaultfloat is the one I need and not fixed nor exponential. But it does not seem to be printing the number of digits after the decimal appropriately and only has an option for significant digits.

AbdealiLoKo
  • 3,261
  • 2
  • 20
  • 36
  • 4
    This would require custom code, working on either a BCD or string representation of the value to be printed. Bear in mind that, because the underlying representation of the floating point number is binary, there is not necessarily an exact binary representation of (say) 123.123. If you showed enough decimal places, something like `double x = 123.123; cout << x` could output something like `123.1230000001`. Thus, I'd suggest writing a function that converts the double to a string with four decimal places and then deletes any trailing zeroes after the decimal place before returning the string. – Simon Dec 19 '16 at 19:58
  • Your examples do not match your problem description. Looking at those examples it's more like you want no more than 4 decimals, without any trailing zeros. That's certainly not the same as exactly 4 decimals. Besides, as pointed out by Simon, given the nature of the representation of IEEE floating point values, you'll have to produce a better specification for what the phrase *"needs a digit"* is supposed to mean. – IInspectable Dec 19 '16 at 20:08
  • 2
    [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) is probably required reading. – IInspectable Dec 19 '16 at 20:11
  • So you want it to print exactly four digits, except when it doesn't print four digits? – Lightness Races in Orbit Dec 19 '16 at 20:11
  • 1
    I would phrase it as "print at most 4 digits after the decimal point, with trailing zeros omitted". – Martin Bonner supports Monica Dec 19 '16 at 20:17
  • Yes, that sounds like a better problem statement. – Lightness Races in Orbit Dec 19 '16 at 20:24
  • Thanks, clarified the question better. And I do understand that the Binary representation would not be exact. And this would be a truncated value or rounded off value upto 4th decimal if the representation requires more than 4th decimal – AbdealiLoKo Dec 19 '16 at 20:34

2 Answers2

4

We can do this using a std::stringstream and a std::string. We pass the double to the stream formatting it like we would if we are sending it to cout. Then we examine the string we get from the stream to see if there are trailing zeros. If there are we get rid of them. Once we do that we check to see if we are left with just a decimal point, if we are then we get rid of that as well. You could use something like this:

int main()
{
    double values[] = { 1.12345, 1.0, 1.12, 1.12345789, 123.123, 123.123456, 123456789, 123.001 };
    std::vector<std::string> converted;
    for (auto e : values)
    {
        std::stringstream ss;
        ss << std::fixed << std::setprecision(4) << e;
        std::string value(ss.str());
        if (value.find(".") != std::string::npos)
        {
            // erase trailing zeros
            while (value.back() == '0')
                value.erase(value.end() - 1);
            // if we are left with a . at the end then get rid of it
            if (value.back() == '.')
                value.erase(value.end() - 1);
            converted.push_back(value);
        }
        else
            converted.push_back(value);
    }
    for (const auto& e : converted)
        std::cout << e << "\n";
}

Which when made into a running example gives

1.1235
1
1.12
1.1235
123.123
123.1235
123456789
123.001
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
1

Using the answer from here along with custom logic to remove the zeros and the point:

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>

std::string remove_zeros(std::string numberstring)
{
    auto it = numberstring.end() - 1;
    while(*it == '0') {
        numberstring.erase(it);
        it = numberstring.end() - 1;
    }
    if(*it == '.') numberstring.erase(it);
    return numberstring;
}

std::string convert(float number)
{
    std::stringstream ss{};
    ss << std::setprecision(4) << std::fixed << std::showpoint << number;
    std::string numberstring{ss.str()};
    return remove_zeros(numberstring);
}

int main()
{
    const float values[]{1.12345, 1.0, 1.12, 1.12345789, 147323.123, 123.123456};
    for(auto i : values)
        std::cout << convert(i) << '\n';
}

produces:

1.1235
1
1.12
1.1235
147323.125
123.1235
Community
  • 1
  • 1
wally
  • 10,717
  • 5
  • 39
  • 72