#include <bits/stdc++.h>
using namespace std;
#define SZ(x) (int)(x.size())
#define REP(i, n) for(int i=0;i<(n);++i)
#define FOR(i, a, b) for(int i=(a);i<(b);++i)
#define RREP(i, n) for(int i=(int)(n)-1;i>=0;--i)
#define RFOR(i, a, b) for(int i=(int)(b)-1;i>=(a);--i)
#define ALL(a) a.begin(),a.end()
#define DUMP(x) cerr<<#x<<" = "<<(x)<<endl
#define DEBUG(x) cerr<<#x<<" = "<<(x)<<" (L"<<__LINE__<<")"<< endl;
using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using P = pair<int, int>;
const double eps = 1e-8;
const ll MOD = 1000000007;
const int INF = INT_MAX / 2;
const ll LINF = LLONG_MAX / 2;
template <typename T1, typename T2>
bool chmax(T1 &a, const T2 &b) {
    if (a < b) { a = b; return true; }
    return false;
}
template <typename T1, typename T2>
bool chmin(T1 &a, const T2 &b) {
    if (a > b) { a = b; return true; }
    return false;
}
template<typename T>
ostream &operator<<(ostream &os, const vector<T> &v) {
    os << "[";
    REP(i, SZ(v)) {
        if (i) os << ", ";
        os << v[i];
    }
    return os << "]";
}
template<typename T1, typename T2>
ostream &operator<<(ostream &os, const map<T1, T2> &mp) {
    os << "{";
    int a = 0;
    for (auto &tp : mp) {
        if (a) os << ", ";
        a = 1;
        os << tp;
    }
    return os << "}";
}
template<typename T1, typename T2>
ostream &operator<<(ostream &os, const pair<T1, T2> &p) {
    os << p.first << ":" << p.second;
    return os;
}


template<typename T>
struct edge {
    int src, to;
    T cost;

    edge() {}
    edge(int to, T cost) : src(-1), to(to), cost(cost) {}
    edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

    friend ostream& operator<<(ostream& os, const edge& e) {
        return os << "(" << e.src << "->" << e.to << ":" << e.cost << ")";
    }
};

template<typename T>
vector<T> dijkstra(const vector<vector<edge<T>>> &g, int s) {
    const T INF = numeric_limits<T>::max();
    vector<T> d(g.size(), INF);

    using Pi = pair<T, int>;
    priority_queue<Pi, vector<Pi>, greater<Pi>> que;
    d[s] = 0;
    que.emplace(d[s], s);
    while (!que.empty()) {
        T cost;
        int v;
        tie(cost, v) = que.top();
        que.pop();
        if (d[v] < cost) continue;
        for (auto &e : g[v]) {
            T nxt = cost + e.cost;
            if (d[e.to] > nxt) {
                d[e.to] = nxt;
                que.emplace(nxt, e.to);
            }
        }
    }
    return d;
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(10);

    for (;;) {
        int n, m; cin >> n >> m;
        if (n == 0) break;
        int s, g; cin >> s >> g; --s, --g;

        if (m == 0) {
            cout << "unreachable" << endl;
            continue;
        }
        vi u(m), v(m), d(m), c(m);
        REP(i, m) {
            cin >> u[i] >> v[i] >> d[i] >> c[i];
            --u[i], --v[i];
        }
        const int MAX_C = *max_element(ALL(c)) + 1;

        vector<vector<edge<double>>> G((n+1) * n * MAX_C);
        REP(i, m) {
            REP(s, MAX_C) {
                REP(from, n+1) {
                    if (v[i] == from) continue;
                    if (0 < s and s <= c[i]) {
                        G[from * n * MAX_C + u[i] * MAX_C + s].emplace_back(
                                u[i] * n * MAX_C + v[i] * MAX_C + s, (double)d[i] / s);
                    }
                    if (0 < s+1 and s+1 <= c[i]) {
                        G[from * n * MAX_C + u[i] * MAX_C + s].emplace_back(
                                u[i] * n * MAX_C + v[i] * MAX_C + s + 1, (double)d[i] / (s + 1));
                    }
                    if (0 < s-1 and s-1 <= c[i]) {
                        G[from * n * MAX_C + u[i] * MAX_C + s].emplace_back(
                                u[i] * n * MAX_C + v[i] * MAX_C + s - 1, (double)d[i] / (s - 1));
                    }
                }
                REP(from, n+1) {
                    if (u[i] == from) continue;
                    if (0 < s and s <= c[i]) {
                        G[from * n * MAX_C + v[i] * MAX_C + s].emplace_back(
                                v[i] * n * MAX_C + u[i] * MAX_C + s, (double) d[i] / s);
                    }
                    if (0 < s+1 and s+1 <= c[i]) {
                        G[from * n * MAX_C + v[i] * MAX_C + s].emplace_back(
                                v[i] * n * MAX_C + u[i] * MAX_C + s + 1, (double)d[i] / (s + 1));
                    }
                    if (0 < s-1 and s-1 <= c[i]) {
                        G[from * n * MAX_C + v[i] * MAX_C + s].emplace_back(
                                v[i] * n * MAX_C + u[i] * MAX_C + s - 1, (double)d[i] / (s - 1));
                    }
                }
            }
        }
        auto dist = dijkstra(G, n * n * MAX_C + s * MAX_C + 0);

        const double inf = 1e18;
        double ans = inf;

        REP(from, n+1) chmin(ans, dist[from * n * MAX_C + g * MAX_C + 1]);
        if (ans == inf) cout << "unreachable" << endl;
        else cout << ans << endl;
    }

    return 0;
}



Measured memory_bytes: 62312