import java.util.ArrayList;
import java.util.Collections;
import java.util.Locale;

public class Euler644 {

    static final double kEps = 1e-12;

    static class Segment {
        double l, r;
        int grundy;

        Segment(double l, double r, int grundy) {
            this.l = l;
            this.r = r;
            this.grundy = grundy;
        }
    }

    static class MSpline {
        double l, r, slope, c;

        MSpline(double l, double r, double slope, double c) {
            this.l = l;
            this.r = r;
            this.slope = slope;
            this.c = c;
        }
    }

    static class Event implements Comparable<Event> {
        double pos, delta;

        Event(double pos, double delta) {
            this.pos = pos;
            this.delta = delta;
        }

        @Override
        public int compareTo(Event o) {
            return Double.compare(this.pos, o.pos);
        }
    }

    static ArrayList<Double> buildBreakpoints(double limit, double sqrt2) {
        ArrayList<Double> vals = new ArrayList<>();
        int max_b = (int) (limit / sqrt2) + 2;
        for (int b = 0; b <= max_b; ++b) {
            double base = sqrt2 * b;
            if (base > limit + kEps)
                break;
            int a_max = (int) Math.floor(limit - base + kEps);
            for (int a = 0; a <= a_max; ++a) {
                vals.add(base + a);
            }
        }
        Collections.sort(vals);
        ArrayList<Double> uniq = new ArrayList<>();
        for (double v : vals) {
            if (uniq.isEmpty() || v - uniq.get(uniq.size() - 1) > kEps) {
                uniq.add(v);
            }
        }
        if (uniq.isEmpty() || limit - uniq.get(uniq.size() - 1) > kEps) {
            uniq.add(limit);
        }
        return uniq;
    }

