20

This code works (C# 3)

double d;
if(d == (double)(int)d) ...;
  1. Is there a better way to do this?
  2. For extraneous reasons I want to avoid the double cast so; what nice ways exist other than this? (even if they aren't as good)

Note: Several people pointed out the (important) point that == is often problematic regrading floating point. In this cases I expect values in the range of 0 to a few hundred and they are supposed to be integers (non ints are errors) so if those points "shouldn't" be an issue for me.

BCS
  • 75,627
  • 68
  • 187
  • 294

12 Answers12

36
d == Math.Floor(d)

does the same thing in other words.

NB: Hopefully you're aware that you have to be very careful when doing this kind of thing; floats/doubles will very easily accumulate miniscule errors that make exact comparisons (like this one) fail for no obvious reason.

  • 1
    Not exactly the same thing. I think it's much better. In case of big inetegr numbers, that can be stored in double but can't be stored in int, casting to int won't work – Maciej Hehl Sep 26 '08 at 22:06
  • As you mentioned, great care should be taken doing any kind of floating point equality comparison - a much better strategy is to use a small epsilon factor in most cases. – Cade Roux Sep 26 '08 at 22:08
  • I like the answer! However in my cases it happens that I should also detect overflow (or any rounding error for that matter) I'll leave the question in the more generic form though. – BCS Sep 26 '08 at 22:10
  • @Cade Roux: exact FP compare is useful if you are looking at rounding actions. – BCS Sep 26 '08 at 22:11
  • @BCS, sure - just need to remember that == means "really equal" - I've had problems with dates in VB, because of the floating point representation - a difference which VB cannot even display. – Cade Roux Sep 26 '08 at 22:16
  • 2
    @Mike F: an alternate strategy would use d == Math.Truncate(d) as this uses a simpler rounding mode. – user7116 Oct 01 '08 at 18:51
8

This would work I think:

if (d % 1 == 0) {
  //...
}
VoxPelli
  • 2,697
  • 1
  • 16
  • 22
  • I'm just a bit weary of int mod with negative numbers, I'm not sure I *want* to figure them out for FP. – BCS Sep 26 '08 at 22:36
  • @BCS, could you please say, what's wrong with "int mod with negative numbers"? – user1234567 Aug 01 '20 at 07:07
  • There are at least two different interpretations for integer modules when one or both operands are negative. And there is a lot of buggy code as a result of people not remembering which the current language uses. I don't know what happens if you toos in FP. And I suspect the answer has too many different "it depends" to be safe to use. For example, without a detailed read of the standards, I'd not be sure that the FP won't get cast to int before the mod in some cases (or some version of the language). – BCS Aug 02 '20 at 23:55
5

If your double is the result of another calculation, you probably want something like:

d == Math.Floor(d + 0.00001);

That way, if there's been a slight rounding error, it'll still match.

Khoth
  • 13,068
  • 3
  • 28
  • 27
  • 1
    @Khoth Please, could you exhibit a case where (d==Math.Floor(d)) != (d==Math.Floor(d+0.00001)) ? – aka.nice Aug 21 '12 at 16:07
  • You should use double.Epsilon... Or if you are considering x many operations x*double.Epsilon. – ccook Nov 18 '12 at 20:10
5

I cannot answer the C#-specific part of the question, but I must point out you are probably missing a generic problem with floating point numbers.

Generally, integerness is not well defined on floats. For the same reason that equality is not well defined on floats. Floating point calculations normally include both rounding and representation errors.

For example, 1.1 + 0.6 != 1.7.

Yup, that's just the way floating point numbers work.

Here, 1.1 + 0.6 - 1.7 == 2.2204460492503131e-16.

Strictly speaking, the closest thing to equality comparison you can do with floats is comparing them up to a chosen precision.

If this is not sufficient, you must work with a decimal number representation, with a floating point number representation with built-in error range, or with symbolic computations.

Sophie Alpert
  • 139,698
  • 36
  • 220
  • 238
ddaa
  • 52,890
  • 7
  • 50
  • 59
  • A good point and well described, However in my case the desired behavior is well defined by the original code. Anything that mimics it is valid. – BCS Sep 26 '08 at 22:17
  • Even 0.1 != 0.1 as far as floats go. The mantisas are necessarily truncating the binary value 0.0001100110011001100110011001100... Try 0.1F==0.1 – ccook Nov 18 '12 at 20:38
3

If you are just going to convert it, Mike F / Khoth's answer is good, but doesn't quite answer your question. If you are going to actually test, and it's actually important, I recommend you implement something that includes a margin of error.

For instance, if you are considering money and you want to test for even dollar amounts, you might say (following Khoth's pattern):

if( Math.abs(d - Math.Floor(d + 0.001)) < 0.001)

In other words, take the absolute value of the difference of the value and it's integer representation and ensure that it's small.

JsAndDotNet
  • 16,260
  • 18
  • 100
  • 123
Bill K
  • 62,186
  • 18
  • 105
  • 157
3

A simple test such as 'x == floor(x)' is mathematically assured to work correctly, for any fixed-precision FP number.

All legal fixed-precision FP encodings represent distinct real numbers, and so for every integer x, there is at most one fixed-precision FP encoding that matches it exactly.

Therefore, for every integer x that CAN be represented in such way, we have x == floor(x) necessarily, since floor(x) by definition returns the largest FP number y such that y <= x and y represents an integer; so floor(x) must return x.

yonil
  • 564
  • 3
  • 12
2

You don't need the extra (double) in there. This works:

if (d == (int)d) {
 //...
}
swilliams
  • 48,060
  • 27
  • 100
  • 130
2

Use Math.Truncate()

Michał Piaskowski
  • 3,800
  • 2
  • 34
  • 46
1

This will let you choose what precision you're looking for, plus or minus half a tick, to account for floating point drift. The comparison is integral also which is nice.

static void Main(string[] args)
{
    const int precision = 10000;

    foreach (var d in new[] { 2, 2.9, 2.001, 1.999, 1.99999999, 2.00000001 })
    {
        if ((int) (d*precision + .5)%precision == 0)
        {
            Console.WriteLine("{0} is an int", d);
        }
    }
}

and the output is

2 is an int
1.99999999 is an int
2.00000001 is an int
loudej
  • 1,889
  • 11
  • 17
0

To handle the precision of the double...

Math.Abs(d - Math.Floor(d)) <= double.Epsilon

Consider the following case where a value less then double.Epsilon fails to compare as zero.

// number of possible rounds
const int rounds = 1;

// precision causes rounding up to double.Epsilon
double d = double.Epsilon*.75;

// due to the rounding this comparison fails
Console.WriteLine(d == Math.Floor(d));

// this comparison succeeds by accounting for the rounding
Console.WriteLine(Math.Abs(d - Math.Floor(d)) <= rounds*double.Epsilon);

// The difference is double.Epsilon, 4.940656458412465E-324
Console.WriteLine(Math.Abs(d - Math.Floor(d)).ToString("E15"));
ccook
  • 5,869
  • 6
  • 56
  • 81
0

Something like this

double d = 4.0;
int i = 4;

bool equal = d.CompareTo(i) == 0; // true
Darren Kopp
  • 76,581
  • 9
  • 79
  • 93
0

Could you use this

    bool IsInt(double x)
    {
        try
        {
            int y = Int16.Parse(x.ToString());
            return true;
        }
        catch 
        {
            return false;
        }
    }
Crash893
  • 11,428
  • 21
  • 88
  • 123