4

I can't even find search keywords for this. Please consider this code:

float inputValue = getInputValue();
float resultValue;

if (inputValue < 0.1f) {
    resultValue = 0.01f;
}
else if (inputValue < 1.0f) {
    resultValue = 0.1f;
}
else if (inputValue < 10.0f) {
    resultValue = 1.0f;
}
else {
    resultValue = 10.0f;
}

and so on. There must be a more elegant way to do this. I guess the solution is easy, but I try to find a way now for 2 hours and read about round, ceil, floor...can't find it.

Has anyone an idea?

user1840267
  • 408
  • 1
  • 5
  • 18

4 Answers4

6
powf(10.0f, floorf(log10f(value)))
Spook
  • 25,318
  • 18
  • 90
  • 167
  • No need for the `double`s but, yes, I think this is what the author was asking for rather than what I've posted. – Tommy Mar 18 '14 at 21:40
  • Edited, I just posted pseudocode as an idea. Now it should compile correctly in C++ (I forgot, that C++ actually does have 10-based logarithm in math.h) – Spook Mar 18 '14 at 21:41
  • Works perfectly. Not as easy as I thought, I doubt I would have found that :) Thanks! – user1840267 Mar 18 '14 at 21:49
  • Some credit goes to @Tommy - I forgot about `floorf` and `log10f` functions in math.h. But I'm glad I could help anyway:) – Spook Mar 18 '14 at 21:51
  • Thank you too, Tommy! Has one of you guys an idea how to improve the question title? I'm afraid noone with the same problem will find this solution... – user1840267 Mar 18 '14 at 21:59
  • 1
    How about, "How to round down to the nearest power of 10?" – pat Mar 19 '14 at 00:17
  • Code would handle negative numbers with `powf(10.0f, floorf(log10f(fabsf(value))))`. – chux - Reinstate Monica Oct 31 '16 at 19:11
  • 1
    For edge cases, code likely fails for large values like `value=99999997952` which returns 99,999,997,952 rather than 10,000,000,000. Both of these values are exactly representable as `float`. Values near various negative powers of 10 also likely fail. E.g with `float f = 9.99999975e-03`, this code returns `9.99999975e-03` rather than a value close to `1.0e-03`. Using `double` has the same problem albeit more rare and with different values. – chux - Reinstate Monica Oct 31 '16 at 19:31
  • As the maximum precision of float is rather limited, I strongly recommend to use the double precision (default) version of `log10()`, `floor()` and `pow()`. To avoid the usual rounding problems with floating point values, I also strongly recommend to add a small offset (like `1e-9`) before doing the `floor()`. So my recommended solution is: `pow(10.0, floor(1e-9 + log10(value)))`. On linux libc, `exp10(floor(1e-9 + log10(value)))` is a tiny bit faster. – Kai Petzke Sep 23 '21 at 11:31
2

I'm sure there's a much simpler way that doesn't try to work forward from first principles, but how about:

float inputValue = getInputValue();

float log10 = floorf(log10f(inputValue));
float modulo = powf(10.0f, log10);
inputValue -= fmodf(inputValue, modulo);

EDIT: actually, I think I've assumed you'd want 230 to round to 200, 0.73 to round to 0.7, etc, so this probably isn't the answer. I'm leaving it up on the basis that it may be helpful regardless.

Tommy
  • 99,986
  • 12
  • 185
  • 204
1

Your code isn't doing what you think it does. For any value less than 100 (including 0.001), resultValue will be set to 10. You'd need to check in the opposite order.

I'd start by writing down the spec for this function: Exactly what output values do you expect for what input values? Do you expect 1.0e17 for an input of 1.01e17? What if the input is 0, or -1?

gnasher729
  • 51,477
  • 5
  • 75
  • 98
0

Here is a BigInteger version. I needed the next (rather than nearest) power of 10, but you see the idea:

import java.math.BigInteger;

class A {
    public static void main(String[]args) {
        System.out.println("hi");
    for (long v : new long[] {0,1,2,9,10,11,99,100,101,12345678,123456789012345678L,1234567890123456789L}) {
            System.out.println(""+v+" ==> "+nextPowerOf10(v));
        }
    }
    static long nextPowerOf10(long v) {
        return BigInteger.TEN.pow((int)Math.max(0,1+Math.floor(Math.log10(v)))).longValue();
    }
}

If you want it to behave differently on powers of 10, play with 1+Math.floor above (0/1, floor/ceil/round). By the way, what OP meant under "nearest?" Nearest on the linear or on the logarithmic scale? Nearest greater or nearest smaller?

>java A
hi
0 ==> 1
1 ==> 10
2 ==> 10
9 ==> 10
10 ==> 100
11 ==> 100
99 ==> 100
100 ==> 1000
101 ==> 1000
12345678 ==> 100000000
123456789012345678 ==> 1000000000000000000
1234567890123456789 ==> -8446744073709551616
18446744073709551615
  • 16,368
  • 4
  • 94
  • 127