in src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs [1041:1285]
private static object DoUncheckedConversion(SpecialType destinationType, ConstantValue value)
{
// Note that we keep "single" floats as doubles internally to maintain higher precision. However,
// we do not do so in an entirely "lossless" manner. When *converting* to a float, we do lose
// the precision lost due to the conversion. But when doing arithmetic, we do the arithmetic on
// the double values.
//
// An example will help. Suppose we have:
//
// const float cf1 = 1.0f;
// const float cf2 = 1.0e-15f;
// const double cd3 = cf1 - cf2;
//
// We first take the double-precision values for 1.0 and 1.0e-15 and round them to floats,
// and then turn them back into doubles. Then when we do the subtraction, we do the subtraction
// in doubles, not in floats. Had we done the subtraction in floats, we'd get 1.0; but instead we
// do it in doubles and get 0.99999999999999.
//
// Similarly, if we have
//
// const int i4 = int.MaxValue; // 2147483647
// const float cf5 = int.MaxValue; // 2147483648.0
// const double cd6 = cf5; // 2147483648.0
//
// The int is converted to float and stored internally as the double 214783648, even though the
// fully precise int would fit into a double.
unchecked
{
switch (value.Discriminator)
{
case ConstantValueTypeDiscriminator.Byte:
byte byteValue = value.ByteValue;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)byteValue;
case SpecialType.System_Char: return (char)byteValue;
case SpecialType.System_UInt16: return (ushort)byteValue;
case SpecialType.System_UInt32: return (uint)byteValue;
case SpecialType.System_UInt64: return (ulong)byteValue;
case SpecialType.System_SByte: return (sbyte)byteValue;
case SpecialType.System_Int16: return (short)byteValue;
case SpecialType.System_Int32: return (int)byteValue;
case SpecialType.System_Int64: return (long)byteValue;
case SpecialType.System_Single:
case SpecialType.System_Double: return (double)byteValue;
case SpecialType.System_Decimal: return (decimal)byteValue;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.Char:
char charValue = value.CharValue;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)charValue;
case SpecialType.System_Char: return (char)charValue;
case SpecialType.System_UInt16: return (ushort)charValue;
case SpecialType.System_UInt32: return (uint)charValue;
case SpecialType.System_UInt64: return (ulong)charValue;
case SpecialType.System_SByte: return (sbyte)charValue;
case SpecialType.System_Int16: return (short)charValue;
case SpecialType.System_Int32: return (int)charValue;
case SpecialType.System_Int64: return (long)charValue;
case SpecialType.System_Single:
case SpecialType.System_Double: return (double)charValue;
case SpecialType.System_Decimal: return (decimal)charValue;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.UInt16:
ushort uint16Value = value.UInt16Value;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)uint16Value;
case SpecialType.System_Char: return (char)uint16Value;
case SpecialType.System_UInt16: return (ushort)uint16Value;
case SpecialType.System_UInt32: return (uint)uint16Value;
case SpecialType.System_UInt64: return (ulong)uint16Value;
case SpecialType.System_SByte: return (sbyte)uint16Value;
case SpecialType.System_Int16: return (short)uint16Value;
case SpecialType.System_Int32: return (int)uint16Value;
case SpecialType.System_Int64: return (long)uint16Value;
case SpecialType.System_Single:
case SpecialType.System_Double: return (double)uint16Value;
case SpecialType.System_Decimal: return (decimal)uint16Value;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.UInt32:
uint uint32Value = value.UInt32Value;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)uint32Value;
case SpecialType.System_Char: return (char)uint32Value;
case SpecialType.System_UInt16: return (ushort)uint32Value;
case SpecialType.System_UInt32: return (uint)uint32Value;
case SpecialType.System_UInt64: return (ulong)uint32Value;
case SpecialType.System_SByte: return (sbyte)uint32Value;
case SpecialType.System_Int16: return (short)uint32Value;
case SpecialType.System_Int32: return (int)uint32Value;
case SpecialType.System_Int64: return (long)uint32Value;
case SpecialType.System_Single: return (double)(float)uint32Value;
case SpecialType.System_Double: return (double)uint32Value;
case SpecialType.System_Decimal: return (decimal)uint32Value;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.UInt64:
ulong uint64Value = value.UInt64Value;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)uint64Value;
case SpecialType.System_Char: return (char)uint64Value;
case SpecialType.System_UInt16: return (ushort)uint64Value;
case SpecialType.System_UInt32: return (uint)uint64Value;
case SpecialType.System_UInt64: return (ulong)uint64Value;
case SpecialType.System_SByte: return (sbyte)uint64Value;
case SpecialType.System_Int16: return (short)uint64Value;
case SpecialType.System_Int32: return (int)uint64Value;
case SpecialType.System_Int64: return (long)uint64Value;
case SpecialType.System_Single: return (double)(float)uint64Value;
case SpecialType.System_Double: return (double)uint64Value;
case SpecialType.System_Decimal: return (decimal)uint64Value;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.SByte:
sbyte sbyteValue = value.SByteValue;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)sbyteValue;
case SpecialType.System_Char: return (char)sbyteValue;
case SpecialType.System_UInt16: return (ushort)sbyteValue;
case SpecialType.System_UInt32: return (uint)sbyteValue;
case SpecialType.System_UInt64: return (ulong)sbyteValue;
case SpecialType.System_SByte: return (sbyte)sbyteValue;
case SpecialType.System_Int16: return (short)sbyteValue;
case SpecialType.System_Int32: return (int)sbyteValue;
case SpecialType.System_Int64: return (long)sbyteValue;
case SpecialType.System_Single:
case SpecialType.System_Double: return (double)sbyteValue;
case SpecialType.System_Decimal: return (decimal)sbyteValue;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.Int16:
short int16Value = value.Int16Value;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)int16Value;
case SpecialType.System_Char: return (char)int16Value;
case SpecialType.System_UInt16: return (ushort)int16Value;
case SpecialType.System_UInt32: return (uint)int16Value;
case SpecialType.System_UInt64: return (ulong)int16Value;
case SpecialType.System_SByte: return (sbyte)int16Value;
case SpecialType.System_Int16: return (short)int16Value;
case SpecialType.System_Int32: return (int)int16Value;
case SpecialType.System_Int64: return (long)int16Value;
case SpecialType.System_Single:
case SpecialType.System_Double: return (double)int16Value;
case SpecialType.System_Decimal: return (decimal)int16Value;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.Int32:
int int32Value = value.Int32Value;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)int32Value;
case SpecialType.System_Char: return (char)int32Value;
case SpecialType.System_UInt16: return (ushort)int32Value;
case SpecialType.System_UInt32: return (uint)int32Value;
case SpecialType.System_UInt64: return (ulong)int32Value;
case SpecialType.System_SByte: return (sbyte)int32Value;
case SpecialType.System_Int16: return (short)int32Value;
case SpecialType.System_Int32: return (int)int32Value;
case SpecialType.System_Int64: return (long)int32Value;
case SpecialType.System_Single: return (double)(float)int32Value;
case SpecialType.System_Double: return (double)int32Value;
case SpecialType.System_Decimal: return (decimal)int32Value;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.Int64:
long int64Value = value.Int64Value;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)int64Value;
case SpecialType.System_Char: return (char)int64Value;
case SpecialType.System_UInt16: return (ushort)int64Value;
case SpecialType.System_UInt32: return (uint)int64Value;
case SpecialType.System_UInt64: return (ulong)int64Value;
case SpecialType.System_SByte: return (sbyte)int64Value;
case SpecialType.System_Int16: return (short)int64Value;
case SpecialType.System_Int32: return (int)int64Value;
case SpecialType.System_Int64: return (long)int64Value;
case SpecialType.System_Single: return (double)(float)int64Value;
case SpecialType.System_Double: return (double)int64Value;
case SpecialType.System_Decimal: return (decimal)int64Value;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.Single:
case ConstantValueTypeDiscriminator.Double:
// This code used to invoke CheckConstantBounds and return constant zero if the value is not within the target type.
// The C# spec says that in this case the result of the conversion is an unspecified value of the destination type.
// Zero is a perfectly valid unspecified value, so that behavior was formally correct.
// But it did not agree with the behavior of the native C# compiler, that apparently returned a value that
// would resulted from a runtime conversion with normal CLR overflow behavior.
// To avoid breaking programs that might accidentally rely on that unspecified behavior
// we now removed that check and just allow conversion to overflow.
double doubleValue = value.DoubleValue;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)doubleValue;
case SpecialType.System_Char: return (char)doubleValue;
case SpecialType.System_UInt16: return (ushort)doubleValue;
case SpecialType.System_UInt32: return (uint)doubleValue;
case SpecialType.System_UInt64: return (ulong)doubleValue;
case SpecialType.System_SByte: return (sbyte)doubleValue;
case SpecialType.System_Int16: return (short)doubleValue;
case SpecialType.System_Int32: return (int)doubleValue;
case SpecialType.System_Int64: return (long)doubleValue;
case SpecialType.System_Single: return (double)(float)doubleValue;
case SpecialType.System_Double: return (double)doubleValue;
case SpecialType.System_Decimal: return (value.Discriminator == ConstantValueTypeDiscriminator.Single) ? (decimal)(float)doubleValue : (decimal)doubleValue;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
case ConstantValueTypeDiscriminator.Decimal:
decimal decimalValue = CheckConstantBounds(destinationType, value.DecimalValue) ? value.DecimalValue : 0m;
switch (destinationType)
{
case SpecialType.System_Byte: return (byte)decimalValue;
case SpecialType.System_Char: return (char)decimalValue;
case SpecialType.System_UInt16: return (ushort)decimalValue;
case SpecialType.System_UInt32: return (uint)decimalValue;
case SpecialType.System_UInt64: return (ulong)decimalValue;
case SpecialType.System_SByte: return (sbyte)decimalValue;
case SpecialType.System_Int16: return (short)decimalValue;
case SpecialType.System_Int32: return (int)decimalValue;
case SpecialType.System_Int64: return (long)decimalValue;
case SpecialType.System_Single: return (double)(float)decimalValue;
case SpecialType.System_Double: return (double)decimalValue;
case SpecialType.System_Decimal: return (decimal)decimalValue;
default: throw ExceptionUtilities.UnexpectedValue(destinationType);
}
default:
throw ExceptionUtilities.UnexpectedValue(value.Discriminator);
}
}
// all cases should have been handled in the switch above.
// return value.Value;
}