//http://drken1215.hatenablog.com/entry/2018/07/18/214400
//を参考
#include<bits/stdc++.h>
using namespace std;
typedef vector<int> vi;
const int MAXN=30;
const int INF=1e9;
double dp[MAXN+5][MAXN+5][MAXN+5];// 現在地点,直前の地点,今の速度
int n,m;
double ans=INF;
template<typename A,size_t N,typename T>
void Fill(A (&array)[N],const T &val)
{
  std::fill( (T*)array,(T*)(array+N),val );
}

struct State
{
  double cost;
  int now,prev,speed;
  State(double c,int n,int p,int s):cost(c),now(n),prev(p),speed(s){}//コスト,現在地,直前の地点,今の速度

  bool operator < (State s)const 
  {
    return s.cost<cost;
  }
};

struct Edge
{
  int to,dist,limit;
  Edge(int t,int d,int l):to(t),dist(d),limit(l){}
};

vector<Edge> Graph[MAXN+5];

int main(void)
{
  while(cin >> n >> m && n)
    {
      int s,t;
      cin >> s >> t;
      for (int i = 0; i <= MAXN; ++i)
	{
	  for (int j = 0; j <= MAXN; ++j)
	    {
	      for (int k = 0; k <= MAXN; ++k)
		{
		  dp[i][j][k]=INF;
		}
	    }
	}
      for(int i=0;i<MAXN+5;i++)
	{
	  Graph[i].clear();
	}
      for (int i = 0; i < m; ++i)
	{
	  int x,y,d,c;
	  cin >> x >> y >> d >> c;
	  Graph[x].push_back(Edge(y,d,c));
	  Graph[y].push_back(Edge(x,d,c));
	}
      ans=INF;
      priority_queue<State> que;//上のStateと持つ状態は同じ
      que.push(State(0.0,s,0,1));
      dp[s][0][1]=0.0;
      while(!que.empty())
	{
	  auto state=que.top();que.pop();
	  int nowpoint=state.now;
	  int prevpoint=state.prev;
	  int nowspeed=state.speed;
	  double nowcost=state.cost;
	  for (int i = 0; i < int(Graph[nowpoint].size()); ++i)
	    {
	      for(int j=-1;j<=1;j++)
		{
		  if(prevpoint==0)
		    {
		      if(j==-1 || j==1) continue;
		    }
		  int nextspeed=nowspeed+j;
		  int nextpoint=Graph[nowpoint][i].to;
		  int nextlimit=Graph[nowpoint][i].limit;
		  double gocost=(double)Graph[nowpoint][i].dist/(double)nextspeed;
		  if(nextpoint==prevpoint) continue;
		  if(nextspeed>nextlimit) continue;
		  if(dp[nextpoint][nowpoint][nextspeed]>nowcost+gocost)
		    {
		      dp[nextpoint][nowpoint][nextspeed]=nowcost+gocost;
		      que.push(State(dp[nextpoint][nowpoint][nextspeed],nextpoint,nowpoint,nextspeed));
		    }
		}
	    }
	}
      for (int i = 1; i <= n; ++i)
	{
	  if(dp[t][i][1]<ans) ans=dp[t][i][1];
	}
      if(ans==INF) cout << "unreachable" << endl;
      else cout << fixed << setprecision(12) << ans << endl;
    }
  return 0;
 
}

