Floating point precision, in practice

Normally as a programmer Float’s are more than precise enough. During one of my projects however I had some serious headaches because of it. At start of the project I was rolling my own points in 3D-space, just a fancy wrapper around 3 Doubles. When I needed more vector powers (cross products, quaternations, rotation matrices) I switched to com.github.jpbetz.subspace.Vector3 from https://github.com/jpbetz/subspace. This Vector3 uses 3 Floats internally, which was fine, cause it wouldn’t make that much of a difference, I thought.

The project I’m working on includes fitting some points to a predefined shape (a inline skate track to be precise). I was calculating the center of the GPS input like this:

val vs: Seq[Vector3] = /* about 3600 points */ ???
val center = vs.reduce(_ + _) / vs.size

Using this center I plotted a blue ‘perfect’ track and my input points in green. The result:
GPX points wrongly centered

As you can see there is some offset between the green and the blue tracks. The orientation is different too, but first I wanted to fix the positioning problem. After many hours of thinkering and continuing with other issues, I came across another issue with my Floats: the formatting back to GPS longitude and latitudes. For some reason I wrongly typed the formatting string, but due to this issue my attention shifted to floating point precision and it’s quirks. Then I wrote this little test:

val sum_d = vs
  .map(v => (v.x.toDouble, v.y.toDouble, v.z.toDouble))
  .reduce((a,b) => (a._1+b._1, a._2+b._2, a._3+b._3))
val center_d = Vector3(
  (sum_d._1/vs.size).toFloat,
  (sum_d._2/vs.size).toFloat,
  (sum_d._3/vs.size)).toFloat
)
var center = vs.reduce(_ + _) / vs.size
println(s"Center: $center")
println(s"Center using doubles: $center_d")
println(s"Diff: ${center - center_d}")

which printed

Center: (3912099.2, 301133.1, 5028436.0)
Center using doubles: (3912057.8, 301132.88, 5028494.5)
Diff = (41.5, 0.21875, -58.5)

Tada! Issue found. Adding all those large numbers (vectors relative to the earths center) caused the precision of the resulting float to be too little to accomodate for the large numbers plus their precision. Using doubles to store the accumulating numbers results in a better center, which produces the following image:

GPX points correctly centered

Much better! So remember, floats are not always good enough.

Strava, inline skating heatmaps

Inline skating is one of my favourite things to do during summer, and I love to explore new routes. With skates however the road quality is very important. If the asphalt has too much gravel the skates shake heavily and the experience is awful.

Strava, logging activities for cycling and running has had Heatmaps for quite some time. Also their Route Planner integrates the same data and uses *popularity* to snap routes to frequently traveled roads. However, this uses cycling and running data only.

heatmaps

Wouldn’t it be nice if Strava adds the option to view Heatmaps for inline skating too? And plan routes and use popularity of roads for a generic sport instead of just cycling and running? I think so!

route-planner

I contacted Strava and will update this blog with reactions I receive. Fingers crossed!