game error arithmetic floating-point

floating-point - error - floating point representation



¿Qué es un "valor de sesgo" de los números de coma flotante? (3)

Agregar más detalles a las respuestas anteriores.

Para representar 0 , infinity y NaN (Not-a-Number) en coma flotante, IEEE decidió usar valores de codificación especiales.

  • Si todos los bits del campo de exponente se establecen en 0, entonces el número de coma flotante es 0.0.

  • Si todos los bits del campo de exponente se establecen en 1 y todos los bits de la parte de fracción son 0, entonces el número de coma flotante es infinito .

  • Si todos los bits del campo de exponente se establecen en 1 y todos los bits de la parte de fracción no son iguales a 0, entonces el número de punto flotante es NaN .

Entonces, en precisión simple, tenemos 8 bits para representar el campo exponente y hay 2 valores especiales, por lo que básicamente tenemos 256 - 2 = 254 valores que se pueden representar en el exponente. Entonces, podemos representar efectivamente -126 a 127 en el exponente, es decir, 254 valores (126 + 127 + 1), 1 se agrega para 0.

Al aprender cómo se representan los números de coma flotante en las computadoras, he encontrado el término "valor de sesgo" que no entiendo del todo.

El valor de polarización en números de coma flotante tiene que ver con el negativo y el positivo de la parte exponente de un número de coma flotante.

El valor de polarización de un número de coma flotante es 127, lo que significa que 127 siempre se agrega a la parte exponente de un número de coma flotante. ¿Cómo ayuda esto a determinar si el exponente es negativo o positivo o no?


En punto flotante de precisión simple, obtienes 8 bits para almacenar el exponente. En lugar de almacenarlo como un número de complemento de dos firmado, se decidió que sería más fácil agregar 127 solo al exponente (ya que el más bajo podría estar en 8 bits firmado es -127) y simplemente almacenarlo como un número sin firmar . Si el valor almacenado es mayor que el sesgo, eso significa que el valor del exponente es positivo, si es menor que el sesgo, es negativo, si es igual, es cero.


b0lt ya ha explicado cómo funciona el sesgo. En una suposición, tal vez le gustaría saber por qué usan una representación sesgada aquí, aunque prácticamente todas las computadoras modernas usan el complemento de dos esencialmente en cualquier otro lugar (e incluso las máquinas que no usan complemento de dos, usan complemento o magnitud de signo) , no sesgo).

Uno de los objetivos de los estándares de punto flotante de IEEE era que podía tratar los bits de un número de coma flotante como un entero (firmado) del mismo tamaño, y si los comparaba de esa manera, los valores se clasificarían en el mismo orden que los números de coma flotante que representaron.

Si utilizó una representación de complemento a dos para el exponente, un pequeño número positivo (es decir, con un exponente negativo) se vería como un entero muy grande porque se establecería el segundo MSB. Al usar una representación de sesgo en su lugar, no se topa con eso: un exponente más pequeño en el número de punto flotante siempre se ve como un entero más pequeño.

FWIW, este es también el motivo por el cual los números de punto flotante se organizan generalmente con el signo primero, luego el exponente y finalmente el significado en los bits menos significativos: de esta manera, puede tomar números de coma flotante positivos, tratar esos bits como enteros y ordenarlos Cuando lo haga, el resultado tendrá los números de coma flotante en el orden correcto. Por ejemplo:

#include <vector> #include <algorithm> #include <iostream> int main() { // some arbitrary floating point values std::vector<double> vals = { 1e21, 1, 2.2, 2, 123, 1.1, 0.0001, 3, 17 }; std::vector<long long> ivals; // Take those floating point values, and treat the bits as integers: for (auto &&v : vals) ivals.push_back(*reinterpret_cast<long long *>(&v)); // Sort them as integers: std::sort(ivals.begin(), ivals.end()); // Print out both the integers and the floating point value those bits represent: for (auto &&i : ivals) std::cout << i << "/t(" << *reinterpret_cast<double *>(&i) << ")/n"; }

Cuando ejecutamos esto, el resultado es así:

4547007122018943789 (0.0001) 4607182418800017408 (1) 4607632778762754458 (1.1) 4611686018427387904 (2) 4612136378390124954 (2.2) 4613937818241073152 (3) 4625478292286210048 (17) 4638355772470722560 (123) 4921056587992461136 (1e+21)

Como puede ver, aunque los clasificamos como enteros, los números de coma flotante que esos bits representan también salen en el orden correcto.

Esto tiene limitaciones con respecto a los números de coma flotante. Si bien todas las computadoras (no antiguas) concuerdan en la representación de los números positivos, hay tres representaciones que se han utilizado (bastante recientemente) para los números con signo: magnitud firmada, complemento de uno y complemento de dos.

Solo tratar los bits como un número entero y compararlos funcionará bien en una computadora que use una representación de magnitud firmada para enteros. Para las computadoras que usan complemento de uno o complemento de dos, los números negativos se ordenarán en orden invertido. Como esta sigue siendo una regla simple, es bastante fácil escribir código que funcione con ella. Si cambiamos la llamada de sort anterior a algo como esto:

std::sort(ivals.begin(), ivals.end(), [](auto a, auto b) { if (a < 0.0 && b < 0.0) return b < a; return a < b; } );

... entonces clasificará correctamente los números positivos y negativos. Por ejemplo, la entrada de:

std::vector<double> vals = { 1e21, 1, 2.2, 2, 123, 1.1, 0.0001, 3, 17, -0.001, -0.00101, -1e22 };

Producirá un resultado de:

-4287162073302051438 (-1e+22) -4661071411077222194 (-0.00101) -4661117527937406468 (-0.001) 4547007122018943789 (0.0001) 4607182418800017408 (1) 4607632778762754458 (1.1) 4611686018427387904 (2) 4612136378390124954 (2.2) 4613937818241073152 (3) 4625478292286210048 (17) 4638355772470722560 (123) 4921056587992461136 (1e+21)