Unwanted division operator behavior, what should I do?

Description of the problem

During fluid simulation, physical time runs as 0, 0.001, 0.002, ..., 4.598, 4.599, 4.6, 4.601, 4.602, ... Now I want to select time = 0.1, 0.2, ..., 4.5, 4.6, ... from this time series, and then conduct further analysis. So I wrote the following code to judge whether fractpart reached zero.

But I'm so surprised that I found that the following two division methods get two different results: what should I do?

 double param, fractpart, intpart; double org = 4.6; double ddd = 0.1; // This is the correct one I need. I got intpart=46 and fractpart=0 // param = org*(1/ddd); // This is not what I want. I got intpart=45 and fractpart=1 param = org/ddd; fractpart = modf(param , &intpart); Info<< "\n\nfractpart\t=\t" << fractpart << "\nAnd intpart\t=\t" << intpart << endl; 

Why is this happening?
And if you guys endure me a little, I can scream loudly: "Can the C ++ committee do something with this? Because it is confusing." :)

What is the best way to get the right balance to avoid the crop effect? Is fmod the best solution? Thanks

Answer the answer

David schwartz

 double aTmp = 1; double bTmp = 2; double cTmp = 3; double AAA = bTmp/cTmp; double BBB = bTmp*(aTmp/cTmp); Info<< "\n2/3\t=\t" << AAA << "\n2*(1/3)\t=\t" << BBB << endl; 

And I got both

 2/3 = 0.666667 2*(1/3) = 0.666667 
+6
source share
4 answers

Floating-point values ​​cannot accurately represent all possible numbers, so your numbers are getting closer. This leads to different results when used in the calculations.

If you need to compare floating point numbers, you should always use a small epsilon value, not equality testing. In your case, I would round to the nearest integer (without rounding down), subtract from the original value and compare the abs () of the result with epsilon.

If the question is why the amount is different, the simple answer is that they are different amounts. For a more detailed explanation, the actual representations of numbers are given here:

  org: 4.5999999999999996 = 0x12666666666666 * 2^-50 ddd: 0.10000000000000001 = 0x1999999999999a * 2^-56 1/ddd: 10 = 0x14000000000000 * 2^-49 org * (1/ddd): 46 = 0x17000000000000 * 2^-47 org / ddd: 45.999999999999993 = 0x16ffffffffffff * 2^-47 

You will see that no input value is exactly represented in double, each of which is rounded up or down to the nearest value. org rounded since the next bit in the sequence will be 0. ddd was rounded because the next bit in this sequence will be 1.

Because of this, when mathematical operations are performed, rounding can either be canceled or accumulated depending on the operation and how the original numbers were rounded.

In this case, 1 / 0.1 is rounded neatly back exactly to 10.

Multiplying org by 10 rounds off.

The division of org into ddd is rounded (I say happens, but you divide the rounded number by the rounded number, so it’s natural that the result is less).

Different inputs will rotate in different ways.

This is only one bit of error that can be easily ignored even with small epsilon.

+7
source

If I understand your question correctly, it is: Why is X/Y not the same as X * (1/Y) with limited precision arithmetic?

And the reason is simple: consider, for example, six decimal digits. Although this is not what a double really is, the concept is exactly the same.

With six decimal places 1/3 - .333333 . But 2/3 is .666667 . So:

 2 / 3 = .666667 2 * (1/3) = 2 * .333333 = .6666666 

It is simply the nature of mathematics with fixed precision. If you cannot tolerate this behavior, do not use limited precision types.

+1
source

You cannot exactly imagine 4.6 : http://www.binaryconvert.com/result_double.html?decimal=052046054

Use rounding before separating integers and fractions.

UPDATE

You can use the rational class from the Boost library: http://www.boost.org/doc/libs/1_52_0/libs/rational/rational.html

ABOUT YOUR TASK

To find the required double , consider accuracy, for example, to find 4.6 calculate the "proximity" to it:

 double time; ... double epsilon = 0.001; if( abs(time-4.6) <= epsilon ) { // found! } 
0
source

Not sure what you want to achieve, but if you want to get a value and then want to make some refinements in the 1/1000 range, why not use integers instead of floats / double?

You will have a divisor that is 1000, and you have values ​​that you iterate over what you need to multiply by your divisor.

So, you would get something like

 double org = ... // comes from somewhere int divisor = 1000; int referenceValue = org * div; for (size_t step = referenceValue - 10; step < referenceValue + 10; ++step) { // use (double) step / divisor to feed to your algorithm } 
0
source

All Articles