Pregunta

Refrescante en flotante puntos (también PDF), IEEE-754 y tomar parte en esta discusión en punto flotante de redondeo en la conversión a cadenas de, y me trajo a tinker:¿cómo puedo obtener el valor máximo y mínimo para un determinado número de punto flotante, cuyas representaciones binarias son iguales.

Descargo de responsabilidad:para esta discusión, me gusta el stick de 32 bits y 64 bits de punto flotante, como se describe por IEEE-754.Yo no estoy interesado en la extensión de punto flotante (80 bits) o quads (128 bits IEEE-754-2008) o cualquier otro estándar (IEEE-854).

De fondo:Los equipos son malos en la representación de 0.1 en representación binaria.En C#, un flotador representa esto como 3DCCCCCD internamente (C# utiliza ronda-para-la más cercana) y un doble como 3FB999999999999A.Los mismos patrones de bits que se utilizan para el decimal 0.100000005 (float) y 0.1000000000000000124 (doble), pero no para 0.1000000000000000144 (doble).

Para mayor comodidad, el siguiente código de C# se le da a estas representaciones internas:

string GetHex(float f)
{
    return BitConverter.ToUInt32(BitConverter.GetBytes(f), 0).ToString("X");
}

string GetHex(double d)
{
    return BitConverter.ToUInt64(BitConverter.GetBytes(d), 0).ToString("X");
}

// float
Console.WriteLine(GetHex(0.1F));

// double 
Console.WriteLine(GetHex(0.1));

En el caso de 0.1, no existe un número decimal que está representado con el mismo patrón de bits, cualquier 0.99...99 dará lugar a una diferente representación de bit (es decir, de flotación para 0.999999937 los rendimientos 3F7FFFFF internamente).

Mi pregunta es simple:¿cómo puedo encontrar el más bajo y el más alto valor decimal para un determinado float (o doble) que internamente se almacenan en la misma representación binaria.

Por qué:(Sé que te voy a pedir) para encontrar el error de redondeo en .NET cuando se convierte en una cadena y cuando se convierte en una cadena, para encontrar el interno valor exacto y a entender mi propia errores de redondeo mejor.

Supongo que es algo así como:tomar la mantisa, quite el resto, obtener su valor exacto, conseguir uno (mantisa bits) mayor, y calcular la media:cualquier cosa por debajo del que se dará el mismo patrón de bits.Mi principal problema es:cómo conseguir que la parte fraccionaria como entero (manipulación de bits que no era mi mejor activo). Jon Skeet del DoubleConverter la clase puede ser útil.

¿Fue útil?

Solución

Una forma de llegar a tu pregunta es para averiguar el tamaño de un ULP, o Unit en el Last Pde encaje, de su número en punto flotante.Simplificando un poco, esta es la distancia entre un determinado número de punto flotante y el siguiente número más grande.De nuevo, simplificando un poco, dado un representable en coma flotante valor de x, cualquier decimal string cuyo valor es entre (x - 1/2 ulp) y (x + 1/2 ulp) será redondeado a x cuando se convierte a un valor de punto flotante.

El truco está en que (x +/- 1/2 ulp) no es representable número de punto flotante, así que en realidad el cálculo de su valor requiere el uso de un amplio tipo de punto flotante (si está disponible) o a un arbitrario ancho grande decimal o de tipo similar para hacer el cálculo.

¿Cómo encontrar el tamaño de un ulp?Una manera relativamente fácil, es más o menos lo que usted sugiere, aquí escrito es C-ish pseudocódigo porque no sé C#:

float absX = absoluteValue(x);
uint32_t bitPattern = getRepresentationOfFloat(absx);
bitPattern++;
float nextFloatNumber = getFloatFromRepresentation(bitPattern);
float ulpOfX = (nextFloatNumber - absX);

Esto funciona debido a que la adición de uno al patrón de bits de x corresponde exactamente a la adición de uno ulp para el valor de x.No redondeo de punto flotante se produce en la sustracción debido a que los valores involucrados están tan cerca (en particular, existe un teorema de la ieee-754 de aritmética de punto flotante que si dos números x e y satisfacen y/2 <= x <= 2y, entonces x - y se calcula exactamente).La única advertencias aquí son:

  1. si x pasa a ser el más grande finito número de punto flotante, esto no funciona (se devolverá inf, que es claramente errónea).
  2. si la plataforma no admite correctamente gradual de subdesbordamiento (por decir un dispositivo integrado, que se ejecuta en ras-al-modo cero), esto no funciona para valores muy pequeños de x.

Suena como que usted está probablemente no va a estar en cualquiera de esas situaciones, así que esto debería funcionar igual de bien para sus fines.

Ahora que usted sabe lo que es una práctica discriminatoria de x es, usted puede encontrar el intervalo de valores que se redondea a x.Usted puede calcular ulp(x)/2 exactamente en punto flotante, porque de punto flotante de la división por 2 es exacto (de nuevo, salvo que se subdesbordamiento).A continuación, sólo necesita calcular el valor de x +/- ulp(x)/2 apto mayor tipo de punto flotante (double si usted está interesado en float) o en un Gran tipo Decimal, y su intervalo.

Hice un par de supuestos simplificadores a través de esta explicación.Si usted necesita para realmente ser escrito exactamente, dejar un comentario y voy a ampliar sobre las partes que son un poco borrosa cuando tengo la oportunidad.


Otra nota la siguiente declaración en su pregunta:

En el caso de 0.1, no es menor número decimal que representa con el mismo patrón de bits

es incorrecta.Usted acaba de pasar a estar buscando en el lugar equivocado valores (0.999999...en lugar de 0.099999...-- un sencillo error de hacer).

Otros consejos

Python 3.1 implementado simplemente algo como esto: la lista de cambios (desplazarse un poco) , informe de error .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top