precision of double function based string - c++

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

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.

How to find first pi beginning with 3.14159

For determining how many terms are required for the first time getting pi that begins with 3.14159 I wrote the following program that calculates terms as (pi = 4 - 4/3 + 4/5 - 4/7 + ...).
My problem is that I reached 146063 terms as the result but when I checked, there are many pis that begin similarly before that.
//piEstimation.cpp
//estima mathematical pi and detrmin when
//to get a value beganing with 3.14159
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
int main(){
//initialize vars
double denominator{1.0};
double pi{0};
string piString;
double desiredPi;
int terms;
int firstDesiredTerm;
//format output floating point numbers to show 10 digits after
// decimal poin
cout << setprecision (10) <<fixed;
for (terms = 1; ; terms++){
if(0 == terms % 2){ //if term is even
pi -= 4/denominator;
}
else{ //if term is odd
pi += 4/denominator;
}
// draw table
cout << terms << "\t" << pi << endl;
//determin first time the pi begains with 3.14159
piString = to_string(pi).substr(0,7);
if(piString == "3.14159"){
firstDesiredTerm = terms;
desiredPi = pi;
break;
}
denominator += 2;
}//end for
cout << "The first time that pi value begans with 3.14159 "
<< "the number of terms are " << firstDesiredTerm << " and pi value is "<< desiredPi <<endl;
}//end main
A number x begins with 3.14159 if x >= 3.14159 && x < 3.1416. There is no need to use strings and compare characters. to_string has to use some kind of round operation. Without the string the algorithm finds the result after 136121 steps
#include <iostream>
#include <iomanip>
int main(){
//initialize vars
double denominator{1.0};
double pi{0};
double desiredPi;
int terms;
int firstDesiredTerm;
//format output floating point numbers to show 10 digits after
// decimal poin
std::cout << std::setprecision (20) << std::fixed;
for (terms = 1; ; terms++){
if(0 == terms % 2){ //if term is even
pi -= 4/denominator;
}
else{ //if term is odd
pi += 4/denominator;
}
// draw table
std::cout << terms << "\t" << pi << std::endl;
if(pi >= 3.14159 && pi < 3.1416){
firstDesiredTerm = terms;
desiredPi = pi;
break;
}
denominator += 2;
}//end for
std::cout << "The first time that pi value begans with 3.14159 "
<< "the number of terms are " << firstDesiredTerm
<< " and pi value is "<< desiredPi << std::endl;
}
Output:
The first time that pi value begans with 3.14159 the number of terms are 136121 and pi value is 3.14159999999478589672
Here you can see how to_string rounds the result:
#include <iostream>
#include <iomanip>
#include <string>
int main(){
std::cout << std::setprecision (20) << std::fixed;
std::cout << std::to_string(3.14159999999478589672) << '\n';
}
Output:
3.141600
You can read on cppreference
std::string to_string( double value ); Converts a floating point value to a string with the same content as what std::sprintf(buf, "%f", value) would produce for sufficiently large buf.
You can read on cppreference
f F Precision specifies the exact number of digits to appear after the decimal point character. The default precision is 6
That means that std::to_string rounds after 6 digits.

What does double store?

I'm sending the value 4 *cos( fmod( acos(2.0/4.0), 2*3.14159265) ) as double to this function but I get output as
2
1k1
What is wrong here?
void convert_d_to_f(double n)
{
cout<<n<<" ";
double mantissa;
double fractional_part;
fractional_part = modf(n,&mantissa);
double x = fractional_part;
cout<<mantissa<<"k"<<fractional_part<<'\n';
}
The problem is that cout truncates and rounds double while printing. You can print the desired number of decimal places usingiomanip library.
#include <iostream>
#include <cmath>
#include <iomanip>
void convert_d_to_f(double n)
{
cout<<std::fixed<<std::setprecision(20); //number of decimal places you need to print to
cout<<n<<" ";
double mantissa;
double fractional_part;
fractional_part = modf(n,&mantissa);
double x = fractional_part;
cout<<mantissa<<"k"<<fractional_part<<'\n';
}
int main() {
convert_d_to_f(4 *cos( fmod( acos(2.0/4.0), 2*3.14159265) ));
return 0;
}
For all practical intents and purposes, your number n evaluates to 2. If you want it to display as 1.9999999... etc. then follow Kapil's solution and set the floating point precision for std::cout to many decimal places. Keep in mind the difference between precision and accuracy if you are going to go that route.
That being said, your void convert_d_to_f(double n) function is replicating the functionality of std::frexp(double arg, int* exp) with a limitation where your results are going out of scope after you print them to the screen. If you desire to use your exponent and mantissa values after computing them, then you can do it like this.
#include <iostream>
#include <cmath>
int main()
{
double n = 4 *cos( fmod( acos(2.0/4.0), 2*3.14159265) );
std::cout << "Given the number " << n << std::endl;
// convert the given floating point value `n` into a
// normalized fraction and an integral power of two
int exp;
double mantissa = std::frexp(n, &exp);
// display results as Mantissa x 2^Exponent
std::cout << "We have " << n << " = "
<< mantissa << " * 2^" << exp << std::endl;
return 0;
}

Rounding to second decimal place unless the number is whole c++

I am trying to print the average in Cpp up to 2 decimal points of the float num. avg is float, sum is float, count is int.
Currently if I have 10/1 for example it outputs 10.00. I want the output to be just 10. if avg gets value 3.1467665 for example, it should be displayed as 3.14.
avg = sum/count;
std::cout << std::fixed << std::setprecision(2) << avg;
The rounding should be just for the output. No need to change avg but if it is easier, its value can be changed.
Looking for a solution using a standard before c++11.
UPD: the output is 27.50 when I want it to be 27.5.
You could choose precision according to the floating modulus of the avg value. The following works:
int main() {
double sum = 10;
double count = 3;
double avg = sum/count;
double mod;
std::cout << std::fixed
<< std::setprecision((modf(avg, &mod) != 0.0) ? 2 : 0)
<< avg
<< std::endl;
}
Considering the added specifications:
Write 2.5 instead of 2.50
Write 3.14 for 3.1421783921, rather than 3.15
Here is a possible implementation using #IInspectable's suggested method:
std::stringstream ss;
ss << std::fixed << avg;
size_t pos = ss.str().find('.');
if (pos + 2 < ss.str().size()) // If there is a '.' and more than 2 digits after it
pos += 3;
std::string s = ss.str().substr(0, pos); // Leave only two digits
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return ch != '0'; }).base(), s.end()); // Remove trailing zeros
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return ch != '.'; }).base(), s.end()); // Remove trailing '.' when needed
std::cout << s << std::endl;
This will output:
10/4 -> 2.5
10/3 -> 3.33
10/2 -> 5
10/7 -> 1.42
3.9999 -> 3.99

Output float as three digits, or more to avoid exponent

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!