views:

1107

answers:

10

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.

+1  A: 

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

if (d == (int)d) {
 //...
}
swilliams
That works! BTW, are you sure that it will be exactly the same?
BCS
Um. What about this? `d= 3e30; if (d == (int)d) {`
ΤΖΩΤΖΙΟΥ
Yup, it wont do exactly what I asked for there but I will do what I need.
BCS
+21  A: 
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.

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
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
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
@Cade Roux: exact FP compare is useful if you are looking at rounding actions.
BCS
@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
@Mike F: an alternate strategy would use d == Math.Truncate(d) as this uses a simpler rounding mode.
sixlettervariables
A: 

Something like this

double d = 4.0;
int i = 4;

bool equal = d.CompareTo(i) == 0; // true
Darren Kopp
+2  A: 

Use Math.Truncate()

Michał Piaskowski
+6  A: 

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
+3  A: 

This would work I think:

if (d % 1 == 0) {
  //...
}
VoxPelli
lol @ BC3. performance wh*re
Darren Kopp
I had the same idea. I tested it and it seems to work fine.
Wedge
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
+6  A: 

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.

ddaa
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
+2  A: 

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.

Bill K
+1  A: 

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
A: 

Could you use this

    bool IsInt(double x)
    {
        try
        {
            int y = Int16.Parse(x.ToString());
            return true;
        }
        catch 
        {
            return false;
        }
    }
Crash893
int.TryParse would be easier to use, but it will also incur a (relatively) large overhead.
Hosam Aly
x.ToString() feed into some string searching logic might be even better.
BCS
I guess you could do something likeif(x.tostring().indexof('.')!= -1){true}else{false}
Crash893
@Crash893: You'd need IndexOfAny('.', 'e', 'E'), as some values don't have dots (e.g. 1e15).
Hosam Aly
but that could be a int
Crash893
@Crash893, you're right. I missed that.
Hosam Aly