89

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.

mskfisher
  • 3,291
  • 4
  • 35
  • 48
sidyll
  • 57,726
  • 14
  • 108
  • 151
  • 10
    Your method fails when the number is greater than the maximum allowable integer value. – Mark Ransom Apr 26 '11 at 22:08
  • See my answer for a fix the the problem with OP's approach. – R.. GitHub STOP HELPING ICE Apr 26 '11 at 22:13
  • Surely the correct answer is: you are asking the wrong question. – John Marshall Apr 27 '11 at 01:32
  • @John Marshall: So, what would be the correct question? – sidyll Apr 28 '11 at 00:00
  • 2
    Well, 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 an `int` of the appropriate size? – John Marshall Apr 28 '11 at 13:47
  • You 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` or `float` 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 an `int` is trivial, and your code explicitly converts the ints to `float` 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 Jun 28 '11 at 09:04
  • (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... – John Marshall Jun 28 '11 at 09:07
  • @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. – Emile Cormier Jan 11 '20 at 21:44

8 Answers8

86

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 floats.

Community
  • 1
  • 1
Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90
  • 1
    Well, 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 Apr 28 '11 at 00:03
  • 1
    @sidyll: both expressions round `f`, yes, but they do so in order to _check_ whether it's an integer. – Marc Mutz - mmutz Apr 28 '11 at 05:20
  • @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. – Marc Mutz - mmutz Apr 28 '11 at 05:28
  • I'm sorry! I should use glasses. Makes all sense. – sidyll Apr 28 '11 at 17:57
  • 6
    While this works, `ceilf` and `floorf` 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 be `rintf(f)==f`. With `-fno-math-errno`, GCC will compile `rintf` to a single inline instruction. – R.. GitHub STOP HELPING ICE Jun 12 '13 at 18:37
  • 5
    @R.. Am I correct in assuming `nearbyintf` will do the same without the exception nor flag? – Slipp D. Thompson May 01 '14 at 04:39
  • @SlippD.Thompson "The only difference between `nearbyint` and `rint` is that `nearbyint` never raises `FE_INEXACT`." – Tur1ng Oct 21 '21 at 14:52
  • @R..GitHubSTOPHELPINGICE Sadly, actually [recent GCC may generate a `call` to `nearbyint`](https://gcc.godbolt.org/z/Kjx69MYWM) instead by a fp instruction like `frndint`... – FrankHB Nov 23 '21 at 19:02
  • @FrankHB: Any idea why? – R.. GitHub STOP HELPING ICE Nov 23 '21 at 20:19
  • @R..GitHubSTOPHELPINGICE Typical implementations use hardware floating-point environment state, so `nearbyint` actually do more than `rint` 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 Nov 27 '21 at 22:54
  • That 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` and `trunc` all work in normal cases, they also have the `FE_INEXACT` issue. (And all these functions can be even more expensive in the implementation than `nearbyint` by the unnecessary overhead of calculating the floating-point status word...) – FrankHB Nov 27 '21 at 23:13
  • @R..GitHubSTOPHELPINGICE Ah... my fault. I should have at @SlippD.Thompson and @Tur1ng. I didn't mean GCC generate code for `rint` differently. The single `frndint` instruction instead of the call to `rint` is still generated from a `rint` call by GCC using x87 (but not by other compilers), regardless of `-fno-math-errno` (BTW there are [other issues](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88576) on this option). However, as I said above, `nearbyint` is likely more fit here. – FrankHB Nov 27 '21 at 23:33
29

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.

borgr
  • 20,175
  • 6
  • 25
  • 35
Jason C
  • 38,729
  • 14
  • 126
  • 182
13
if (fmod(f, 1) == 0.0) {
  ...
}

Don't forget math.h and libm.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
9

stdlib float modf (float x, float *ipart) splits into two parts, check if return value (fractional part) == 0.

