tags:

views:

132

answers:

5

I have a float value that must be constrained to an multiple of 0.25.

Examples of valid values: 1.0, 1.25, 2.0, 2.5, 20.25, 20.5, 21.0, 21.25, ...

Examples of invalid values: 0.93, 3.31, 4.249, 5.02, ...

Is there an mathematic function or something convenient to achieve this? When the value is invalid, I would round it up to the nearest valid value.

+5  A: 

Well, to achieve the results you want...

float input = ????;

float desiredValue = ceilf(input*4.0f)/4.0f;

I think ceilf() is part of C99, so make sure you're compiler is set to it.


[EDIT]

While this doesn't test for input == multiple of 1/4, it'll perform the same operation. Numbers which are a multiple of 1/4 won't be altered. @whatnick brings up an excellent point that floating point comparison is just unreliable.

Plus, there's a small performance boost for not having the test/jump. small boost.

Stephen Furlani
Regarding performance: You have two function calls (`ceilf` and a divide since division is done in software on ARM)--This will likely be slower than a branch.
rpetrich
@rpetrich, well, yes, but the fact that a good 90% or more of the time the `if` statement will fail, resulting in the calls to the same functions anyway.
Stephen Furlani
You actually get a bigger performance boost from avoiding floats all together just because you don't have to do float arithmetic. Comparing exponents before adding/subtracting mantissas and off-setting the bit patterns gets really ugly.
steven_desu
The user asked for a formula or convenient method. Not necessarily performance. I put that comment in as an aside, not a descriptive thesis on performance of functions. Sheesh.
Stephen Furlani
+1  A: 

Floating point comparisons are not accurate and a lot may be lost in calculation. Try to stick with integers if you can. Here is the classic floating point link.

whatnick
-1. While floating point *calculations* are potentially inaccurate, IEEE floating-point *comparisons* are entirely well-specified. The biggest source of trouble seems to be with `gcc -mfpmath=x87`, but this is not a problem on iDevice (ARM) or on the simulator (-mfpmath=sse is default on x86).
tc.
@tc, @whatnick refers to the problem of comparing floating point integers. I guarantee you that the following will always produce a false result: `const double x = 0.05; const double y = 0.07; const double z = x + y; const double key_num = 0.12; if( key_num == z ) { std::cout << "==\n"; } else { std::cout << "!=\n"; }`
Stephen Furlani
+2  A: 

You could simply use an integer representing four times the needed value. That way, it cannot ever be invalid.

int repr = 5; // Represents 5 * 0.25 = 1.25
float value = repr / 4.0; // 1.25; valid, by definition
calmh
that's a good point!
openfrog
+1  A: 

What functions are available to you depends on the language you're using. Is this C? C++? Java? In any case, the "best" solution depends on how you're getting the value to check. Are you mathematically computing the value and you then want to make sure it's a multiple of 0.25? Or is a user inputting a value and you then want to make sure it's a multiple of 0.25?

If you're mathematically calculating it, just multiply all of your calculations by 4, use integer math, then divide the result by 4.0. This guarantees a result that is a multiple of 0.25

If a user is entering a value then you can't just convert it to an integer without risking losing the decimal. The "best" solution in this case would be to look for an existing ceiling function. Multiply their number by 4, take the ceiling, then divide by 4. This will round up to the nearest 0.25. Calculating a "ceiling" directly is an infinite series and would thus leave your program running forever (until you ran out of significant digits of accuracy and you were infinitely adding 0). If there is no "ceiling" function in the language you're using, most languages posses a modulus function. This modulus actually contains the exact same infinite series used in calculating a ceiling, so through substitution we can use it.

ceiling(x) = floor(x - 2*(x%1) + 1)

Remembering that floor(x) = (int)(x), we can just cast "x-2*(x%1)+1" as an int and we have the ceiling.

If your language doesn't have a modulus OR a ceiling function then for many practical purposes with sufficient significant digits you can add 1 and cast it as an int, but this will not work in cases of exact integers. Alternatively you could add something like 0.99 then cast it as an int, as this will only fail for numbers greater than n and less than n+0.01 (for any integer n). The more 9's you append the greater accuracy of the approximation (until you have more 9's than the computer can store, then it will round to 1 and you'll have the original problem)

steven_desu
-1. `%` only works for integers in (Objective-)C. There's also no reason you need an "infinite series" to calculate a ceiling: `floor(x) == x ? x : floor(x)+1`.
tc.
I suppose I should have specified. You need an infinite series to calculate it strictly mathematically. I was trying to create the ceiling() function sans conditionals =) Not sure why, again... When I read the problem before I felt like there should be a solution without any control logic.
steven_desu
+1  A: 

Every integer is a multiple of 0.25 so drop the integer part (fVal - Int(flval). Now verify that the decimal part is either equal to 0, 0.25, 0.5 or 0.75.

Simon T.