ShubhamAvasthi's blog

By ShubhamAvasthi, history, 4 years ago, In English

I solved a problem recently using floating point arithmetic. I suspect the solution should not work for some test case because of floating point precision errors, but cannot find a case where it would fail. Moreover, the solution gets "Accepted" verdict.

Link to the submission (Codeforces Round #660 E): 89366718

The submission is an implementation of the Convex Hull Trick approach mentioned in the editorial of the problem.

If you look through the code, you will find that I have maintained forbidden ranges of cotangents of an angle and then iterate over the critical angles (i.e. extremeties of some range of forbidden angles, which are not forbidden themselves).

I suspect that float(xr[j] - xl[i]) / (y[i] - y[j]) should return unequal values for some mathematically equal fractions (like $$$5/3$$$ and $$$15/9$$$) because of precision errors.

In such case, let's say there are two ranges $$$[a, b]$$$ and $$$[c, d]$$$ with $$$b = c$$$ (mathematically). In such a case a possible value that is not forbidden is $$$b$$$. This obviously will not work if there are precision errors which make the calculated values of $$$b$$$ and $$$c$$$ such that $$$b$$$ is slightly greater $$$c$$$. Then, $$$b$$$ will become part of a forbidden range.

My question is, does the above case not show up in the test cases of the problem or is the floating point arithmetic in my code guaranteed to be exact because of some reason?

Can you suggest a case where the program would break?

The following program shows though that the precision errors I mentioned do occur:

#include <cassert>
#include <iomanip>
#include <iostream>
#include <random>
using namespace std;

float foo (int n, int d, int c)
{
    return float(n * c) / (d * c);
}

int main()
{
    default_random_engine generator;
    uniform_int_distribution<> dist(1, 10000);
    while(true)
    {
        int n = dist(generator), d = dist(generator), c1 = dist(generator), c2 = dist(generator);
        float f1 = foo(n, d, c1), f2 = foo(n, d, c2);
        cerr << "Testing with n=" << n << ", d=" << d << ", c1=" << c1 << ", c2=" << c2 << '\n';
        cerr << fixed << setprecision(10) << "Received f1=" << f1 << " and f2=" << f2 << '\n';
        assert(f1 == f2);
        cerr << "OK\n";
    }
}
  • Vote: I like it
  • +19
  • Vote: I do not like it

»
4 years ago, # |
  Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by ShubhamAvasthi (previous revision, new revision, compare).

»
4 years ago, # |
  Vote: I like it +11 Vote: I do not like it

I think the tests may be weak. On this test case:

3
200000 200001 200001
800001 800002 800005
400002 400003 400002

your program gets 3.3281250000, while the implementation in the editorial gives 3.333331111.