C#
tiene el decimal
tipo que se usa para los números que necesitan una representación exacta en la base 10. Por ejemplo, 0.1
no se puede representar en la base 2 (por ejemplo, float
y double
) y siempre será una aproximación cuando se almacena en variables que son de estos tipos.
Me preguntaba si el hecho inverso también era posible. ¿Hay números que no son representables en la base 10 pero que se pueden representar en la base 2 (en cuyo caso me gustaría usar un en float
lugar de un decimal
para manejarlos)?
0.11_b2
, escríbelo como0.5 + 0.5 * 0.5
. ¿Hay algún paso que pueda fallar o dar como resultado un decimal repetido? Personalmente, encuentro que este ejercicio hace un gran trabajo al transmitir una intuición sobre los números de base 2. Supongo que uno podría ir un paso más allá y convertir este ejercicio en una prueba por construcción.0.0999999....998..
exactamente, pero no el número completo0.1
; las aproximaciones como redondear al centésimo más cercano0.100
son un problema de implementación que implica no mostrarle todos los dígitos y redondearlo.Respuestas:
Aquí está la clave de su dilema:
10
es el producto de2
y5
. Puede representar cualquier número exactamente en la base 10 decimales que es k * 1/2 n * 1/5 m , dondek
,n
ym
son números enteros.Alternativamente redactado: si el número
n
en 1 / n contiene un factor que no es parte de los factores de la base, el número no podrá representarse exactamente en un número fijo de dígitos en el binario / decimal / cualquier expansión de ese número: tendrá una parte repetida. Por ejemplo 1/15 = 0.0666666666 .... porque 3 (15 = 3 * 5) no es un factor de 10.Por lo tanto, cualquier cosa que pueda representarse exactamente en la base 2 (k * 1/2 n ) puede representarse exactamente en la base 10.
Más allá de eso, está la cuestión de cuántos dígitos / bits está utilizando para representar el número. Hay algunos números que pueden representarse exactamente en alguna base, pero se necesitan más de un número de dígitos / bits para hacerlo.
En binario, el número 1/10 que es convenientemente 0.1 en decimal no puede representarse como un número que puede representarse en un número fijo de bits en binario. En cambio, el número es 0.00011001100110011 ... 2 (con la parte 0011 repitiéndose para siempre).
Veamos el número 1 2 /1010 2 un poco más de cerca.
Este es exactamente el mismo tipo de cosas que obtienes cuando intentas hacer la división larga para 1/3.
1/10, cuando se factoriza es 1 / (2 1 * 5 1 ). Para la base 10 (o cualquier múltiplo de 10), este número termina y se conoce como un número regular . Una expansión decimal que se repite se conoce como decimal decimal , y los números que continúan para siempre sin repetirse son números irracionales.
La matemática detrás de esta ahonda en el pequeño teorema de Fermat ... y una vez que empieza a decir Fermat o teorema, se convierte en una cuestión Math.SE .
La respuesta es no'.
Entonces, en este punto, todos deberíamos tener claro que cada expansión binaria de longitud fija de un número racional puede representarse como una expansión decimal de longitud fija.
Veamos más de cerca el decimal en C # que nos lleva al punto flotante decimal en .NET y dado el autor, aceptaré que así es como funciona.
Señalaré de inmediato que, debido a esta implementación, hay números en el
double
tipo que no se pueden representardecimal
, aquellos que están fuera del rango.Double.Epsilon
es lo4.94065645841247e-324
que no puede representarse en adecimal
, pero puede en adouble
.Sin embargo, dentro del rango que puede representar el decimal, tiene más bits de precisión que otros tipos nativos y puede representarlos sin error.
Hay algunos otros tipos flotando. Hay un BigInteger en C # que puede representar un entero arbitrariamente grande. No hay equivalente al BigDecimal de Java (que puede representar números con dígitos decimales de hasta 2 32 dígitos de largo, que es un rango considerable) exactamente . Sin embargo, si hurgas un poco , puedes encontrar implementaciones enrolladas a mano.
Hay algunos lenguajes que también tienen un tipo de datos racional que le permite representar exactamente los racionales (de modo que 1/3 es en realidad 1/3).
Específicamente para C # y la elección de flotante o racional, diferiré a Jon Skeet de la pinta flotante Decimal en .NET :
fuente
n = 15
yb = 10
no son relativamente primos ("no comparten factores positivos comunes (divisores) excepto 1") porque comparten 5 como factor. La clave es que no todos los factores de 15 (5 y 3) no son también factores de 10. (Aparte: ¿hay una palabra para indicar números que comparten o no todos los factores comunes?) Creo que eso es claramente envuelto en suk, n, m
ecuación, pero para entenderlo realmente, necesitaría ver un diagrama en 3D. En cualquier caso, bien merecido +1 para ti.Una vez que salga del rango de valores aceptables, la respuesta es sí. Dicho esto, casi cualquier cosa dentro del rango tendrá una representación. C # Referencia decimal Aunque no se indica en la especificación, los números irracionales no se pueden representar con exactitud (p. Ej., E 1 , pi, raíz cuadrada de 2, etc.).
1 Gracias a MichaelT por recordarme otro número irracional.
fuente
e
(2.71 ...). El log natural - ln (x) es log base e. Por lo tanto, existen bases irracionales y son útiles. No estoy seguro de la utilidad particular de la base pi, pero eso no significa que no se use en alguna parte.Un tipo de punto flotante de base dos podría representar con precisión muchos valores que no podría un tipo de base diez del mismo tamaño . Cualquier valor que sería exactamente representable por un tipo base-2 de algún tamaño sería exactamente representable en un tipo base-diez de tamaño suficiente. El tamaño requerido para un tipo de base puramente diez para representar todos los valores de un número de punto flotante binario dependería del rango de exponente del tipo binario; cientos de bits por a
float
, o miles por adouble
.Dicho esto, el
Decimal
tipo es lo suficientemente grande como para que hubiera sido posible utilizarlo como un tipo "universal" capaz de mantener el valor de cualquier otra primitiva numérica y proporcionar otras características adicionales además (si nada más, use un bit para indicar si el valor almacenado es el resultado de convertir adouble
, y si ese bit está establecido, use 64 bits para mantener el valor en cuestión). Microsoft optó por no hacer eso, sin embargo. Como resultado, la conversión dedouble
aDecimal
fallará por completo para valores grandes, hará que los valores pequeños se redondeen al 1E-28 más cercano. Además, incluso dentro del rango dinámico dedecimal
, el método de conversión no será "ida y vuelta". Por ejemplo, evaluar 1.0 / 3.0 como doble producirá 0.3333333333333333148, pero convertir eso a decimal generará 0.333333333333333m y convertir eso nuevamente a doble produciría 0.3333333333333329818.fuente