// Original C++ solution (slower):
#include <cstdio>

#include <cstring>

#include <vector>

#include <queue>

#include <algorithm>

using namespace std;

const int INFINITY_HAHA = 999999;

 

typedef struct status {

    int from, to, speed;

    double time;

    status(int x, int x2, double y, int z): from(x), to(x2), time(y), speed(z) {}

    bool operator< (const status &b) const {

        return time > b.time;

    }

} Status;







int main() {

    // freopen("input.txt", "r", stdin);

 

    int ncities, nroads;

    while(scanf("%d%d", &ncities, &nroads) != EOF && !(ncities == 0 && nroads == 0)) {

 

        int visited[50][50][50];

        priority_queue< Status> Q;

 

        vector< vector<int> > path_cost(ncities + 1, vector<int>(ncities + 1, -1) );

        vector< vector<int> > path_limit(ncities + 1, vector<int>(ncities + 1, -1) );

 

        memset(visited, 0, sizeof(visited));

 

        // Input

        int start_index, end_index;

        scanf("%d%d" , &start_index, &end_index);

 

        for(int n = 0; n < nroads; ++n) {

            int x, y, d, c;

            scanf("%d%d%d%d", &x, &y, &d, &c);

            path_cost[x][y] = path_cost[y][x] = d;

            path_limit[x][y] = path_limit[y][x] = c;

        }

 

        double minimun_v = 0;

        Q.push(Status(start_index, start_index, 0.0, 0));

 

        while(!Q.empty()) {

            Status cs = Q.top();

            Q.pop();

 

            if(cs.to == end_index && cs.speed == 1) {

                minimun_v = cs.time;

                break;

            }

 

            if(visited[cs.from][cs.to][cs.speed]) continue;

 

            visited[cs.from][cs.to][cs.speed] = 1;

 

            for(int j = 1; j <= ncities; ++j) {

                if(path_cost[cs.to][j] == -1) continue;

                if(j == cs.from) continue;

                 

 

                for(int z = -1; z <= 1; ++z) {

                    if(!visited[cs.to][j][cs.speed + z] && cs.speed + z >= 1 && cs.speed + z <= path_limit[cs.to][j]) {

                        Q.push(Status(cs.to, j, cs.time + ((double)path_cost[cs.to][j])/(cs.speed + z), cs.speed + z));

                    }

                }

            }

        }

 

        if(minimun_v) {

            printf("%0.5lf\n", minimun_v);

        } else {

            printf("unreachable\n");

        }

    }

 

    return 0;

}

// Optimized solution to the same problem (faster):
#include <cstdio>

#include <cstring>

#include <vector>

#include <queue>

#include <algorithm>

using namespace std;

const int INFINITY_HAHA = 999999;



typedef struct edge

{

    int to, limit;

    double dist;

    edge(int x, double y, int z): to(x), dist(y), limit(z) {}

} Edge;





typedef struct status {

    int from, now, speed;

    double time;

    status(int x, int x2, double y, int z): from(x), now(x2), time(y), speed(z) {}

    bool operator< (const status &b) const {

        return time > b.time;

    }

} Status;







int main() {

    // freopen("input.txt", "r", stdin);

 

    int ncities, nroads;

    double d[35][35][35];



    while(scanf("%d%d", &ncities, &nroads) != EOF && !(ncities == 0 && nroads == 0)) {



        // Input

        int start_index, end_index;

        scanf("%d%d" , &start_index, &end_index);



        vector<Edge> paths[100]; // Array of vector

        for(int n = 0; n < nroads; ++n) {

            int x, y, d, c;

            scanf("%d%d%d%d", &x, &y, &d, &c);

            paths[x].push_back(Edge(y, d, c));

            paths[y].push_back(Edge(x, d, c));

        }



        for(int i = 0; i < 35; ++i) {

            for(int j = 0; j < 35; ++j) {

                for(int k = 0; k < 35; ++k) {

                    d[i][j][k]= INFINITY_HAHA;

                }

            }

        }



        double minimun_v = 0;

        priority_queue< Status> Q;

        for(int i = 0; i < paths[start_index].size(); ++i) {

            Edge e = paths[start_index][i];

            Q.push(Status(start_index, e.to, e.dist, 1));

            d[start_index][e.to][1] = e.dist;

        }



        while(!Q.empty()) {

            Status cs = Q.top();

            Q.pop();

 

            if(d[cs.from][cs.now][cs.speed] < cs.time) continue;



            if(cs.now == end_index && cs.speed == 1) {

                minimun_v = cs.time;

                break;

            }



            for(int j = 0; j < paths[cs.now].size(); ++j) {

                Edge s = paths[cs.now][j];

                if(s.to == cs.from) continue;



                for(int z = -1; z <= 1; ++z) {

                    if(cs.speed + z > 0 && cs.speed + z <= s.limit) {

                        double newdist = cs.time + s.dist/((double)(cs.speed + z));

                        if(newdist < d[cs.now][s.to][cs.speed + z]) {

                            d[cs.now][s.to][cs.speed + z] = newdist;

                            Q.push(Status(cs.now, s.to, newdist, cs.speed + z));

                        }

                    }

                }

            }

        }

 

        if(minimun_v) {

            printf("%0.5lf\n", minimun_v);

        } else {

            printf("unreachable\n");

        }

    }

 

    return 0;

}

// Fastest known solution for this problem: