# Best way to see if a number is an integer

## Recommended Posts

I feel like an idiot asking this but I don't think I ever found out the best way to see if a number is an integer and the darn GameDev search is down again (Does anyone know why? I really miss it when it's down). Meaning, if we have
#include <cmath>

int main()
{
float number1 = std::sqrt(36.0f);
float number2 = std::sqrt(19.0f);

return 0;
}


What's the best method for seeing if number1 and number2 are integers? I've been using a float and integer cast hack, but it's very ugly and there's got to be a better solution.

##### Share on other sites
Well, since most numbers aren't able to be represented exactly in floating point format you're best bet is to see if the real floating point value is within an acceptable range with respect to the nearest integer value.

Something like:

if(value - FLOOR(value) < epsilon || CIELING(value) - value < epsilon)
return true;

return false;

Where epsilon is your acceptable range.

##### Share on other sites
const float EPSILON = 0.00001f; bool IsInt(float f){	return (fabsf(f - (float)(int)(f + EPSILON)) < EPSILON);}

##### Share on other sites
bool IsInt( float Test ){if( float( Test ) == int( Test ) )return true;elsereturn false;}

It's a bit of a slow-down from the cast, don't think it'll cause too much overhead though. If you don't need too much precision, then that's what I'd suggest, if you're doing some funky stuff to the float though, you're going to have some rounding problems and should prolly use the first answer

- Jordan

##### Share on other sites
My bad. Above post is mine.

EDIT: Eh... Nevermind. You should prolly use the above examples, mine's pretty flakey. [smile]

- Jordan

##### Share on other sites
For what purpose is this needed anyway? I'm supposing that your sqrt() example was merely that, an example. If you need 100% reliable mathematical accuracy, then you're probably going to have to really on more mathematical methods, such as keeping rational numbers in exact, fractional form (integer over integer, rather than floating point), keeping irrational numbers in their representative form (sqrt(2) stays as that, not as 1.4142...), etcetera. Which means that for things such as sqrt(36), you're just going to have to know that it is exactly 6, or write something to factor it into its prime components. Things like that.

##### Share on other sites
This is off topic, but if the gdnet search is down, you can use google and type "<your search term(s)> site:gamedev.net" (without quotes or angle brackets)

Regards,
jflanglois

##### Share on other sites
Quote:
 Original post by RavyneWell, since most numbers aren't able to be represented exactly in floating point format you're best bet is to see if the real floating point value is within an acceptable range with respect to the nearest integer value.Something like:if(value - FLOOR(value) < epsilon || CIELING(value) - value < epsilon) return true;return false;Where epsilon is your acceptable range.

Quote:
 Original post by Aprosenfconst float EPSILON = 0.00001f; bool IsInt(float f){ return (fabsf(f - (float)(int)(f + EPSILON)) < EPSILON);}

This method looks good, thanks.

Agony: The sqrt example was actually the thing I needed it for, but I'll definitely need to use it for more complex things eventually. Thanks everybody.

EDIT:
jflanglois: The google search doesn't as well as GameDev's but it's passable. I nearly forgot google can do that :]

##### Share on other sites
FYI
maybe it was just me but I remember once that FLOOR/CEIL ran incredibly slowly on my machine (considering what they did).. so the fabsf version is likly a lot better.

But then again this was a long time ago, and this predudice has stuck so I havn't tried it since [smile]

##### Share on other sites
Float to int is always a slow opperation. I believe theres finally a hardware instructuion for it in SSE3 though. Theres all sorts of interesting mathematical tricks to make it go as fast as possible though. Google for "fast float to int conversion" some time :)

##### Share on other sites
Quote:
 Original post by RavyneFloat to int is always a slow opperation.

Okay, this is true in a general case... but it's not because the conversion itself is slow. Basically, the processor has several different rounding modes... the default is "round to nearest", but the C-standard requires "truncate towards zero". So the C-cast method requires the processor to save the current rounding state, set the rounding state to "truncate toward zero", perform the conversion, then restore the previous rounding state, all of which takes a good deal longer than simply doing the conversion.

