Mathematically,

 the integers Z are included in the rational numbers Q,
 which are included in the real numbers R,
 which can be generalized to the complex numbers C.


This means that each of those larger sets, and the data types used to represent them, include some integers.

Task[edit]
Given a rational, real, or complex number of any type, test whether it is mathematically an integer.

Your code should handle all numeric data types commonly used in your programming language.

Discuss any limitations of your code.

Definition
For the purposes of this task, integerness means that a number could theoretically be represented as an integer at no loss of precision (given an infinitely wide integer type).

In other words:



 Set

 Common representation

 C++ type

 Considered an integer...


 rational numbers Q

 fraction

 std::ratio

 ...if its denominator is 1 (in reduced form)


 real numbers Z
(approximated)

 fixed-point



 ...if it has no non-zero digits after the decimal point


 floating-point

 float, double

 ...if the number of significant decimal places of its mantissa isn't greater than its exponent


 complex numbers C

 pair of real numbers

 std::complex

 ...if its real part is considered an integer and its imaginary part is zero

Extra credit
Optionally, make your code accept a tolerance parameter for fuzzy testing. The tolerance is the maximum amount by which the number may differ from the nearest integer, to still be considered an integer.

This is useful in practice, because when dealing with approximate numeric types (such as floating point), there may already be round-off errors from previous calculations. For example, a float value of 0.9999999998 might actually be intended to represent the integer 1.

Test cases


 Input

 Output

 Comment


 Type

 Value

 exact

 tolerance = 0.00001


 decimal

 25.000000

 true




 24.999999

 false

 true




 25.000100

 false




 floating-point

 -2.1e120

 true

 This one is tricky, because in most languages it is too large to fit into a native integer type.
It is, nonetheless, mathematically an integer, and your code should identify it as such.


 -5e-2

 false




 NaN

 false




 Inf

 false

 This one is debatable. If your code considers it an integer, that's okay too.


 complex

 5.0+0.0i

 true




 5-5i

 false



(The types and notations shown in these tables are merely examples – you should use the native data types and number literals of your programming language and standard library. Use a different set of test-cases, if this one doesn't demonstrate all relevant behavior.)


import java.math.BigDecimal;
import java.util.List;
 
public class TestIntegerness {
    private static boolean isLong(double d) {
        return isLong(d, 0.0);
    }
 
    private static boolean isLong(double d, double tolerance) {
        return (d - Math.floor(d)) <= tolerance || (Math.ceil(d) - d) <= tolerance;
    }
 
    @SuppressWarnings("ResultOfMethodCallIgnored")
    private static boolean isBigInteger(BigDecimal bd) {
        try {
            bd.toBigIntegerExact();
            return true;
        } catch (ArithmeticException ex) {
            return false;
        }
    }
 
    private static class Rational {
        long num;
        long denom;
 
        Rational(int num, int denom) {
            this.num = num;
            this.denom = denom;
        }
 
        boolean isLong() {
            return num % denom == 0;
        }
 
        @Override
        public String toString() {
            return String.format("%s/%s", num, denom);
        }
    }
 
    private static class Complex {
        double real;
        double imag;
 
        Complex(double real, double imag) {
            this.real = real;
            this.imag = imag;
        }
 
        boolean isLong() {
            return TestIntegerness.isLong(real) && imag == 0.0;
        }
 
        @Override
        public String toString() {
            if (imag >= 0.0) {
                return String.format("%s + %si", real, imag);
            }
            return String.format("%s - %si", real, imag);
        }
    }
 
    public static void main(String[] args) {
        List<Double> da = List.of(25.000000, 24.999999, 25.000100);
        for (Double d : da) {
            boolean exact = isLong(d);
            System.out.printf("%.6f is %s integer%n", d, exact ? "an" : "not an");
        }
        System.out.println();
 
        double tolerance = 0.00001;
        System.out.printf("With a tolerance of %.5f:%n", tolerance);
        for (Double d : da) {
            boolean fuzzy = isLong(d, tolerance);
            System.out.printf("%.6f is %s integer%n", d, fuzzy ? "an" : "not an");
        }
        System.out.println();
 
        List<Double> fa = List.of(-2.1e120, -5e-2, Double.NaN, Double.POSITIVE_INFINITY);
        for (Double f : fa) {
            boolean exact = !f.isNaN() && !f.isInfinite() && isBigInteger(new BigDecimal(f.toString()));
            System.out.printf("%s is %s integer%n", f, exact ? "an" : "not an");
        }
        System.out.println();
 
        List<Complex> ca = List.of(new Complex(5.0, 0.0), new Complex(5.0, -5.0));
        for (Complex c : ca) {
            boolean exact = c.isLong();
            System.out.printf("%s is %s integer%n", c, exact ? "an" : "not an");
        }
        System.out.println();
 
        List<Rational> ra = List.of(new Rational(24, 8), new Rational(-5, 1), new Rational(17, 2));
        for (Rational r : ra) {
            boolean exact = r.isLong();
            System.out.printf("%s is %s integer%n", r, exact ? "an" : "not an");
        }
    }
}