Output float as three digits, or more to avoid exponent - c++

I'm trying to output a float as three digits, or more if necessary to avoid an exponent.
Some examples:
0.12 // 0.123 would be ok
1.23
12.3
123
1234
12345
The closest I've gotten is
std::cout << std::setprecision(3) << f << std::cout;
but this prints things like
21 // rather than 21.0
1.23e+03 // rather than 1234
Combining std::setprecision with std::fixed means I always get the same number of post-decimal digits, which is not what I want.
Using std::setw, 123.456 would still print as 123.456 rather than 123.
Any suggestions?

As far as I can tell, the easiest way around this is to roll a function to catch it. I threw this together and it seems to work. I'm not sure if you wanted large numbers to only have 3 significant digits or if they should keep all sig figs to the left of the decimal place, but it wouldn't be hard to make that modification:
void printDigits(float value, int numDigits = 3)
{
int log10ofValue = static_cast<int>(std::log10(std::abs(value)));
if(log10ofValue >= 0) //positive log means >= 1
{
++log10ofValue; //add 1 because we're culling to the left of the decimal now
//The difference between numDigits and the log10 will let us transition across the decimal
// in cases like 12.345 or 1.23456 but cap it at 0 for ones greater than 10 ^ numDigits
std::cout << std::setprecision(std::max(numDigits - log10ofValue, 0));
}
else
{
//We know log10ofValue is <= 0, so set the precision to numDigits + the abs of that value
std::cout << std::setprecision(numDigits + std::abs(log10ofValue));
}
//This is a floating point truncate -- multiply up into integer space, use floor, then divide back down
float truncated = std::floor(value * std::pow(10.0, numDigits - log10ofValue)) / std::pow(10.0, numDigits - log10ofValue);
std::cout << std::fixed << truncated << std::endl;
}
Test:
int main(void)
{
printDigits(0.0000000012345);
printDigits(12345);
printDigits(1.234);
printDigits(12.345678);
printDigits(0.00012345);
printDigits(123456789);
return 0;
}
Output:
0.00000000123
12300
1.23
12.3
0.000123
123000000

Here's the solution I came up with. Ugly, but I believe it works.
if(f>=100) {
std::cout << std::fixed << std::setprecision(0) << f << std::endl;
std::cout.unsetf(std::ios_base::floatfield);
} else {
std::cout << std::showpoint << std::setprecision(3) << f << std::noshowpoint << std::endl;
}
If someone knows how to simplify this, please let me know!

Related

Double precision issues when converting it to a large integer

Precision is the number of digits in a number. Scale is the number of
digits to the right of the decimal point in a number. For example, the
number 123.45 has a precision of 5 and a scale of 2.
I need to convert a double with a maximum scale of 7(i.e. it may have 7 digits after the decimal point) to a __int128. However, given a number, I don't know in advance, the actual scale the number has.
#include <iostream>
#include "json.hpp"
using json = nlohmann::json;
#include <string>
static std::ostream& operator<<(std::ostream& o, const __int128& x) {
if (x == std::numeric_limits<__int128>::min()) return o << "-170141183460469231731687303715884105728";
if (x < 0) return o << "-" << -x;
if (x < 10) return o << (char)(x + '0');
return o << x / 10 << (char)(x % 10 + '0');
}
int main()
{
std::string str = R"({"time": [0.143]})";
std::cout << "input: " << str << std::endl;
json j = json::parse(str);
std::cout << "output: " << j.dump(4) << std::endl;
double d = j["time"][0].get<double>();
__int128_t d_128_bad = d * 10000000;
__int128_t d_128_good = __int128(d * 1000) * 10000;
std::cout << std::setprecision(16) << std::defaultfloat << d << std::endl;
std::cout << "d_128_bad: " << d_128_bad << std::endl;
std::cout << "d_128_good: " << d_128_good << std::endl;
}
Output:
input: {"time": [0.143]}
output: {
"time": [
0.143
]
}
0.143
d_128_bad: 1429999
d_128_good: 1430000
As you can see, the converted double is not the expected 1430000 instead it is 1429999. I know the reason is that a float point number can not be represented exactly. The problem can be solved if I know the number of digit after the decimal point.
For example,
I can instead use __int128_t(d * 1000) * 10000. However, I don't know the scale of a given number which might have a maximum of scale 7.
Question> Is there a possible solution for this? Also, I need to do this conversion very fast.
I'm not familiar with this library, but it does appear to have a mechanism to get a json object's string representation (dump()). I would suggest you parse that into your value rather than going through the double intermediate representation, as in that case you will know the scale of the value as it was written.

Truncate a floating-point number to the leading N decimal digits

