Save number with high decimal
-
Hello. How can I save a number with high decimal in C++? Like 5.69787E-54 and 3.46E-30. I try
long double
but it doesn't useful. -
@SeyMohsenFls
Do you values fit into standarddouble
?long double
is implementation dependent (https://en.cppreference.com/w/cpp/language/types, and it shows ranges for various double sizes there), do your numbers require that. Else you have values outside the range of either, in which case you have a problem.... -
I used double but double can save 15 or 17 decimal and not work for me.
-
@SeyMohsenFls Then you must use a library like e.g. boost.math.highprecission or similar
-
I guess you need to be a little bit more clear what you mean by saving floating point numbers.
A regular
double
has about 16 digits internal precision. This means that if you write your number with an exponent such that there is 1 digit in front of the decimal point you have about 15 digits after the decimal point. If you save this to a binary file (if saving to a file is what you mean) you don't loose anything.You have to be careful with displaying floating point numbers or saving them to an ASCII file. Here you specify the number of digits to be displayed/saved. The floating point standard says that if you convert the
double
to a floating point representation with 16 digits it can be converted back to the same number. Don't be tricked into thinking that just because you don't see digits when you print them on screen that they are not there.For the two example you showed (5.69787E-54 and 3.46E-30) even a regular
float
would be sufficient for representing these numbers: The first number has only 6 digits (float
supports 7) and the exponent for afloat
goes down to -126.It gets trickier, though, when you want to do arithmetic with these numbers. Multiplication and division of floating point numbers does not loose much precision. However, if you want to add or subtract the two numbers, they first have to be brought to the same exponent. Internally the CPU will change (in your example of the two numbers) 5.69787e-54 to 0.0000000000000000569787e-30 (hopefully I got the count of zeros correct). Even with
double
this number cannot be represented anymore.If this problem arises when adding a lot of floating point numbers, the best approach is to only ever sum two numbers (or maybe a bunch of them) and then add two of these sums and so on, until you arrive with a single sum. You can also write your own Kahan double class which internally uses the Kahan summation algorithm to avoid loosing too much precision.
Maybe you can elaborate on what you are actually trying to do, so we can help you with your problem. Because floating point numbers can be really tricky if you are new to this.
-
Thank you @SimonSchroeder . I have written a code that has the result of many decimal places and I need to use these generated numbers. I use eigen library in the written code and these numbers are supposed to be the input of one of the functions of this library.
The function works great when the numbers are integers or have small decimals, but when the decimal places of the numbers are very high, like the two numbers I mentioned, the function can not calculate the result well.
I also wrote the code in MATLAB and tested it and it works well, but in C ++ the result is not good!
-
Not that this is a Qt issue, but can you provide a small example that demonstrates a problem?
The two values you provided can be adequately represented in a C++ float or double as @SimonSchroeder points out. If you are passing them to Eigen functions with a matching signature then there should be no particular problem.
MATLAB's default 16 significant digits matches a double, although it may internally be using an arbitrary precision engine that avoids some issues with accuracy of representation.
-
Yes, the code is as follow:
#include <iostream> #include "Eigen/Dense" using namespace std; using namespace Eigen; int main(){ // 3x3 array double arr1[][3]={{-4.41454283058227e-08,-4.15360517928287e-18,-2.08705000000000e-31}, {1,0,0}, {0,1,0}}; MatrixXd array(3,3); for(int i = 0; i < 3; i++){ for(int j = 0; j < 3; j++){ array(i,j)=arr1[i][j]; } } MatrixXcd eig = array.eigenvalues(); }
The results shown in eig are incorrect and that's the problem.
-
What do you expect? You're using double values in the first place... As already said - you have to create the values directly with eigen to get the correct precision.
-
Can you provide an example to solve this problem?
-
@SeyMohsenFls No, I don't use such libraries. But found an example for boost::multiprecission. You should ask at the eigen forum (or boost) - they know such stuff much better.
-
@SeyMohsenFls said in Save number with high decimal:
The results shown in eig are incorrect and that's the problem.
Can you show us the output from both your little program and also from your Matlab code?
-
Yes.
in MATLAB:>> A=[-4.41454283058227e-08,-4.15360517928287e-18,-2.08705000000000e-31;1 0 0; 0 1 0]; >> eig(A) ans = 1.0e-07 * -0.440511378824384 -0.000942401498110 -0.000000502735733
and in C++:
(-4.41454283058e-08, 0.0) (1.10850979645e-08, 0.0) (-1.10850979645e-08, 0.0)
-
A floating point representation of most decimal numbers will not be exact: this is a fundamental limitation of a fixed number of binary digits. Even so, your specific input example closest possible representation are equal to the same specified number of decimal significant digits. They are not precisely equal though, as you will see in the output below.
The default interpretation of numeric literals is as doubles and MatrixXd is a matrix of doubles, so this is not a gross type conversion problem on input (like treating them as single-precision floats and losing the precision).
This is what I put in and get:
// cout.precision(17); // cout << array << std::endl; -4.4145428305822701e-08 -4.1536051792828701e-18 -2.0870500000000001e-31 1 0 0 0 1 0 // cout << eig << std::endl; (-4.4145428305822701e-08,0) (1.1085097964543275e-08,0) (-1.1085097964543283e-08,0)
Now, testing the resulting eigenvalues
// Cross checks MatrixXd identity(3,3); identity.setIdentity(); for (int i = 0; i < 3; ++i) { MatrixXd result = array - (real(eig(i)) * identity); double det = result.determinant(); cout << i << ": " << det << std::endl; } 0: 1.8336247094772579e-25 1: -6.8327370866854444e-24 2: -4.0163905414930213e-24
So the values are tiny but not precisely zero as they would be in the ideal, unlimited precision world.
Octave, a MatLab work-alike, gives this result:
octave:1> output_precision(16) octave:2> A=[-4.41454283058227e-08,-4.15360517928287e-18,-2.08705000000000e-31;1 0 0; 0 1 0]; octave:3> eigvals = eig(A) eigvals = -4.405113788243840e-08 -9.424014981096368e-11 -5.027357330211182e-14 octave:4> det(A - eigvals(1) * eye(3)) ans = -7.375946750553847e-38 octave:5> det(A - eigvals(2) * eye(3)) ans = 1.191979506216298e-43 octave:6> det(A - eigvals(3) * eye(3)) ans = 0
Its cross check values are not all precisely zero. I suspect MatLab's will not be either.
I am guessing that both MatLab and Octave use an algorithm for determining the eigenvalues that is not the same as the one in Eigen. Error propagation and iteration limits may simply explain the differences. Only an Eigen forum will help explain exactly and probably suggest ways to help the situation.
-
Use a stable solver. Your matrix is quite close to singular. QR comes to mind, eigen already provides it out of the box.