David Neiss
  • 8,161
  • 2
  • 20
  • 21
  • I just keep wondering if this isn't too expensive, if compared with my initial solution (as @R.. says in the other similar answer). – sidyll Apr 28 '11 at 00:01
  • 2
    @mmutz: have I said I should use glasses? – sidyll Apr 28 '11 at 17:58
  • does that work when the number is 23.9999999999? it seems to me this only works when the number is 24.0000000000001 – Jon V Apr 06 '17 at 12:01
6
if (f <= LONG_MIN || f >= LONG_MAX || f == (long)f) /* it's an integer */
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 1
    If `sizeof(f) > sizeof(long)` this might not hold. – Mark Ransom Apr 26 '11 at 22:27
  • `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 reality `float` will always be either IEEE single-precision, or something worse, with less than 32 bits in the mantissa. – R.. GitHub STOP HELPING ICE Apr 26 '11 at 22:31
  • 3
    Just making the point in case someone finds this answer and tries to apply it to a `double`. – Mark Ransom Apr 26 '11 at 22:34
  • 1
    Good point. For `double` I would use `long long` and `LLONG_MAX`/`LLONG_MIN`. – R.. GitHub STOP HELPING ICE Apr 26 '11 at 22:35
  • Note that the same argument for why `long` should work with `float` applies to `long long` and `double`. – R.. GitHub STOP HELPING ICE Apr 26 '11 at 22:52
  • For 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)` – Alec Jacobson Sep 22 '14 at 23:33
  • 3
    @mangledorf: Your transformation is invalid. `(long)f` invokes undefined behavior if the value is not representable as `long`. Thus you have to make the comparisons first to be safe. – R.. GitHub STOP HELPING ICE Sep 23 '14 at 01:29
  • And FYI, I think my comment on the accepted answer has the optimal solution: `rintf(f)==f`, with `-fno-math-errno`. – R.. GitHub STOP HELPING ICE Sep 23 '14 at 01:46
  • @R.., If `(long)f` is UB if `f` 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. – Justin L. Aug 23 '17 at 04:51
  • 1
    Shouldn't it be: `f <= LONG_MAX && f >= LONG_MIN && f == (long)f` ? – Emile Cormier Jan 11 '20 at 21:27
  • @R.. Can you explain the logic, please? Why is `f` considered an integer if it's less than `LONG_MIN`? For example, `f=-1.1e20`. – Emile Cormier Jan 11 '20 at 23:41
  • 1
    @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. Since `float` has a 24-bit significand, any value at least 2^24 in magnitude has no significand bits after the radix point. C requires `long` to be at least 32 bits, so values greater than `LONG_MAX` or less than `LONG_MIN` are way outside that range. – R.. GitHub STOP HELPING ICE Jan 12 '20 at 00:04
  • @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 Jan 12 '20 at 00:21
  • @R.. https://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 extra `f <= LONG_MAX && f >= LONG_MIN` in my variant seem to be redundant according to my tests. – Emile Cormier Jan 12 '20 at 00:44
  • 1
    @EmileCormier: It's not redundant. Read the prior comments. `(long)f` has undefined behavior if the value of `f` is outside the range of `long`. – R.. GitHub STOP HELPING ICE Jan 12 '20 at 01:30
1

This deals with computational round-off. You set the epsilon as desired:

bool IsInteger(float value)
{
    return fabs(ceilf(value) - value) < EPSILON;
}
Jiminion
  • 5,080
  • 1
  • 31
  • 54
0
#define twop22 (0x1.0p+22)
#define ABS(x) (fabs(x))
#define isFloatInteger(x) ((ABS(x) >= twop22) || (((ABS(x) + twop22) - twop22) == ABS(x)))
kanna
  • 1,412
  • 1
  • 15
  • 33
0

I'm not 100% sure but when you cast f to an int, and subtract it from f, I believe it is getting cast back to a float. This probably won't matter in this case, but it could present problems down the line if you are expecting that to be an int for some reason.

I don't know if it's a better solution per se, but you could use modulus math instead, for example: float f = 4.5886; bool isInt; isInt = (f % 1.0 != 0) ? false : true; depending on your compiler you may or not need the .0 after the 1, again the whole implicit casts thing comes into play. In this code, the bool isInt should be true if the right of the decimal point is all zeroes, and false otherwise.