Checking if float is an integer
Solution 1
Apart from the fine answers already given, you can also use ceilf(f) == f
or floorf(f) == f
. Both expressions return true
if f
is an integer. They also returnfalse
for NaNs (NaNs always compare unequal) and true
for ±infinity, and don't have the problem with overflowing the integer type used to hold the truncated result, because floorf()
/ceilf()
return float
s.
Solution 2
Keep in mind that most of the techniques here are valid presuming that round-off error due to prior calculations is not a factor. E.g. you could use roundf
, like this:
float z = 1.0f;
if (roundf(z) == z) {
printf("integer\n");
} else {
printf("fraction\n");
}
The problem with this and other similar techniques (such as ceilf
, casting to long
, etc.) is that, while they work great for whole number constants, they will fail if the number is a result of a calculation that was subject to floating-point round-off error. For example:
float z = powf(powf(3.0f, 0.05f), 20.0f);
if (roundf(z) == z) {
printf("integer\n");
} else {
printf("fraction\n");
}
Prints "fraction", even though (31/20)20 should equal 3, because the actual calculation result ended up being 2.9999992847442626953125.
Any similar method, be it fmodf
or whatever, is subject to this. In applications that perform complex or rounding-prone calculations, usually what you want to do is define some "tolerance" value for what constitutes a "whole number" (this goes for floating-point equality comparisons in general). We often call this tolerance epsilon. For example, lets say that we'll forgive the computer for up to +/- 0.00001 rounding error. Then, if we are testing z
, we can choose an epsilon of 0.00001 and do:
if (fabsf(roundf(z) - z) <= 0.00001f) {
printf("integer\n");
} else {
printf("fraction\n");
}
You don't really want to use ceilf
here because e.g. ceilf(1.0000001)
is 2 not 1, and ceilf(-1.99999999)
is -1 not -2.
You could use rintf
in place of roundf
if you prefer.
Choose a tolerance value that is appropriate for your application (and yes, sometimes zero tolerance is appropriate). For more information, check out this article on comparing floating-point numbers.
Solution 3
if (fmod(f, 1) == 0.0) {
...
}
Don't forget math.h
and libm
.
Solution 4
stdlib float modf (float x, float *ipart) splits into two parts, check if return value (fractional part) == 0.
Solution 5
if (f <= LONG_MIN || f >= LONG_MAX || f == (long)f) /* it's an integer */
Related videos on Youtube
sidyll
Updated on July 11, 2020Comments
-
sidyll almost 4 years
How can I check if a
float
variable contains an integer value? So far, I've been using:float f = 4.5886; if (f-(int)f == 0) printf("yes\n"); else printf("no\n");
But I wonder if there is a better solution, or if this one has any (or many) drawbacks.
-
Mark Ransom about 13 yearsYour method fails when the number is greater than the maximum allowable integer value.
-
R.. GitHub STOP HELPING ICE about 13 yearsSee my answer for a fix the the problem with OP's approach.
-
John Marshall about 13 yearsSurely the correct answer is: you are asking the wrong question.
-
sidyll about 13 years@John Marshall: So, what would be the correct question?
-
John Marshall about 13 yearsWell, what is it that you're trying to do? If you're storing a value in a
float
, presumably it's because you want to do floating point arithmetic on it. Then your question becomes: does this variable, whose value is somewhat fluffy in domain-specific ways and in ways that depend on how carefully you do the arithmetic, have a value that's fluffily integral? To answer that meaningfully, you need to consider the domain-specific fluffiness. Or, put another way: what is this scenario in which the correct approach is not to store your value in anint
of the appropriate size? -
John Marshall almost 13 yearsYou say "parsed", so I'm going to assume that this file is a text file, containing text representations of numeric data. Then the parsing answer to your problem would be to parse each item into an
int
orfloat
according to what the text actually is, and return it probably in a union (along with a flag saying which it is, of course). Then your question as to whether an item is anint
is trivial, and your code explicitly converts the ints tofloat
when necessary. Or, more simply: you have the text representation of your number. It's an int if it doesn't match/[.Ee]/
, essentially. -
John Marshall almost 13 years(You need a cleverer pattern if you have hex integer constants, obviously.) And this answers a slightly different question. If an item is written as 1.5E+2, do you want to do integer operations on 150, or do you want to warn that that doesn't look like an integer? Whether warning in this case is a bug or a feature is in the eye of the beholder...
-
Emile Cormier over 4 years@JohnMarshall, perhaps he's parsing JSON and wants to store the number in an union (like many JSON libraries do). He might want to leverage a standard or library function to do the actual parsing, which is not trivial to do correctly. This is what I'm trying to do myself and is what led me to this question.
-
-
R.. GitHub STOP HELPING ICE about 13 yearsThis works, but in principle
fmod
is a rather expensive operation. -
Mark Ransom about 13 yearsIf
sizeof(f) > sizeof(long)
this might not hold. -
R.. GitHub STOP HELPING ICE about 13 years
long
is required to be at least 32-bit. The C standard imposes no requirements whatsoever on the quality of floating-point types, but in realityfloat
will always be either IEEE single-precision, or something worse, with less than 32 bits in the mantissa. -
Mark Ransom about 13 yearsJust making the point in case someone finds this answer and tries to apply it to a
double
. -
R.. GitHub STOP HELPING ICE about 13 yearsGood point. For
double
I would uselong long
andLLONG_MAX
/LLONG_MIN
. -
Mark Ransom about 13 yearsThe
%
operator only works on integers. You're correct about the int being cast back to a float though. -
R.. GitHub STOP HELPING ICE about 13 yearsNote that the same argument for why
long
should work withfloat
applies tolong long
anddouble
. -
Controllerface about 13 yearsmy mistake, i should have used (fmod(f , 1.0) != 0) instead.
-
sidyll about 13 yearsI just keep wondering if this isn't too expensive, if compared with my initial solution (as @R.. says in the other similar answer).
-
sidyll about 13 yearsWell, that defeats the purpose. What I want is to check if the float is an integer, as the title says; and not rounding it. In the latter case, I'd always have an integer. But thanks for pointing it out too.
-
sidyll about 13 yearsYes, I'm aware that it will remain as float, but I just want to check it.
-
Marc Mutz - mmutz about 13 years@sidyll: both expressions round
f
, yes, but they do so in order to check whether it's an integer. -
Marc Mutz - mmutz about 13 years@sidyll: more precisely: note how the result of the rounding operation is in turn compared to
f
, I've edited my answer to be more clear on this, thanks. -
sidyll about 13 yearsI'm sorry! I should use glasses. Makes all sense.
-
sidyll about 13 years@mmutz: have I said I should use glasses?
-
R.. GitHub STOP HELPING ICE almost 11 yearsWhile this works,
ceilf
andfloorf
are unnecessarily expensive operations on at least some archs (mainly x86) due to the need to change and restore rounding mode. A faster test would berintf(f)==f
. With-fno-math-errno
, GCC will compilerintf
to a single inline instruction. -
Slipp D. Thompson about 10 years@R.. Am I correct in assuming
nearbyintf
will do the same without the exception nor flag? -
Alec Jacobson over 9 yearsFor what it's worth, on my machine with gcc this is twice as fast as the
ceilf
floorf
solution. (In my case, I assuming I run into smaller magnitude numbers more often so I changed the order to(f == (long)f || f <= LONG_MIN || f >= LONG_MAX)
-
R.. GitHub STOP HELPING ICE over 9 years@mangledorf: Your transformation is invalid.
(long)f
invokes undefined behavior if the value is not representable aslong
. Thus you have to make the comparisons first to be safe. -
R.. GitHub STOP HELPING ICE over 9 yearsAnd FYI, I think my comment on the accepted answer has the optimal solution:
rintf(f)==f
, with-fno-math-errno
. -
Jonathan V about 7 yearsdoes that work when the number is 23.9999999999? it seems to me this only works when the number is 24.0000000000001
-
MikeFry over 6 years@R.., If
(long)f
is UB iff
is not representable as an int, then surely(long)f
is UB if f is NaN. So I think the example above needs a NaN check. -
weima over 5 yearsdoesnt compile on gcc
-
Emile Cormier over 4 yearsShouldn't it be:
f <= LONG_MAX && f >= LONG_MIN && f == (long)f
? -
Emile Cormier over 4 years@R.. Can you explain the logic, please? Why is
f
considered an integer if it's less thanLONG_MIN
? For example,f=-1.1e20
. -
R.. GitHub STOP HELPING ICE over 4 years@EmileCormier: As a number, -1.1 * 10^20 is an integer anyway, so I guess you meant something different. However the precision of
float
is limited such that the nearest representable value to that number is -109999998686059298816, which is also an integer. Sincefloat
has a 24-bit significand, any value at least 2^24 in magnitude has no significand bits after the radix point. C requireslong
to be at least 32 bits, so values greater thanLONG_MAX
or less thanLONG_MIN
are way outside that range. -
Emile Cormier over 4 years@R.. Yes, I meant a huge, non-integral negative number. While I understand now how your solutions works, I think it breaks the principle of least surprise and would warrant a comment when used in a program. I tried your variant and mine and they both agree. I'll post an online IDE link shortly...
-
Emile Cormier over 4 years@R.. ideone.com/1l5iqv I understand now that the difference is that your variant determines if it's integral, whereas mine determines if it's integral and fits in a
long
variable. The extraf <= LONG_MAX && f >= LONG_MIN
in my variant seem to be redundant according to my tests. -
R.. GitHub STOP HELPING ICE over 4 years@EmileCormier: It's not redundant. Read the prior comments.
(long)f
has undefined behavior if the value off
is outside the range oflong
. -
Tur1ng over 2 years@SlippD.Thompson "The only difference between
nearbyint
andrint
is thatnearbyint
never raisesFE_INEXACT
." -
FrankHB over 2 years@R..GitHubSTOPHELPINGICE Sadly, actually recent GCC may generate a
call
tonearbyint
instead by a fp instruction likefrndint
... -
R.. GitHub STOP HELPING ICE over 2 years@FrankHB: Any idea why?
-
FrankHB over 2 years@R..GitHubSTOPHELPINGICE Typical implementations use hardware floating-point environment state, so
nearbyint
actually do more thanrint
to save and restore the state in each call. (See glibc source for the x87 non-builtin implementations for the instance of the logic.) So GCC may think it is too complex and not worthing replacing the call by simpler instructions. I am not that sure about the concrete differences in generated SSE code, but saving and restoring SSE state seem more expensive than x87, so it is likely the same reason. -
FrankHB over 2 yearsThat said, to avoid
FE_INEXACT
in a portable way,nearbyint
is still preferred, unless the input is already verified elsewhere. At least SSE has the inexact exception (not tested, though). Also note although the rounding mode is irrelevant,floor
,ceil
,round
,roundeven
andtrunc
all work in normal cases, they also have theFE_INEXACT
issue. (And all these functions can be even more expensive in the implementation thannearbyint
by the unnecessary overhead of calculating the floating-point status word...) -
FrankHB over 2 years@R..GitHubSTOPHELPINGICE Ah... my fault. I should have at @SlippD.Thompson and @Tur1ng. I didn't mean GCC generate code for
rint
differently. The singlefrndint
instruction instead of the call torint
is still generated from arint
call by GCC using x87 (but not by other compilers), regardless of-fno-math-errno
(BTW there are other issues on this option). However, as I said above,nearbyint
is likely more fit here.