c# - tipos - Double.MaxValue to integer is negative?
conversion de tipos de datos en c# (3)
La especificación de lenguaje C # (Versión 5.0) dice lo siguiente en 6.2.1 "Conversiones numéricas explícitas" (énfasis agregado):
Para una conversión de tipo flotante o doble a un tipo integral, el procesamiento depende del contexto de verificación de desbordamiento (§7.6.12) en el que tiene lugar la conversión:
En un contexto comprobado, la conversión procede de la siguiente manera:
- Si el valor del operando es NaN o infinito, se lanza una excepción System.OverflowException.
- De lo contrario, el operando de origen se redondea hacia cero al valor integral más cercano. Si este valor integral está dentro del rango del tipo de destino, este valor es el resultado de la conversión.
- De lo contrario, se lanza una excepción System.OverflowException.
En un contexto sin marcar, la conversión siempre se realiza correctamente y se realiza de la siguiente manera.
- Si el valor del operando es NaN o infinito, el resultado de la conversión es un valor no especificado del tipo de destino.
- De lo contrario, el operando de origen se redondea hacia cero al valor integral más cercano. Si este valor integral está dentro del rango del tipo de destino, este valor es el resultado de la conversión.
- De lo contrario, el resultado de la conversión es un valor no especificado del tipo de destino.
Y en 7.6.12 "Los operadores verificados y sin marcar".
Para expresiones no constantes (expresiones que se evalúan en tiempo de ejecución) que no están encerradas por ningún operador o declaración marcada o no verificada, el contexto predeterminado de verificación de desbordamiento está desactivado a menos que factores externos (como los conmutadores del compilador y la configuración del entorno de ejecución) requieran evaluación comprobada.
Para conversiones de double
a decimal
: "Si el valor de origen es NaN, infinito o demasiado grande para representarlo como decimal, se emite una excepción System.OverflowException". checked
contra lo que unchecked
está unchecked
no entra en juego (solo se trata de operaciones integrales).
¿Por qué Double.MaxValue
en un tipo integral da como resultado un valor negativo, el valor más pequeño de ese tipo?
double maxDouble = double.MaxValue; // 1.7976931348623157E+308
long maxDoubleLong = (long) maxDouble; // -9223372036854775808
Entendería un error del compilador si es demasiado grande o una OverflowException
en el tiempo de ejecución o si usaría unchecked
que la conversión podría no generar una excepción, pero el resultado se vuelve indefinido e incorrecto (negativo).
También es extraño que el valor sea long.MinValue
:
bool sameAsLongMin = maxDoubleLong == long.MinValue; // true
Por cierto, lo mismo sucede si lo int
a int
:
int maxDoubleInt = (int)maxDouble; // -2147483648
bool sameAsIntMin = maxDoubleInt == int.MinValue; // true
Si intento convertirlo en decimal
obtengo una OverflowException
en tiempo de ejecución
decimal maxDoubleDec = (decimal)maxDouble; // nope
Actualización : parece que las respuestas de Michael y Barre dan en el clavo en la cabeza, si lo utilizo explícitamente, obtengo una OverflowException
:
checked
{
double maxDouble = double.MaxValue; // 1.7976931348623157E+308
long maxDoubleLong = (long) maxDouble; // nope
}
Parece que el comportamiento predeterminado aquí unchecked
está unchecked
, es decir, a menos que especifique explícitamente que esté checked
, el desbordamiento no se detecte:
double maxDouble = double.MaxValue; // 1.7976931348623157E+308
long uncheckedMaxDoubleLong = (long)maxDouble; // -9223372036854775808
long checkedMaxDoubleLong = checked((long)maxDouble); // ** Overflow Exception
En retrospectiva, no es aconsejable intentar la conversión directa de double
a long
sin validar o restringir la entrada debido a dos aspectos:
- desajustes de rango numérico / potencial de desbordamiento
- consideraciones de redondeo
Por lo tanto, una mejor apuesta aquí podría haber sido utilizar Convert.ToInt64
:
var convertedDouble = Convert.ToInt64(maxDouble); // ** OverflowException
Como esto hace internamente el cheque por usted, y toma una opinión sobre el redondeo, a saber:
return checked((long)Math.Round(value));
Quizás no sea una respuesta completa, pero la Especificación de lenguaje C # (§6.2.1) dice esto:
En un contexto sin marcar, la conversión siempre se realiza correctamente y se realiza de la siguiente manera.
• Si el valor del operando es NaN o infinito, el resultado de la conversión es un valor no especificado del tipo de destino.
• De lo contrario, el operando fuente se redondea hacia cero al valor integral más cercano. Si este valor integral está dentro del rango del tipo de destino, este valor es el resultado de la conversión.
• De lo contrario, el resultado de la conversión es un valor no especificado del tipo de destino.
(énfasis mío).
(Michael Burr respondió al mismo tiempo que yo, y también incluyó información sobre el contexto predeterminado checked
/ unchecked
marcar en C #, ver comentarios a continuación, por lo que esta respuesta es en gran medida redundante ahora).
Edición 1: Tenga en cuenta que si la conversión se realiza en tiempo de compilación (conversión de expresión constante), las reglas son un poco diferentes. Intente modificar su variable maxDouble
con el modificador const
. El compilador de C # podrá ver los valores, y requerirá que usted sea explícito acerca de lo que unchecked
.
Edición 2: en mi versión del tiempo de ejecución (.NET 4.5 para Windows 8.1), el siguiente código:
double d1 = double.PositiveInfinity;
double d2 = double.MaxValue;
double d3 = 2.3e23;
double d4 = double.NaN;
double d5 = -2.3e23;
double d6 = double.MinValue;
double d7 = double.NegativeInfinity;
Console.WriteLine((long)d1);
Console.WriteLine((long)d2);
Console.WriteLine((long)d3);
Console.WriteLine((long)d4);
Console.WriteLine((long)d5);
Console.WriteLine((long)d6);
Console.WriteLine((long)d7);
da:
-9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808 -9223372036854775808
por lo que parece que el "valor no especificado" es de hecho "siempre" MinValue
del tipo de destino, en esta implementación.