#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

// (x,y)
typedef pair<int,int> P;
// ( テゥツ?淌・ツコツヲ , (テ・ツ可催」ツ?ョテァツ板コ,テ、ツサツ甘」ツ?ョテァツ板コ) )
typedef pair<int,P> Node;
// (cost,Node)
typedef pair<double,Node> State;

const int MAX_V = 31;
const double INF = 1e200;

struct edge{
	int to, d, c;
	edge(int to_, int d_, int c_){
		to = to_; d = d_; c = c_;
	}
};

// テ」ツつーテ」ツδゥテ」ツδ?
vector<edge> G[MAX_V];
// テ」ツつケテ」ツつソテ」ツδシテ」ツδ暗」ツ?凝」ツつ嘉」ツつエテ」ツδシテ」ツδォテ」ツ?ォティツセツソテ」ツつ甘」ツ?、テ」ツ?妥」ツつ凝」ツ?凝」ツ?ゥテ」ツ??」ツ??
bool memo[MAX_V];
bool is_connect;

// テ・ツ?・テ・ツ環?
int n, m;

// テ・ツ按敕ヲツ慊淌・ツ個?
void init(){
	is_connect = false;
	for(int i=0 ; i < MAX_V ; i++ ){
		G[i].clear();
	}
}

// ティツセツコテ」ツ?ョティツソツステ・ツ環?
void add_edge(int x, int y, int d, int c){
	edge e( y , d , c );
	G[x].push_back( e );
	e.to = x;
	G[y].push_back( e );
}

// テ」ツつケテ」ツつソテ」ツδシテ」ツδ暗」ツ?凝」ツつ嘉」ツつエテ」ツδシテ」ツδォテ」ツ?ォティツセツソテ」ツつ甘」ツ?、テ」ツ?妥」ツつ凝」ツ?凝」ツ?ゥテ」ツ??」ツ?凝」ツδ?」ツつァテ」ツδε」ツつッ
void check(int v, int g){
	if( v == g ){
		is_connect = true;
	}
	if( memo[v] ) return;
	memo[v] = true;
	
	for(int i=0 ; i < G[v].size() ; i++ ){
		int to = G[v][i].to;
		if( memo[to] ) continue;
		check( to , g );
	}
}

// テ」ツδ?」ツつ、テ」ツつッテ」ツつケテ」ツδ暗」ツδゥテヲツウツ?
double solve(int s, int g){
	// d[v][x][y] := テゥツ?淌・ツコツヲv, テ・ツ可催」ツ?ョテァツ板コx, テ、ツサツ甘」ツ?ョテァツ板コyテ」ツ?セテ」ツ?ァテ」ツ?ョテヲツ慊?・ツーツ湘」ツつウテ」ツつケテ」ツδ?
	double d[31][31][31];
	// ティツィツェテ」ツつ古」ツ?淌」ツ?凝」ツ?ゥテ」ツ??」ツ??
	bool visited[31][31][31];
	// テ・ツ按敕ヲツ慊淌・ツ個?
	for(int y=0 ; y < MAX_V ; y++ ){
		for(int x=0 ; x < MAX_V ; x++ ){
			for(int v=0 ; v < 31 ; v++ ){
				d[v][y][x] = INF;
				visited[v][y][x] = false;
			}
		}
	}
	
	// State := (cost,Node), Node := ( テゥツ?淌・ツコツヲ , (テ・ツ可催」ツ?ョテァツ板コ,テ、ツサツ甘」ツ?ョテァツ板コ) )
	priority_queue< State, vector<State> , greater<State> > q;
	for(int i = 0 ; i < G[s].size() ; i++ ){
		int to = G[s][i].to;
		double dis = G[s][i].d;
		
		Node start( 1 , P(s,to) );
		q.push( State(dis,start) );
		d[1][s][to] = dis;
	}
	
	double ans = INF;
	int cnt=0;
	while( !q.empty() ){
		//cnt++;
		//if( cnt >= 2000000 ) break;
		
		// テ・ツ?、テ」ツつ津・ツ渉姪」ツつ甘・ツ?コテ」ツ??
		double now_cost = q.top().first;
		int v = q.top().second.first;
		int x = q.top().second.second.first;
		int y = q.top().second.second.second;
		q.pop();
		
		if( now_cost >= ans || visited[v][x][y] ) continue;
		visited[v][x][y] = true;
		
		// テァツ崢ョテァツ堋?・ツ慊ーテ」ツ?ョテ」ツ?ィテ」ツ??
		if( y == g && v == 1 ){
			ans = min( ans , now_cost );
			break;
		}
		
		for(int i=0 ; i < G[y].size() ; i++ ){
			int to = G[y][i].to;
			int dis = G[y][i].d;
			int c = G[y][i].c;
			if( to == x ) continue;
			
			for(int dv = -1 ; dv <= 1 ; dv++ ){
				int next_v = v + dv;
				if( next_v > c || next_v <= 0 ) continue;
				
				double cost = (double)dis / next_v;
				if( now_cost + cost < d[next_v][y][to] ){
					d[next_v][y][to] = now_cost + cost;
					
					Node next( next_v , P(y,to) );
					State p( now_cost + cost , next );
					q.push( p );
				}
			}
		}
	}
	return ans;
}

int main(){
	while( scanf("%d %d", &n, &m) , n || m ){
		// テ・ツ按敕ヲツ慊淌・ツ個?
		init();
		
		int s, g;
		cin >> s >> g;
		
		for(int i=0 ; i < m ; i++ ){
			int x, y, d, c;
			scanf("%d %d %d %d", &x, &y, &d, &c );
			add_edge( x , y , d , c );
		}
		
		{
			// テ」ツつケテ」ツつソテ」ツδシテ」ツδ暗」ツ?凝」ツつ嘉」ツつエテ」ツδシテ」ツδォテ」ツ?ォティツセツソテ」ツつ甘」ツ?、テ」ツ?妥」ツつ凝」ツ?凝」ツ?ゥテ」ツ??」ツ??
			for(int i=0 ; i < MAX_V ; i++ ){
				memo[i] = false;
			}
			check( s , g );
		}
		
		// テ」ツつエテ」ツδシテ」ツδォテ」ツ?ォティツセツソテ」ツつ甘」ツ?、テ」ツ?妥」ツ?ェテ」ツ??」ツ?ィテ」ツ??
		if( is_connect == false ){
			puts("unreachable");
		}else{
			double ans = solve( s , g );
			printf("%.4f\n", ans );
		}
	}
}

Measured memory_bytes: 2016