Checking if float is an integer

122,627

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

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 */
Share:
122,627

Related videos on Youtube

sidyll
Author by

sidyll

Updated on July 11, 2020

Comments

  • sidyll
    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
      Mark Ransom about 13 years
      Your method fails when the number is greater than the maximum allowable integer value.
    • R.. GitHub STOP HELPING ICE
      R.. GitHub STOP HELPING ICE about 13 years
      See my answer for a fix the the problem with OP's approach.
    • John Marshall
      John Marshall about 13 years
      Surely the correct answer is: you are asking the wrong question.
    • sidyll
      sidyll about 13 years
      @John Marshall: So, what would be the correct question?
    • John Marshall
      John Marshall about 13 years
      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
      John Marshall almost 13 years
      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
      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
      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
    R.. GitHub STOP HELPING ICE about 13 years
    This works, but in principle fmod is a rather expensive operation.
  • Mark Ransom
    Mark Ransom about 13 years
    If sizeof(f) > sizeof(long) this might not hold.
  • R.. GitHub STOP HELPING ICE
    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 reality float will always be either IEEE single-precision, or something worse, with less than 32 bits in the mantissa.
  • Mark Ransom
    Mark Ransom about 13 years
    Just making the point in case someone finds this answer and tries to apply it to a double.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE about 13 years
    Good point. For double I would use long long and LLONG_MAX/LLONG_MIN.
  • Mark Ransom
    Mark Ransom about 13 years
    The % operator only works on integers. You're correct about the int being cast back to a float though.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE about 13 years
    Note that the same argument for why long should work with float applies to long long and double.
  • Controllerface
    Controllerface about 13 years
    my mistake, i should have used (fmod(f , 1.0) != 0) instead.
  • sidyll
    sidyll about 13 years
    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
    sidyll about 13 years
    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
    sidyll about 13 years
    Yes, I'm aware that it will remain as float, but I just want to check it.
  • Marc Mutz - mmutz
    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
    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
    sidyll about 13 years
    I'm sorry! I should use glasses. Makes all sense.
  • sidyll
    sidyll about 13 years
    @mmutz: have I said I should use glasses?
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE almost 11 years
    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.
  • Slipp D. Thompson
    Slipp D. Thompson about 10 years
    @R.. Am I correct in assuming nearbyintf will do the same without the exception nor flag?
  • Alec Jacobson
    Alec Jacobson over 9 years
    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)
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 9 years
    @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
    R.. GitHub STOP HELPING ICE over 9 years
    And FYI, I think my comment on the accepted answer has the optimal solution: rintf(f)==f, with -fno-math-errno.
  • Jonathan V
    Jonathan V about 7 years
    does that work when the number is 23.9999999999? it seems to me this only works when the number is 24.0000000000001
  • MikeFry
    MikeFry over 6 years
    @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.
  • weima
    weima over 5 years
    doesnt compile on gcc
  • Emile Cormier
    Emile Cormier over 4 years
    Shouldn't it be: f <= LONG_MAX && f >= LONG_MIN && f == (long)f ?
  • Emile Cormier
    Emile Cormier over 4 years
    @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.
  • R.. GitHub STOP HELPING ICE
    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. 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.
  • Emile Cormier
    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
    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 extra f <= LONG_MAX && f >= LONG_MIN in my variant seem to be redundant according to my tests.
  • R.. GitHub STOP HELPING ICE
    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 of f is outside the range of long.
  • Tur1ng
    Tur1ng over 2 years
    @SlippD.Thompson "The only difference between nearbyint and rint is that nearbyint never raises FE_INEXACT."
  • FrankHB
    FrankHB over 2 years
    @R..GitHubSTOPHELPINGICE Sadly, actually recent GCC may generate a call to nearbyint instead by a fp instruction like frndint...
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 2 years
    @FrankHB: Any idea why?
  • FrankHB
    FrankHB over 2 years
    @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
    FrankHB over 2 years
    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
    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 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 on this option). However, as I said above, nearbyint is likely more fit here.