4

In the following code, I would expect var to get resolved to an Int64, but it gets resolved to a double. Why is it so?

string a =  "1234";
bool asInt = true;
var b = (asInt) ? Int64.Parse(a) : Double.Parse(a) ;
Console.WriteLine(b.GetType());
Ayush
  • 41,754
  • 51
  • 164
  • 239
  • 1
    I'm kind of surprised that even compiles. I thought both sides of the ternary have to return the same type, though I suspose there is an implicit conversation from long to double – J. Holmes Feb 14 '12 at 12:32
  • There is. ,) which explains this. – TomTom Feb 14 '12 at 12:34

6 Answers6

12

There is an implicit conversion from Int64 to Double but not the other way (due to possible loss of precision in that direction).

Since both "branches" of the conditional need to resolve to the same type, the type of b ends up being inferred as a Double.

Magnus Hoff
  • 21,529
  • 9
  • 63
  • 82
Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • Makes sense. Is there any way to create the variable `b` of the type Int64 when needed and Double when needed (based on the bool `asInt`)? Of course, initializing it in a if statement doesn't work because it's scope is then limited within that if block – Ayush Feb 14 '12 at 12:35
  • @xbonez - Not with the conditional operator. The type must be inferred/determined in runtime. You _may_ be able to use `dynamic`. – Oded Feb 14 '12 at 12:36
  • Hmm...thanks. I'll look into dynamic. I know that mathematical operations are slower on doubles, so I was hoping to not use a double when not needed. I'll look into dynamic, but if that doesn't work, I guess I'll live with it. – Ayush Feb 14 '12 at 12:37
  • Dynamic did the trick, coupled with changing the ternary operator to a `if-else` – Ayush Feb 14 '12 at 12:42
  • 2
    @xbonez: Be aware that `dynamic` comes with a runtime overhead of its own. You might be better off going with `double` if you are trying to optimize execution speed. – Magnus Hoff Feb 14 '12 at 12:43
  • @MagnusHoff: ah, haha...didn't think of that. Yeah, I figure I might as well just stick with double – Ayush Feb 14 '12 at 12:46
  • 4
    @xbonez: Using dynamic will be potentially hundreds of thousands of times slower than double. Remember, **dynamic starts the compiler again at runtime**, that's what **dynamic** means. If you have a scenario where the difference between integer and double arithmetic -- a couple of nanoseconds -- is relevant then you need to be doing so **very** careful profile-driven analysis. You can't just look at code and know what parts of it are going to take one machine cycle vs four machine cycles. – Eric Lippert Feb 14 '12 at 14:46
  • @xbonez if you know that the value will be in the range of Int64, and you either know that it will be integral or you are willing to lose the fractional portion, you can convert the double to Int64: `var b = (asInt) ? Int64.Parse(a) : (long)Math.Round(Double.Parse(a));` Of course, if you prefer truncation (or you know it'll be an integer) you can drop the call to `Math.Round`. – phoog Feb 14 '12 at 15:38
5

You can implicitly cast a long to a double.

You cannot implicitly cast a double to a long

So the C# compiler decided the only possibility for your variable type was double.

See Implicit Numeric Conversions Table.

ken2k
  • 48,145
  • 10
  • 116
  • 176
4

Because the compiler needs to infer a type that can hold values of both Int64.Parse(a) and Double.Parse(a) without requiring an explicit cast. If long was inferred, precision would be lost in the other porsion of the expression.

If you need to distinguish the type, you have to declare two variables and rewrite your code:

if (asInt)
{
    var b = Int64.Parse(a); // will infer a `long`
    Console.WriteLine(b.GetType());
}
else
{
    var b = Double.Parse(a); // will infer a `double`
    Console.WriteLine(b.GetType());
}
Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
  • 1
    So, is there any way I can do what you've done yet be able to access the variable b outside the if-else scope? – Ayush Feb 14 '12 at 12:36
  • @xbonez, yes, but you should define `b` as type `object`: `object b;` – Dims Feb 14 '12 at 12:47
  • Perfect...this is what I've been looking for so long. `Object` does exactly what I need. Thank you! – Ayush Feb 14 '12 at 12:49
  • Oh, wait...it won't let me do any operations on `Object` ( >, < etc.) unless I first cast it to either `Int64` or `Double`, which kinda defeats the purpose since I don't know the type until runtime. – Ayush Feb 14 '12 at 12:52
1

The C# compiler is inferring the type from the common denominator between the two return types of your ternary. Int64 can be implicitly converted to Double. The inverse is not so.

Note that the state of the Boolean in your code example has nothing to do with the inferred type.

My Other Me
  • 5,007
  • 6
  • 41
  • 48
0

This is the work of ?: operator. It should cast all results to one type.

P.S. The similar behavior of + operator you know:

string a =  "1234";
var b = Int64.Parse(a) + Double.Parse(a) ;
Console.WriteLine(b.GetType());

P.P.S. To have what you want you should use object:

string a =  "1234";
bool asInt = true;
object b;
if(asInt) b=Int64.Parse(a); else b=Double.Parse(a);
Console.WriteLine(b.GetType());

P.P.P.S. Another option is:

        string a = "1234";
#if asInt
        Int64 b = Int64.Parse(a);
#else
        Double b = Double.Parse(a);
#endif
        Console.WriteLine(b.GetType());

to define asInt use

#define asInt
Dims
  • 47,675
  • 117
  • 331
  • 600
  • Unfortunately, `object` requires me to cast it as either an `Int64` or `double` before I can do any operations on it, which isn't possible since I don't know the type until runtime – Ayush Feb 14 '12 at 12:54
  • Then try conditional compilation directives, `#if` etc – Dims Feb 14 '12 at 13:18
  • Also you can use generic methods and/or types if you are not relying on arithmetic operators. If you are then you will to do more tricks. – Dims Feb 14 '12 at 13:19
0

I'm surprised nobody has pointed out that if you know the value will be a legal long value, you can change the compiler's behavior with an explicit cast, and just use long.

This may or may not be helpful, depending on the condition that determines the value for asInt, and depending on what you intend to do with the result of the expression. Here's an example:

string a =  "1234.56"; 
bool asDouble = a.Contains("."); 
var b = asDouble ? (long)Double.Parse(a) : Int64.Parse(a);
Console.WriteLine(b.GetType());

In fact, in this example, you don't need the conditional operator; this would work too:

string a =  "1234.56"; 
var b = (long)Double.Parse(a);
Console.WriteLine(b.GetType());

In other words, it's possible that the best solution wouldn't use the ternary operator, but the question doesn't give enough context to know.

phoog
  • 42,068
  • 6
  • 79
  • 117