If I had realized this thread was a sticky I'd have posted my findings here to begin with. It's actually a little more complicated than this. The game is run in "ticks" (equivalent to frames), the speed is measured in pixels per tick, and you can't have a fraction of a tick; the layer will move a distance equal to the speed*ticks. 1 block length is equal to 32 pixels.
What makes it dicey is that the number of ticks are a bit off from what is expected.
0.1 second = 7 ticks
0.2 = 14 ticks
0.3 = 21 ticks
0.4 = 27 ticks
0.5 = 33 ticks
0.6 = 40 ticks
0.7 = 47 ticks
0.8 = 53 ticks
0.9 = 59 ticks
1.0 = 66 ticks
1.1 = 73 ticks
1.2 = 79 ticks
1.3 = 85 ticks
1.4 = 92 ticks
1.5 = 99 ticks
1.6 = 105 ticks
1.7 = 111 ticks
1.8 = 118 ticks
1.9 = 125 ticks
2.0 = 131 ticks
8.0 = 521 ticks
No wonder it's so hard to prevent cycling layers from getting farther and farther off if you don't use the same speed and delay both ways. A 2 second delay isn't even the same as two 1-second delays!
The number of ticks for a given number of seconds appears to be:
(seconds*65)+1, rounded to the nearest ODD number if it ends in .5
Normally .5 is rounded to the nearest even number, but presumably it's rounded before the 1 is added, resulting in it rounding towards odd numbers instead.
In that case the distance moved (in pixels) is:
((seconds*65)+1, rounded to the nearest ODD number if it ends in .5)*speed
The extra 1 is what tends to throw off the timing of layers. For example, a speed of -1 for 2 seconds and a speed of 2 for 1 second moves the layer -131 and then +132, causing it to go further off by 1 each cycle.
However, moving it by -1 for 1 second and +2 for 0.5 seconds does work because the number of ticks for 1 second is twice the number for 0.5 seconds. Another solution is to split up the first delay, so you have the layer moving -1 for 1 second, then -1 for 1 second again, then +2 for 1 second.