3

From a given double I want to get the next highest number according to some rules which, since I have some difficulty describing them, I will illustrate by examples:

Input      Desired output
-------    --------------
   0.08         0.1
   0.2          0.5
   5           10
   7           10
  99          100
 100          500
2345         5000

The output should be in some sense the 'next highest multiple of 5 or 10'.

I hope this is understandable; if not, let me know.

The implementation will be in java and input will be positive doubles.

AakashM
  • 62,551
  • 17
  • 151
  • 186
clamp
  • 33,000
  • 75
  • 203
  • 299
  • Are all your input numbers positive? – Sven Marnach Nov 23 '11 at 15:28
  • 2
    What should `function(1e-6)` evaluate to? Trick question – the representable double closest to `1e-6` is not `1e-6` but a number slightly smaller, so `function(1e-6)` arguably should evaluate to `1e-6`. If this possibility freaks you out, you should be using `java.math.BigDecimal` or an equivalent. – Per Nov 24 '11 at 06:03

4 Answers4

4
function top5_10 (x) {
  var ten = Math.pow(10, Math.ceiling(Math.ln(x)/Math.LN10)));
  if (ten > 10 * x) { ten = ten / 10; }
  else if (ten <= x) { ten = 10 * ten; }
  return x < ten / 2 ? ten / 2 : ten;
}

or something like this :-)

2

Here's a function that works on the sample data:

def f(x):
    lx = log10(x)
    e = floor(lx)
    if (lx - e) < log10(5):
        return 5 * 10 ** e
    else:
        return 10 ** (e+1)
outis
  • 75,655
  • 22
  • 151
  • 221
2

Pseudo code should be something like this:

If number > 1
    n = 1
    While(true)
        If(number < n)
            return n
        If(number < n*5)
            return n*5
        n = n*10
Else
    n = 1.0
    While(true)
        If(number > n/2)
            return n
        If(number > n/10)
            return n*2
        n = n/10.0

For numbers > 1, it checks like this: if < 5, 5. if <10, 10, if < 50, 50. For numbers < 1, it checks like this: if > 0.5 1. if > 0.1, 0.5. etc.

holgac
  • 1,509
  • 1
  • 13
  • 25
0

If you intend to use doubles and need precise result, all methods using double-precision multiply/divide/log10 are not working (or at least are hard to implement and prove correctness). Multi-precision arithmetic might help here. Or use search like this:

powers = [1.e-309, 1.e-308, ..., 1.e309]
p = search_first_greater(powers, number)
if (number < p / 2.) return p / 2.
return p

search_first_greater may be implemented as:

  • linear search,
  • or binary search,
  • or direct calculation of the array index by n=round(log10(number)) and checking only powers[n-1 .. n]
  • or using logarithm approximation like cutting the exponent part out of the number and checking 4 elements of powers[].
Evgeny Kluev
  • 24,287
  • 7
  • 55
  • 98
  • *all methods using multiply/divide/logarithm are not working* +1 for being right about the currently posted answers (at least in theory), -1 for a cargo cult approach to floating point – the log function, while transcendental, won't give you completely random answers. – Per Nov 24 '11 at 05:03
  • @clamp They are perfectly working if all arithmetic is performed by some multiprecision library. Otherwise (for double precision arithmetic) they may give wrong result if the input value is close to a multiple of 5 or 10: calculations are usually inexact in the last one or several bits, so when comparing input value to some inexact result (or using floor/round/ceiling), you can get wrong result, say, 1 instead of 5. – Evgeny Kluev Nov 28 '11 at 11:14
  • @clamp Best answer to "why exactly are those methods not working" you may get yourself after performing a proper set of unit tests. – Evgeny Kluev Nov 28 '11 at 11:25
  • @Evgeny thank you. so i guess this is because of numerical errors. in my specific case the input values can not get closer than 4 decimal places. e.g. 34.9999 – clamp Nov 28 '11 at 13:24
  • @clamp Right. For your specific case herby's and outis' variants should give enough precision, revani's variant still have very small probability to work incorrectly. – Evgeny Kluev Nov 28 '11 at 14:52