Which is the most optimal way to get the n leftmost non-zero digits of a floating point number (number >= 0.0).
For example,
if n = 1:
0.014568 -> 0.01
0.246456 -> 0.2
if n = 2:
0.014568 -> 0.014
0.246456 -> 0.24
After #schil227 comment:
Currently I am doing multiplications and divisions (by 10) as necessary in order to have the n digits at the decimal number field.
Code could use sprintf(buf, "%e",...) to do most of the heavy lifting.
There are so many corner cases that other direct code may fail, sprintf() is likely to be, at least, as good solid reference solution.
This code prints the double to DBL_DECIMAL_DIG places to insure there is no rounding in digits that would make a difference. Then it zeros out various digits depending on n.
See #Mark Dickinson comment for reasons to use a greater value than DBL_DECIMAL_DIG. Perhaps on the order of DBL_DECIMAL_DIG*2. As mentioned above, there are many corner cases.
#include <float.h>
#include <math.h>
#include <stdio.h>
double foo(double x, int n) {
if (!isfinite(x)) {
return x;
}
printf("%g\n", x);
char buf[DBL_DECIMAL_DIG + 11];
sprintf(buf, "%+.*e", DBL_DECIMAL_DIG, x);
//puts(buf);
assert(n >= 1 && n <= DBL_DECIMAL_DIG + 1);
memset(buf + 2 + n, '0', DBL_DECIMAL_DIG - n + 1);
//puts(buf);
char *endptr;
x = strtod(buf, &endptr);
printf("%g\n", x);
return x;
}
int main() {
foo(0.014568, 1);
foo(0.246456, 1);
foo(0.014568, 2);
foo(0.246456, 2);
return 0;
}
Output
0.014568
0.01
0.246456
0.2
0.014568
0.014
0.246456
0.24
This answer assumes OP does not want a rounded answer. Re: 0.246456 -> 0.24
If you want the result as a string, you should probably print to a string with extra precision, then chop that off yourself. (See #chux's answer for details on how much extra precision you need for IEEE 64-bit double to avoid rounding up from a string of 9s, since you want truncation but all the usual to-string functions round to nearest.)
If you want a double result, then are you sure you really want this? Rounding / truncating early in the middle of a calculation usually just worsens the accuracy of the final result. Of course, there are uses in real algorithms for floor/ceil, trunc, and nearbyint, and this is just a scaled version of trunc.
If you just want a double, you can get fairly good results without ever going to a string. Use ndigits and floor(log10(fabs(x))) to work out a scale factor, then truncate the scaled value to an integer, then scale back.
Tested and working (with and without -ffast-math). See the asm on the Godbolt compiler explorer. This might run reasonably efficiently, especially with -ffast-math -msse4.1 (so floor and trunc can inline to roundsd).
If you care about speed, look into replacing pow() with something that takes advantage of the fact that the exponent is a small integer. I'm not sure how fast library pow() implementations are in that case. GNU C __builtin_powi(x, n) trades accuracy for speed, for integer exponents, doing a multiplication tree, which is less accurate than what pow() does.
#include <float.h>
#include <math.h>
#include <stdio.h>
double truncate_n_digits(double x, int digits)
{
if (x==0 || !isfinite(x))
return x; // good idea stolen from Chux's answer :)
double l10 = log10(fabs(x));
double scale = pow(10., floor(l10) + (1 - digits)); // floor rounds towards -Inf
double scaled = x / scale;
double scaletrunc = trunc(scaled); // trunc rounds towards zero
double truncated = scaletrunc * scale;
#if 1 // debugging code
printf("%2d %24.14g =>\t%24.14g\t scale=%g, scaled=%.30g\n", digits, x, truncated, scale, scaled);
// print with more accuracy to reveal the real behaviour
printf(" %24.20g =>\t%24.20g\n", x, truncated);
#endif
return truncated;
}
test cases:
int main() {
truncate_n_digits(0.014568, 1);
truncate_n_digits(0.246456, 1);
truncate_n_digits(0.014568, 2);
truncate_n_digits(-0.246456, 2);
truncate_n_digits(1234567, 2);
truncate_n_digits(99999999999, 6);
truncate_n_digits(-99999999999, 6);
truncate_n_digits(99999, 10);
truncate_n_digits(-0.0000000001234567, 3);
truncate_n_digits(1000, 6);
truncate_n_digits(0.001, 6);
truncate_n_digits(1e-312, 2); // denormal, and not exactly representable: 9.999...e-313
truncate_n_digits(nextafter(1e-312, INFINITY), 2); // denormal, just above 1.00000e-312
return 0;
}
each result shown twice: first with only %.14g so rounding gives the string we want, then again with %.20g to show enough places to reveal the realities of floating point math. Most numbers are not exactly-representable, so even with perfect rounding it's impossible to return a double exactly represents the truncated decimal string. (Integers up to about the size of the mantissa are exactly representable, and so are fractions where the denominator is a power of 2.)
1 0.014568 => 0.01 scale=0.01, scaled=1.45679999999999987281285029894
0.014567999999999999353 => 0.010000000000000000208
1 0.246456 => 0.2 scale=0.1, scaled=2.46456000000000008398615136684
0.2464560000000000084 => 0.2000000000000000111
2 0.014568 => 0.014 scale=0.001, scaled=14.5679999999999996163069226895
0.014567999999999999353 => 0.014000000000000000291
2 -0.246456 => -0.24 scale=0.01, scaled=-24.6456000000000017280399333686
-0.2464560000000000084 => -0.23999999999999999112
3 1234.56789 => 1230 scale=10, scaled=123.456789000000000555701262783
1234.567890000000034 => 1230
6 1234.56789 => 1234.56 scale=0.01, scaled=123456.789000000004307366907597
1234.567890000000034 => 1234.5599999999999454
6 99999999999 => 99999900000 scale=100000, scaled=999999.999990000040270388126373
99999999999 => 99999900000
6 -99999999999 => -99999900000 scale=100000, scaled=-999999.999990000040270388126373
-99999999999 => -99999900000
10 99999 => 99999 scale=1e-05, scaled=9999900000
99999 => 99999.000000000014552
3 -1.234567e-10 => -1.23e-10 scale=1e-12, scaled=-123.456699999999983674570103176
-1.234566999999999879e-10 => -1.2299999999999998884e-10
6 1000 => 1000 scale=0.01, scaled=100000
1000 => 1000
6 0.001 => 0.001 scale=1e-08, scaled=100000
0.0010000000000000000208 => 0.0010000000000000000208
2 9.9999999999847e-313 => 9.9999999996388e-313 scale=1e-314, scaled=100.000000003458453079474566039
9.9999999999846534143e-313 => 9.9999999996388074622e-313
2 1.0000000000034e-312 => 9.0000000001196e-313 scale=1e-313, scaled=9.9999999999011865980946822674
1.0000000000034059979e-312 => 9.0000000001195857973e-31
Since the result you want will often not be exactly representable, (and because of other rounding errors) the resulting double will sometimes be below the result you want, so printing it with full precision might give 1.19999999 instead of 1.20000011. You might want to use nextafter(result, copysign(INFINITY, original)) to get a result that's more likely to have a higher magnitude than what you want.
Of course, that could just make things worse in some cases. But since we truncate towards zero, most often we get a result that's just below (in magnitude) the unrepresentable exact value.
Ok, another one like #Peter Cordes but more generic.
/** Return \c digits semantic digis of number \c x.
\tparam T Type of number \c x can be floating point or integral.
\param x The number.
\param digits The requested number of semantic digits of number \c x.
\return The number with only \c digits semantic digits of number \c x. */
template<typename T>
requires(std::integral<T> || std::floating_point<T>)
T roundn(T x, unsigned int digits)
{
if (!x || !std::isfinite(x)) return x;
typedef std::conditional_t<std::floating_point<T>, T, double> Tp;
Tp mul = pow(10, floor(digits - log10(abs(x))));
Tp y = round(x * mul) / mul;
if constexpr (std::floating_point<T>) return y;
else return round(y);
}
int main()
{
cout << setprecision(100);
cout << roundn(123.456789, 1) << "\n";
cout << roundn(123.456789, 2) << "\n";
cout << roundn(123.456789, 3) << "\n";
cout << roundn(123.456789, 4) << "\n";
cout << roundn(123.456789, 5) << "\n";
cout << roundn(-123.456789, 1) << "\n";
cout << roundn(-123.456789, 2) << "\n";
cout << roundn(-123.456789, 3) << "\n";
cout << roundn(-123.456789, 4) << "\n";
cout << roundn(-123.456789, 5) << "\n";
cout << roundn(-123.456789, 15) << "\n";
cout << roundn(123456, 1) << "\n";
cout << roundn(123456, 2) << "\n";
cout << roundn(123456, 3) << "\n";
cout << roundn(123456, 10) << "\n";
cout << roundn(-123456, 1) << "\n";
cout << roundn(-123456, 2) << "\n";
cout << roundn(-123456, 3) << "\n";
cout << roundn(-123456, 10) << "\n";
cout << roundn(0.0123456789, 1) << "\n";
cout << roundn(0.0123456789, 2) << "\n";
cout << roundn(-0.0123456789, 1) << "\n";
cout << roundn(-0.0123456789, 2) << "\n";
return 0;
}
It returns
99.9999999999999857891452847979962825775146484375
120
123
123.5
123.4599999999999937472239253111183643341064453125
-99.9999999999999857891452847979962825775146484375
-120
-123
-123.5
-123.4599999999999937472239253111183643341064453125
-123.4567890000000005557012627832591533660888671875
100000
120000
123000
123456
-100000
-120000
-123000
-123456
0.01000000000000000020816681711721685132943093776702880859375
0.0120000000000000002498001805406602215953171253204345703125
-0.01000000000000000020816681711721685132943093776702880859375
-0.0120000000000000002498001805406602215953171253204345703125

Why cout's default precision doesn't effect evaluated result?

Here is what I am thinking:
#include <iostream>
#include <iomanip>
int main ()
{
double x = 10-9.99;
std::cout << x << std::endl;
std::cout << std::setprecision (16);
std::cout << x;
return 0;
}
The above program prints 0.01 by evaluating x before setprecision () and a long number that is not exactly equal to 0.01, for x after setprecision ().cout has a default precision of 16 when printing floating point numbers in my machine. If precision is 16, the above value should be something like 0.0100000000000000 but it remains 0.01but when I setprecision () to 16, the program prints a long number containing 16 digits. So my question is, why cout doesn't prints all the digits according to types default precision. Why we need to force cout (by using setprecision ()) to print all the digits?
why cout doesn't prints all the digits according to types default precision.
If you use std::fixed as well as setprecision, it will display however-many digits the precision asks for, without rounding and truncating.
As for why the rounding accounts for the output...
Let's get your code to print a couple other things too:
#include <iostream>
#include <iomanip>
int main ()
{
double x = 10-9.99;
std::cout << x << '\n';
std::cout << std::setprecision (16);
std::cout << x << '\n';
std::cout << 0.01 << '\n';
std::cout << std::setprecision (18);
std::cout << x << '\n';
std::cout << 0.01 << '\n';
std::cout << x - 0.01 << '\n';
}
And the output (on one specific compiler/system):
0.01 // x default
0.009999999999999787 // x after setprecision(16)
0.01 // 0.01 after setprecision(16)
0.00999999999999978684 // x after setprecision(18)
0.0100000000000000002 // 0.01 after setprecision(18)
-2.13370987545147273e-16 // x - 0.01
If we look at how 0.01 is directly encoded at 18 digit precision...
0.0100000000000000002
123456789012345678 // counting digits
...we can see clearly why it could get truncated to "0.01" during output at any precision up to 17.
You can also see clearly that there's a different value in x to that created by directly coding 0.01 - that's allowed because it's the result of a calculation, and dependent on a double or CPU-register approximation of 9.99, either or both of which have caused the discrepancy. That error is enough to prevent the rounding to "0.01" at precision 16.
Unfortunately, this kind of thing is normal when handling doubles and floats.

precision of double function based string

Let's say that you have a function:
string function(){
double f = 2.48452
double g = 2
double h = 5482.48552
double i = -78.00
double j = 2.10
return x; // ***
}
* for x we insert:
if we will insert f, function returns: 2.48
if we will insert g, function returns: 2
if we will insert h, function returns: 5482.49
if we will insert i, function returns:-78
if we will insert j, function returns: 2.1
They are only example, who shows how the funcion() works. To precise:
The function for double k return rounded it to: k.XX,
but for:
k=2.20
it return 2.2 as string.
How it implements?
1) Just because you see two digits, it doesn't mean the underlying value was necessarily rounded to two digits.
The precision of the VALUE and the number of digits displayed in the FORMATTED OUTPUT are two completely different things.
2) If you're using cout, you can control formatting with "setprecision()":
http://www.cplusplus.com/reference/iomanip/setprecision/
EXAMPLE (from the above link):
// setprecision example
#include <iostream> // std::cout, std::fixed
#include <iomanip> // std::setprecision
int main () {
double f =3.14159;
std::cout << std::setprecision(5) << f << '\n';
std::cout << std::setprecision(9) << f << '\n';
std::cout << std::fixed;
std::cout << std::setprecision(5) << f << '\n';
std::cout << std::setprecision(9) << f << '\n';
return 0;
}
sample output:
3.1416
3.14159
3.14159
3.141590000
Mathematically, 2.2 is exactly the same as 2.20, 2.200, 2.2000, and so on. If you want to see more insignificant zeros, use [setprecision][1]:
cout << fixed << setprecision(2);
cout << 2.2 << endl; // Prints 2.20
To show up to 2 decimal places, but not showing trailing zeros you can do something such as:
std::string function(double value)
{
// get fractional part
double fracpart = value - static_cast<long>(value);
// compute fractional part rounded to 2 decimal places as an int
int decimal = static_cast<int>(100*fabs(fracpart) + 0.5);
if (decimal >= 100) decimal -= 100;
// adjust precision based on the number of trailing zeros
int precision = 2; // default 2 digits precision
if (0 == decimal) precision = 0; // 2 trailing zeros, don't show decimals
else if (0 == (decimal % 10)) precision = 1; // 1 trailing zero, keep 1 decimal place
// convert value to string
std::stringstream str;
str << std::fixed << std::setprecision(precision) << value;
return str.str();
}