If it just needs to be exact then the rounding mode doesn't matter... simply convert one way, then covert back, and check the result, it is actually fairly quick to do in assembly.
int is_int(float num){    int result = 0;    int true_val = 1;    __asm {        cvtss2si eax,   num        // round to int into eax        movss    xmm0,  num        // move float into xmm0        cvtsi2ss xmm1,  eax        // convert int back to float in xmm1        cmpss    xmm0,  xmm1, 0    // test xmm0 vs xmm1        movmskps eax,   xmm0       // eax != 0 if they are equal        cmp      eax,   0                  cmovne   eax,   true_val   // make sure result == 1 if equal, 0 otherwise.        mov      result, eax           }    return result;}

this is the fastest way to do it mentioned... but I don't think speed is a concern. To test with a threshold value is similar, but the rounding mode must be saved and restored.

##### Share on other sites
Quote:
 this is the fastest way to do it mentioned... but I don't think speed is a concern. To test with a threshold value is similar, but the rounding mode must be saved and restored.

Just curious... have you timed your converting function? If yes, what times have you got? I wonder whether it's faster then FloatToIntPositive/FloatToIntNegative from the code below (it's not my, I've taken it from GPG and modified sligthly).
And does it work for all numbers (regardless sign)?
If it's faster then I'll probably use your version :-)

I've timed mine and it looks that FloatToIntPositive/FloatToIntNegative are 5x times faster than casting and FloatToInt is only 2x faster then casting.

typedef union  {    int     i;          // as integer    float   f;          // as float  } INTORFLOAT;INTORFLOAT ftoibiasp = {((23 + 127) << 23)};INTORFLOAT ftoibiasn = {((23 + 127) << 23) + (1 << 22)};INTORFLOAT ftmp; // ------------------------------------------------- /*! they all _round_ to nearest integer, not throw away values after decimal point (like normal casting do)ie.0.1 -> 0 0.2 -> 00.3 -> 00.4 -> 00.5 -> 10.6 -> 10.7 -> 10.8 -> 10.9 -> 11.0 -> 1*/// ------------------------------------------------- start FloatToIntPositiveunsigned long int SC :: FloatToIntPositive(float _f) {      ftmp.f = _f;  ftmp.f += ftoibiasp.f;  ftmp.i -= ftoibiasp.i;  return ftmp.i;   } // ------------------------------------------------- end FloatToIntPositive// ------------------------------------------------- start FloatToIntNegative inline signed long int SC :: FloatToIntNegative(float _f) {     ftmp.f = _f;  ftmp.f += ftoibiasn.f;  ftmp.i -= ftoibiasn.i;  return ftmp.i;   } // ------------------------------------------------- end FloatToIntNegative// ------------------------------------------------- start FloatToInt signed long int SC :: FloatToInt(float _f) {     return ( (_f < 0) ? (FloatToIntNegative(_f)) : (FloatToIntPositive(_f)) ); } // ------------------------------------------------- end FloatToInt

[Edited by - Koshmaar on December 4, 2004 1:09:21 PM]

##### Share on other sites
Okay, this was a really fun problem :)

I optimized my code, I guess as much as I possibly can. Then I wrote a looping version that processes a stream of floats and writes to a stream of results.

The results were (tested with pipeline simulation on AMD code analyst): My single iteration version runs in 15 cycles. The code you posted runs in 14 cycles (if you don't count branch mis-prediction) and my loop-optimized code runs in 11 cycles per iteration.

I wrote the code to test over 100 floats, 1/3 of which were float version of integers, and 1/3 of which were negative. Your version suffered 28 branch-mispredictions, which cost 30 cycles each, bringing the average value to 21 cycles. My code suffered only the single unavoidable branch-mispredict at the end of the loop.

Here is the code and test harness:

void is_int(float* num, int* result){    __asm {	mov	 edx,	[num]	mov	 esi,   [result]        cvtss2si eax,   [edx]        // round to int into eax        movss    xmm0,  [edx]        // move float into xmm0        cvtsi2ss xmm1,  eax        // convert int back to float in xmm1        cmpss    xmm1,  xmm0, 0    // test xmm0 vs xmm1        movss	 [esi], xmm1       // eax != 0 if they are equal	and		dword ptr[esi],   1    }}void is_int(float* num, int* result, int count){	if(count <= 0)		return;	if(--count == 0)	{		is_int(num, result);		return;	}    __asm {	mov	 edx,	[num]	mov	 esi,	[result]        cvtss2si eax,   [edx]        // round to int into eax        movss    xmm0,  [edx]        // move float into xmm0start_loop:	add	 edx,	4	cvtsi2ss xmm1,  eax	cvtss2si eax,	[edx]        cmpss    xmm1,  xmm2, 0    // test xmm2 vs xmm1	movss	 xmm0,	[edx]        movss	 [esi], xmm1       // xmm1 is 0xffff if equal, 	                movss	 xmm2,  xmm0       // free up register 0need to mask	and	 dword ptr[esi], 0001h	add	 esi,	4	sub	 count, 1	jnz	 start_loop        cvtsi2ss xmm1,  eax        // convert int back to float in xmm1        cmpss    xmm1,  xmm0, 0    // test xmm0 vs xmm1        movss	 [esi], xmm1       // xmm1 is 0xffff if equal, need to mask	and	 dword ptr[esi],   1    }}////////////////////////////////////////////////////////////////////The code you posted along withvoid is_intSC(float* num, int* result){	*result = FloatToInt(*num) == *num;}//////////////////////////////////////////////////////////////////// The test harness:	int results[100];	int resultsSC[100];	int resultsOpt[100];	float floats[100];	for(int i = 0; i < 100; i++)		floats[i] = (i + i/3.0f) * (i % 3 ? 1.0f : -1.0f);	for(int i = 0; i < 100; i++)		is_int(floats + i, results + i);	for(int i = 0; i < 100; i++)		is_intSC(floats + i, resultsSC + i);	is_int(floats, resultsOpt, 100);	for(int i = 0; i < 20; i++)	{		for(int j = 0; j < 5; j++)			std::cout << floats[i * 5 + j] << ": " << results[i * 5 + j] << "   ";		std::cout << std::endl;		for(int j = 0; j < 5; j++)			std::cout << floats[i * 5 + j] << ": " << resultsSC[i * 5 + j] << "   ";		std::cout << std::endl;		for(int j = 0; j < 5; j++)			std::cout << floats[i * 5 + j] << ": " << resultsOpt[i * 5 + j] << "   ";		std::cout << std::endl;	}

And luckily, they all agree on all the numbers :)

##### Share on other sites
Thanks for great answer! :-) it looks like FloatToInt is certainly slower than your version, though there is still one unknown: if FloatToInt was so close to your optimized version and it was 2x slower than normal casting, then FloatToIntPositive and FloatToIntNegative (which were 5x faster then normal casting) should be faster then your version, in spite of having no "if" which makes cpu branching miss.

I wonder if you could time also theese two functions, using appropriate numbers (positive for FloatToIntPositive, negative for the second one)? Odds that they will be sligthly faster... and in spite of all, sometimes they can be useful, ie. when you are sure that this particular number will always be positive/negative - in that case you can gain few cycles (wow! micro optimization rocks ;->)

Anyway, I'm going to use your asm version in place of my old FloatToInt, so big thx again.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628301
• Total Posts
2981906

• 9
• 11
• 11
• 10
• 10