    static int findSegment(ArrayList<Segment> segs, double x) {
        int lo = 0;
        int hi = segs.size();
        while (lo < hi) {
            int mid = (lo + hi) >> 1;
            if (segs.get(mid).r <= x + kEps) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        if (lo >= segs.size())
            return segs.size() - 1;
        return lo;
    }

    static void collectXors(double u, ArrayList<Segment> segs, ArrayList<Integer> reachable) {
        if (u < -kEps)
            return;
        if (u <= kEps) {
            reachable.set(0, 1);
            return;
        }
        if (segs.isEmpty())
            return;
        int i = 0;
        int j = findSegment(segs, u);
        while (i < segs.size() && j >= 0) {
            double x_l = segs.get(i).l;
            double x_r = Math.min(segs.get(i).r, u);
            if (x_l >= u - kEps)
                break;
            double y_l = segs.get(j).l;
            double y_r = segs.get(j).r;
            double left = Math.max(x_l, u - y_r);
            double right = Math.min(x_r, u - y_l);
            if (right > left + kEps) {
                int val = segs.get(i).grundy ^ segs.get(j).grundy;
                while (val >= reachable.size())
                    reachable.add(0);
                reachable.set(val, 1);
            }
            if (x_r <= u - y_l + kEps) {
                ++i;
            } else {
                --j;
            }
        }
    }

    static ArrayList<Segment> buildGrundySegments(double limit, double sqrt2) {
        ArrayList<Double> points = buildBreakpoints(limit, sqrt2);
        ArrayList<Segment> segs = new ArrayList<>();
        ArrayList<Integer> reachable = new ArrayList<>(128);
        for (int i = 0; i < 128; i++)
            reachable.add(0);
        for (int i = 0; i + 1 < points.size(); ++i) {
            double l = points.get(i);
            double r = points.get(i + 1);
            double mid = (l + r) * 0.5;
            for (int k = 0; k < reachable.size(); k++)
                reachable.set(k, 0);
            if (mid >= 1.0 - kEps)
                collectXors(mid - 1.0, segs, reachable);
            if (mid >= sqrt2 - kEps)
                collectXors(mid - sqrt2, segs, reachable);
            int mex = 0;
            while (mex < reachable.size() && reachable.get(mex) == 1)
                ++mex;
            while (mex >= reachable.size())
                reachable.add(0);
            if (!segs.isEmpty() && Math.abs(segs.get(segs.size() - 1).r - l) <= kEps
                    && segs.get(segs.size() - 1).grundy == mex) {
                segs.get(segs.size() - 1).r = r;
            } else {
                segs.add(new Segment(l, r, mex));
            }
        }
        return segs;
    }

    static ArrayList<MSpline> buildMSplines(ArrayList<Segment> gsegs, double limit) {
        int max_g = 0;
        for (Segment seg : gsegs)
            max_g = Math.max(max_g, seg.grundy);
        ArrayList<ArrayList<Segment>> per_g = new ArrayList<>(max_g + 1);
        for (int i = 0; i <= max_g; i++)
            per_g.add(new ArrayList<>());
        for (Segment seg : gsegs)
            per_g.get(seg.grundy).add(seg);

        ArrayList<Event> events = new ArrayList<>();
        for (ArrayList<Segment> lst : per_g) {
            int n = lst.size();
            for (int i = 0; i < n; ++i) {
                for (int j = i; j < n; ++j) {
                    double weight = (i == j) ? 1.0 : 2.0;
                    double A = lst.get(i).l;
                    double B = lst.get(i).r;
                    double C = lst.get(j).l;
                    double D = lst.get(j).r;
                    double p1 = A + C;
                    double p2 = A + D;
                    double p3 = B + C;
                    double p4 = B + D;
                    if (p1 <= limit + kEps)
                        events.add(new Event(p1, weight));
                    if (p2 <= limit + kEps)
                        events.add(new Event(p2, -weight));
                    if (p3 <= limit + kEps)
                        events.add(new Event(p3, -weight));
                    if (p4 <= limit + kEps)
                        events.add(new Event(p4, weight));
                }
            }
        }

        Collections.sort(events);
        ArrayList<MSpline> splines = new ArrayList<>();
        double prev = 0.0;
        double slope = 0.0;
        double value = 0.0;
        int idx = 0;
        while (idx < events.size()) {
            double pos = events.get(idx).pos;
            if (pos > limit + kEps)
                break;
            if (pos > prev + kEps) {
                double c = value - slope * prev;
                splines.add(new MSpline(prev, pos, slope, c));
                value += slope * (pos - prev);
                prev = pos;
            }
            double delta = 0.0;
            while (idx < events.size() && Math.abs(events.get(idx).pos - pos) <= kEps) {
                delta += events.get(idx).delta;
                ++idx;
            }
            slope += delta;
        }
        if (prev < limit - kEps) {
            double c = value - slope * prev;
            splines.add(new MSpline(prev, limit, slope, c));
        }
        if (splines.isEmpty()) {
            splines.add(new MSpline(0.0, limit, 0.0, 0.0));
        }
        return splines;
    }

    static int findMSegment(ArrayList<MSpline> splines, double x) {
        int lo = 0;
        int hi = splines.size();
        while (lo < hi) {
            int mid = (lo + hi) >> 1;
            if (splines.get(mid).r <= x + kEps) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        if (lo >= splines.size())
            return splines.size() - 1;
        return lo;
    }

    static double evalM(ArrayList<MSpline> splines, double u) {
        if (u <= 0.0)
            return 0.0;
        int idx = findMSegment(splines, u);
        double val = splines.get(idx).slope * u + splines.get(idx).c;
        if (val < 0.0 && val > -1e-9)
            val = 0.0;
        return val;
    }

    static double eValue(ArrayList<MSpline> splines, double sqrt2, double L) {
        double u1 = L - 1.0;
        double u2 = L - sqrt2;
        if (u1 <= 0.0 || u2 <= 0.0)
            return 0.0;
        double m1 = evalM(splines, u1);
        double m2 = evalM(splines, u2);
        return 0.5 * L * (m1 / u1 + m2 / u2);
    }

    static double fPrime(double L, double sqrt2, double s1, double c1, double s2, double c2) {
        double t1 = L - 1.0;
        double t2 = L - sqrt2;
        return (s1 + s2) - c1 / (t1 * t1) - c2 * sqrt2 / (t2 * t2);
    }

    static double findRoot(double a, double b, double sqrt2, double s1, double c1, double s2, double c2) {
        double fa = fPrime(a, sqrt2, s1, c1, s2, c2);
        double fb = fPrime(b, sqrt2, s1, c1, s2, c2);
        if (Math.abs(fa) <= 1e-18)
            return a;
        if (Math.abs(fb) <= 1e-18)
            return b;
        if (fa * fb > 0.0)
            return Double.NaN;
        for (int it = 0; it < 80; ++it) {
            double mid = (a + b) * 0.5;
            double fm = fPrime(mid, sqrt2, s1, c1, s2, c2);
            if (fa * fm <= 0.0) {
                b = mid;
                fb = fm;
            } else {
                a = mid;
                fa = fm;
            }
        }
        return (a + b) * 0.5;
    }

    static double maxEInRange(ArrayList<MSpline> splines, ArrayList<Double> breakpoints, double sqrt2, double Lmin,
            double Lmax) {
        int start = 0;
        while (start < breakpoints.size() && breakpoints.get(start) < Lmin - kEps)
            start++;
        int end = 0;
        while (end < breakpoints.size() && breakpoints.get(end) <= Lmax + kEps)
            end++;
        int last_interval = breakpoints.size() - 1;
        if (start >= last_interval)
            return 0.0;
        if (end > last_interval)
            end = last_interval;
        if (end <= start)
            return 0.0;

        double best = 0.0;
        for (int i = start; i < end; ++i) {
            double a = Math.max(breakpoints.get(i), Lmin);
            double b = Math.min(breakpoints.get(i + 1), Lmax);
            if (b <= a + kEps)
                continue;

            double mid = (a + b) * 0.5;
            double u1 = mid - 1.0;
            double u2 = mid - sqrt2;
            if (u1 <= 0.0 || u2 <= 0.0)
                continue;
            int idx1 = findMSegment(splines, u1);
            int idx2 = findMSegment(splines, u2);
            double s1 = splines.get(idx1).slope;
            double c1 = splines.get(idx1).c;
            double s2 = splines.get(idx2).slope;
            double c2 = splines.get(idx2).c;

            double ea = eValue(splines, sqrt2, a);
            double eb = eValue(splines, sqrt2, b);
            best = Math.max(best, Math.max(ea, eb));

            double[] points = { a, a + (b - a) * 0.25, mid, a + (b - a) * 0.75, b };
            double[] fp = new double[5];
            for (int p = 0; p < 5; ++p)
                fp[p] = fPrime(points[p], sqrt2, s1, c1, s2, c2);
            for (int p = 0; p < 4; ++p) {
                if (fp[p] == 0.0) {
                    double ev = eValue(splines, sqrt2, points[p]);
                    best = Math.max(best, ev);
                    continue;
                }
                if (fp[p] * fp[p + 1] <= 0.0) {
                    double root = findRoot(points[p], points[p + 1], sqrt2, s1, c1, s2, c2);
                    if (!Double.isNaN(root)) {
                        double ev = eValue(splines, sqrt2, root);
                        best = Math.max(best, ev);
                    }
                }
            }
        }
        return best;
    }

    public static String solve() {
        double Lmin = 200.0;
        double Lmax = 500.0;
        double sqrt2 = Math.sqrt(2.0);

        ArrayList<Segment> gsegs = buildGrundySegments(Lmax, sqrt2);
        ArrayList<MSpline> splines = buildMSplines(gsegs, Lmax - 1.0);

        double global_min = Math.min(Lmin, 2.0);
        ArrayList<Double> breakpoints = new ArrayList<>();
        breakpoints.add(global_min);
        breakpoints.add(Lmax);
        for (MSpline sp : splines) {
            double a = sp.l + 1.0;
            double b = sp.r + 1.0;
            double c = sp.l + sqrt2;
            double d = sp.r + sqrt2;
            if (a <= Lmax + kEps)
                breakpoints.add(a);
            if (b <= Lmax + kEps)
                breakpoints.add(b);
            if (c <= Lmax + kEps)
                breakpoints.add(c);
            if (d <= Lmax + kEps)
                breakpoints.add(d);
        }
        Collections.sort(breakpoints);
        ArrayList<Double> uniq = new ArrayList<>();
        for (double v : breakpoints) {
            if (v < global_min - kEps || v > Lmax + kEps)
                continue;
            if (uniq.isEmpty() || v - uniq.get(uniq.size() - 1) > kEps)
                uniq.add(v);
        }
        if (uniq.isEmpty() || global_min - uniq.get(0) < -kEps)
            uniq.add(0, global_min);
        if (uniq.isEmpty() || Lmax - uniq.get(uniq.size() - 1) > kEps)
            uniq.add(Lmax);

        double ans = maxEInRange(splines, uniq, sqrt2, Lmin, Lmax);
        return String.format(Locale.US, "%.8f", ans);
    }

    public static void main(String[] args) {
        System.out.println(solve());
    }
}