How to produce formatting similar to .NET's '0.###%' in iostreams?

I would like to output a floating-point number as a percentage, with up to three decimal places.
I know that iostreams have three different ways of presenting floats:
"default", which displays using either the rules of fixed or scientific, depending on the number of significant digits desired as defined by setprecision;
fixed, which displays a fixed number of decimal places defined by setprecision; and
scientific, which displays a fixed number of decimal places but using scientific notation, i.e. mantissa + exponent of the radix.
These three modes can be seen in effect with this code:
#include <iostream>
#include <iomanip>
int main() {
double d = 0.00000095;
double e = 0.95;
std::cout << std::setprecision(3);
std::cout.unsetf(std::ios::floatfield);
std::cout << "d = " << (100. * d) << "%\n";
std::cout << "e = " << (100. * e) << "%\n";
std::cout << std::fixed;
std::cout << "d = " << (100. * d) << "%\n";
std::cout << "e = " << (100. * e) << "%\n";
std::cout << std::scientific;
std::cout << "d = " << (100. * d) << "%\n";
std::cout << "e = " << (100. * e) << "%\n";
}
// output:
// d = 9.5e-05%
// e = 95%
// d = 0.000%
// e = 95.000%
// d = 9.500e-05%
// e = 9.500e+01%
None of these options satisfies me.
I would like to avoid any scientific notation here as it makes the percentages really hard to read. I want to keep at most three decimal places, and it's ok if very small values show up as zero. However, I would also like to avoid trailing zeros in fractional places for cases like 0.95 above: I want that to display as in the second line, as "95%".
In .NET, I can achieve this with a custom format string like "0.###%", which gives me a number formatted as a percentage with at least one digit left of the decimal separator, and up to three digits right of the decimal separator, trailing zeros skipped: http://ideone.com/uV3nDi
Can I achieve this with iostreams, without writing my own formatting logic (e.g. special casing small numbers)?
I'm reasonably certain nothing built into iostreams supports this directly.
I think the cleanest way to handle it is to round the number before passing it to an iostream to be printed out:
#include <iostream>
#include <vector>
#include <cmath>
double rounded(double in, int places) {
double factor = std::pow(10, places);
return std::round(in * factor) / factor;
}
int main() {
std::vector<double> values{ 0.000000095123, 0.0095123, 0.95, 0.95123 };
for (auto i : values)
std::cout << "value = " << 100. * rounded(i, 5) << "%\n";
}
Due to the way it does rounding, this has a limitation on the magnitude of numbers it can work with. For percentages this probably isn't an issue, but if you were working with a number close to the largest that can be represented in the type in question (double in this case) the multiplication by pow(10, places) could/would overflow and produce bad results.
Though I can't be absolutely certain, it doesn't seem like this would be likely to cause an issue for the problem you seem to be trying to solve.
This solution is terrible.
I am serious. I don't like it. It's probably slow and the function has a stupid name. Maybe you can use it for test verification, though, because it's so dumb I guess you can easily see it pretty much has to work.
It also assumes decimal separator to be '.', which doesn't have to be the case. The proper point could be obtained by:
char point = std::use_facet< std::numpunct<char> >(std::cout.getloc()).decimal_point();
But that's still not solving the problem, because the characters used for digits could be different and in general this isn't something that should be written in such a way.
Here it is.
template<typename Floating>
std::string formatFloatingUpToN(unsigned n, Floating f) {
std::stringstream out;
out << std::setprecision(n) << std::fixed;
out << f;
std::string ret = out.str();
// if this clause holds, it's all zeroes
if (std::abs(f) < std::pow(0.1, n))
return ret;
while (true) {
if (ret.back() == '0') {
ret.pop_back();
continue;
} else if (ret.back() == '.') {
ret.pop_back();
break;
} else
break;
}
return ret;
}
And here it is in action.