variable c# .net decimal epsilon

variable - C#Decimal.Epsilon



decimal c# (3)

¿Por qué el tipo de datos Decimal no tiene el campo Epsilon ?

En el manual , el rango de valores decimal es de ± 1.0 × 10e − 28 a ± 7.9 × 10e28.

La descripción de Double.Epsilon :

Representa el valor Double positivo más pequeño mayor que cero

Por lo que parece, Decimal tiene un valor (no trivial) también. Pero ¿por qué no es fácilmente accesible?

Entiendo que +1.0 × 10e − 28 es exactamente el valor decimal más pequeño y positivo mayor que cero:

decimal Decimal_Epsilon = new decimal(1, 0, 0, false, 28); //1e-28m;

Por cierto, hay un par de preguntas que proporcionan información sobre la representación interna del tipo de datos decimal:

Aquí hay un ejemplo donde Epsilon sería útil.

Digamos que tengo una suma ponderada de valores de algún conjunto de muestreo y suma de pesos (o conteo) de muestras tomadas. Ahora quiero calcular el valor medio ponderado. Pero sé que la suma de pesos (o conteo) aún puede ser cero. Para evitar la división por cero, podría hacerlo if... else... y verificar el cero. O podría escribir así:

T weighted_mean = weighted_sum / (weighted_count + T.Epsilon)

Este código es más corto en mi ojo. O, alternativamente, puedo omitir el + T.Epsilon y en su lugar inicializar con:

T weighted_count = T.Epsilon;

Puedo hacer esto cuando sé que los valores de los pesos reales nunca están cerca de Epsilon .

Y para algunos tipos de datos y casos de uso, esto puede ser incluso más rápido, ya que no involucra sucursales. Según tengo entendido, los procesadores no pueden tomar ambas ramas para el cálculo, incluso cuando las ramas son cortas. Y es posible que sepa que los ceros se producen aleatoriamente a una tasa del 50%: =) Para el Decima l, es probable que el aspecto de la velocidad no sea importante o incluso sea positivamente útil en el primer caso.

Mi código puede ser genérico (por ejemplo, generado) y no quiero escribir código separado para decimales. Por lo tanto, nos gustaría ver que el Decimal tenga una interfaz similar a la de otros tipos de valores reales.


Contrariamente a esa definición, épsilon es en realidad un concepto utilizado para eliminar la ambigüedad de la conversión entre representaciones binarias y decimales de valores. Por ejemplo, 0.1 en decimal no tiene una representación binaria simple, por lo que cuando declara un doble como 0.1, en realidad está estableciendo ese valor en una representación aproximada en binario. Si agrega ese número de representación binario a sí mismo 10 veces (matemáticamente), obtendrá un número que es aproximadamente 1.0, pero no exactamente. Un épsilon te permitirá manipular los cálculos matemáticos y decir que la representación aproximada de 0.1 agregada a sí misma puede considerarse equivalente a la representación aproximada de 0.2.

Esta aproximación causada por la naturaleza de las representaciones no es necesaria para el tipo de valor decimal, que ya es una representación decimal. Esta es la razón por la que cada vez que necesite tratar con números reales y números que no son aproximaciones (es decir, dinero en lugar de masa), el tipo de punto flotante correcto que se debe usar es decimal y no el doble.


El número más pequeño que puedo calcular para el decimal es:

public static decimal DecimalEpsilon = (decimal) (1 / Math.Pow(10, 28));

Esto es de ejecutar lo siguiente en una ventana interactiva de C #:

for (int power = 0; power <= 50; power++) { Console.WriteLine($"1 / 10^{power} = {((decimal)(1 / (Math.Pow(10, power))))}"); }

Que tiene la siguiente salida:

1 / 10^27 = 0.000000000000000000000000001 1 / 10^28 = 0.0000000000000000000000000001 1 / 10^29 = 0 1 / 10^30 = 0


Si solo pensamos en la mantisa de 96 bits, se puede pensar que el tipo decimal tiene una épsilon igual al recíproco de un BigInteger construido con 96 bits establecidos. Obviamente, es un número demasiado pequeño para representarlo con los tipos de valores intrínsecos actuales.

En otras palabras, necesitaríamos un valor "BigReal" para representar una fracción tan pequeña.

Y, francamente, eso es sólo la "granularidad" del épsilon. Entonces necesitaríamos conocer el exponente (los bits 16-23 del Int32 más alto de GetBits ()) para llegar al épsilon "real" para un valor decimal GIVEN.

Obviamente, el significado de "épsilon" para Decimal es variable. Puede utilizar la épsilon de granularidad con el exponente y crear una épsilon específica para un decimal GIVEN.

Pero considere la siguiente situación bastante problemática:

[TestMethod] public void RealEpsilonTest() { var dec1 = Decimal.Parse("1.0"); var dec2 = Decimal.Parse("1.00"); Console.WriteLine(BitPrinter.Print(dec1, " ")); Console.WriteLine(BitPrinter.Print(dec2, " ")); }

DEC1: 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001010

DEC2; 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 01100100

A pesar de que los dos valores analizados parecen ser iguales, ¡su representación no es la misma!

La moraleja de la historia es ... ¡ten mucho cuidado de que entiendas bien a Decimal antes de PENSAR que lo entiendes!

INSINUACIÓN:

Si desea la épsilon para Decimal (teóricamente), cree un UNION ([StructLayout[LayoutKind.Explicit]) combinando Decimal (128 bits) y BigInteger (96 bits) y Exponent (8 bits). El captador para Epsilon devolvería el valor BigReal correcto basado en epsilon y exponente de granularidad; asumiendo, por supuesto, la existencia de una definición de BigReal (que he estado escuchando durante bastante tiempo, vendrá).

La granularidad de épsilon, por cierto, sería un campo constante o estático ...

static grain = new BigReal(1 / new BitInteger(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF });

TAREA: ¿El último byte a BigInteger debe ser 0xFF o 0x7F (o algo más)?

PD: Si todo eso suena bastante más complicado de lo que esperabas, ... considera que la ciencia de la paga paga razonablemente bien. / -)