0

My function to get rid of fractional part of two doubles while retaining their ratio:

void enlarge(double &a, double &b)
{
    while (a != trunc(a) || b != trunc(b))
    {
        a *= 10;
        b *= 10;
        //output added for debugging
        cout << "a = " << a << ", b = " << b << endl;
        cout << "trunc(a) = " << trunc(a) << ", trunc(b) = " << trunc(b) << endl;
        cout << "a == trunc(a): " << to_string(a == trunc(a)) << ", b == trunc(b): " << to_string(b == trunc(b)) << endl;
        //to see output step by step
        string asd;
        cin >> asd;
    }
}

Output when it stops working correctly:

a = 0.876, b = 99.9
trunc(a) = 0, trunc(b) = 99
a == trunc(a): 0, b == trunc(b): 0
a
a = 8.76, b = 999
trunc(a) = 8, trunc(b) = 999
a == trunc(a): 0, b == trunc(b): 1
a
a = 87.6, b = 9990
trunc(a) = 87, trunc(b) = 9990
a == trunc(a): 0, b == trunc(b): 1
a
a = 876, b = 99900
trunc(a) = 876, trunc(b) = 99900    //This is where it stops working
a == trunc(a): 0, b == trunc(b): 1  //Notice how 876 != 876
a
a = 8760, b = 999000
trunc(a) = 8760, trunc(b) = 999000
a == trunc(a): 0, b == trunc(b): 1

What do I do in this situation?

foxneSs
  • 2,259
  • 3
  • 18
  • 21
  • A) Its rather hard to find bugs in code that you do not show. B) std::cout is not printing the numbers to their full precision. There are several questions here on this topic. Just search for it. If you care about output format, I would use printf. C) To answer your question: Use a more precise number type. – 463035818_is_not_an_ai Mar 18 '15 at 18:47
  • Take a look at this: http://stackoverflow.com/questions/10334688/how-dangerous-is-it-to-compare-floating-point-values – NathanOliver Mar 18 '15 at 18:48
  • 1
    Have you considered using *fixed point* math? You may be able get better precision. – Thomas Matthews Mar 18 '15 at 18:49
  • I recommend reading [What Every Computer Scientist Should Know About Floating Point](http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CB8QFjAA&url=http%3A%2F%2Fdocs.oracle.com%2Fcd%2FE19957-01%2F806-3568%2Fncg_goldberg.html&ei=NMkJVdGsJMe2ogSJ_YGICQ&usg=AFQjCNHSL-lclWJrAUgNWVbQ6BAvZRoP9Q&sig2=x-zI2vrS_91kJJvGWh3BUA&bvm=bv.88528373,d.cGU) – Thomas Matthews Mar 18 '15 at 18:52
  • 1
    By the way, don't compare floating point values for equality or inequality. Since most floating point quantities can't be represented internally as exact values, they won't stand up to equality. See the document I quoted in my previous comment. – Thomas Matthews Mar 18 '15 at 18:54
  • `a *= 10` - immediate red flags. Computers tend to work in binary, obscure IBM machines excepted. – MSalters Mar 18 '15 at 20:44

2 Answers2

1

The problem is that the original number wasn't exactly .876, although it wasn't much different. So you may have to multiply by 10 quite a few times to get it to be an integer.

Also, note that if the number was slightly smaller than .876, 1000 times the number would be slightly smaller than 876.0 and trunc(875.99999999...) is 875, not 876.

You might consider some threshold value where the number is "close enough" to an integer. for example, instead of using a==trunc(a), you could use the test

abs(a - round(a)) < 1e-6

Finally, if you wanted to be precise, you could multiply by 2 at each step, instead of by 10. You lose a little precision every time you multiply by 10, because 10 is not a power of 2. Multiplying by 2 will produce an integer after a maximum of 52 multiplications.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Why bothering with `double`, if you could simply use _fixed point math_? – πάντα ῥεῖ Mar 18 '15 at 18:55
  • @πάνταῥεῖ: If I could use fixed point *simply*, I'd probably use it more often, but it's only simple to use in very restricted problem domains, even with external libraries. In the very link you provide, Fowler complains about the lack of currency support in standard libraries, for example. So I bother with `double` when I can't be bothered to build a fixed-point environment :) – rici Mar 18 '15 at 21:35
1

"My function to get rid of fractional part of two doubles while retaining their ratio:"

You probably want to use fixed point values, instead of double at all, to get this working correctly. Something like described in the Money Pattern, which was designed to overcome these kind of problems.

The general idea is you have an int value that holds it by a multiplied (internal) representation according to the precision you want/need. So if you need a precision of 3 decimal points you multiply that (internally) representing value with 1000, 4 decimal points -> * 10000, aso.

If you have to handle huge values that cannot be held in (represented as) a long long integer variable, there are (non standard) big integer managing libraries you could use for this also.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190