views:

590

answers:

7

Not sure if that is the right way to ask this or not but here is the problem.

Given a latitude of 26.746346081599476, how do I find the number 26.75 as the 16th greater than the number and 26.6875 as the 16th lower than the number?

26.0
26.0625
26.125
26.1875
26.25
26.3125
26.375
26.4375
26.5
26.5625
26.625
26.6875
My Number: 26.746346081599476
26.75
26.8125
26.875
26.9375
27.0

I'm using JavaScript so an answer in that would be helpful but not necessary. I could brute force it but I'm looking for the elegant way to do it.

The bigger picture is that I want to create standard tiles for a mapping application I'm working on. We are using Bing maps and I am loading data on-demand, every time the user pans or zooms in. It would be nice to take advantage of server side caching for these requests so if I standardize the queries sent to the server I would get some cache hits. If I don't standardize the requests to the server it is highly unlikely that the same user would be viewing the exact some location at the same time.

So there is a higher chance of getting cache hits with: /path/data.json?tl=26.6875,-80.6875&br=26.75,-80.75 than with: /path/data.json?tl=26.74946187679896,-80.10930061340332&br=26.743234270702878,-80.09607195854187

Any outside the box answers are welcome as well.

+10  A: 

To find the nearest multiples of 1/n:

lower_bound = 1.0 / n * Math.floor(n * your_number);
upper_bound = 1.0 / n * Math.ceil(n * your_number);

You may want to use some special handling if your number is already a multiple of 1/16.

// alternate solution so that lower_bound <= your_number < upper_bound
lower_bound = 1.0 / n * Math.floor(n * your_number);
upper_bound = 1.0 / n * Math.floor(n * your_number + 1.0);
mobrule
Beat me to it. I almost got it working in PowerShell and always stumbled over round to even :)
Joey
@mobrule: Can you tell me why your way is better than Guffa's? I like that his is a little less code but I'm interested in how the special handling would help me here.
sheats
Depending on what you do with the bounds, you might not want to let `lower_bound == upper_bound`. For example you might perform some linear transformation of the box you drew around a coordinate, and you might have an expression with `(upper_bound - lower_bound)` in a denominator somewhere.
mobrule
A: 

JavaScript doesn't have a "round to X places" built-in function, but it's easy to do with the built-in "round to nearest integer" function:

function round(value, places) {
    var xten;

    xten = Math.pow(10, places);
    return Math.round(value * xten) / xten;
}
T.J. Crowder
It isn't a "round to *x* places", it's a "round to nearest multiple of 1/16"
Joey
Drat, missed that. :-) Still, building block to work with.
T.J. Crowder
+8  A: 

You multiply the value by 16, use the floor or ceil method, and divide by 16:

var higher = Math.ceil(number * 16) / 16;
var lower = Math.floor(number * 16) / 16;
Guffa
A: 

What is the smallest fractions you are interested in splitting? IE are 16ths going to be the smallest increments?

If yes, simply multiply your number by 16. Trunc it to an int and divide by 16 to find the lower bound. Trunc it to an int, add 1, then divide by 16 to find the upper bound.

David Oneill
+1  A: 

Sounds like rounding to the nearest 16th…

rounded = Math.round(number * 16) / 16;

You could get numbers that aren't exact because of the float representation, but that shouldn't matter in your case if you use it just for caching.

Georg
A: 

A couple of strategies not posted so far:

A) create a lookup table that maps the digits after the decimal to the nearest 16th. Just use whatever precision you need (probably hundredths).

B) create a table of all the 16ths from 0 to 1, and do a binary style search with your number % 1.

patros
+1  A: 
function bounds(number, numerator, denominator) {
  var frac = denominator/numerator;
  return {
    lower: Math.floor(frac * number) / frac,
    upper: Math.ceil(frac * number) / frac,
  }
} 


bounds(26.746346081599476,1,16)
// returns an object with properties
// lower : 26.6875 
// upper : 26.75
Russ Cam