type tipos operator objetos datos convert conversion cast another c# .net casting type-conversion

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.