Representación binaria de los números decimales

Figura
Figura. Representación con 8 bits fraccionarios

Los números decimales se tienen que representar en binario. Por lo tanto hay que saber al menos como convertir un número decimal en binario y al revés. Presentaremos en este tema un convertidor binario-decimal para practicar estas conversiones.

Como la representación binaria de un número decimal siempre estará limitada a un número determinado de bits, eso introducirá limitaciones en la precisión que hay que conocer. Por ejemplo, en la Figura puede ver que con una representación de 8 bits fraccionarios sólo podemos precisar números decimales con 2 dígitos fraccionarios. Así desde 3.008 hasta 3.011 se representan con el mismo binario 11.00000010.

Otro tema que afecta a los números tiene que ver con los cálculos de operaciones aritméticas. Se presentará también en este tema una calculadora binaria para realizar las operación básicas y que nos servirá en temas posteriores para resolver algunas dudas.

Convertir un número binario real en decimal es bastante simple. El valor 1001.1 se convierte haciendo la suma de cada bit i-ésimo de la parte entera multiplicado por potencias de dos d×2i. Mientras que para la parte fraccionaria en lugar de multiplicar dividiremos, o lo que es lo mismo, multiplicaremos con potencias negativas d×2-i.

1×23 + 0×22 + 0×21 + 1×20 + 1×2-1 = 8 + 0 + 0 + 1 + 0.5 = 9.5

En cambio convertir un real decimal en binario tiene algo más de trabajo. El valor 9.5 lo separamos en parte entera y fraccionaria. Con la parte entera hacemos una división entera 9/2=4 obteniéndose un resto 1, por lo que el primer dígito será 1. Con el cociente anterior volvemos a dividir 4/2=2 y resto 0. Luego 2/2=1, resto 0 y, finalmente, 1/2=0, resto 1, obteniéndose el binario 1001.

Para convertir la parte fraccionaria 0.5 la multiplicamos por dos sucesivamente y observamos si es menor que uno, con lo que el dígito será 0. En otro caso el dígito será 1. Así tenemos 0.5×2 = 1 ≥ 1 por lo que el primer dígito será 1. Si el último resultado de la multiplicación tuviera fracción, volveríamos a coger la parte fraccionaria repitiendo el proceso. Como no hay fracción finalizamos con el binario 0.1, así que el binario completo correspondiente al decimal real 9.5 es el binario real 1001.1 tal como se esperaba.

Los números reales no enteros que sean el resultado de una división por una potencia de dos tienen un número determinado de dígitos binarios. Decimos que tienen una representación binaria exacta. Así tenemos 9.5 = 19/2 cuyo binario real vimos que es 1001.1. Para el resto de números reales no enteros la parte binaria fraccionaria contiene un número infinito de dígitos binarios o bits. Como para el decimal real 0.7:

0.7×2 = 1.4 ≥ 1 ⇒ 1 0.4×2 = 0.8 < 1 ⇒ 0 0.8×2 = 1.6 ≥ 1 ⇒ 1 0.6×2 = 1.2 ≥ 1 ⇒ 1 0.2×2 = 0.4 < 1 ⇒ 0 0.4×2 = 0.8 < 1 ⇒ 0 0.8×2 = 1.6 ≥ 1 ⇒ 1 0.6×2 = 1.2 ≥ 1 ⇒ 1 0.2×2 = 0.4 < 1 ⇒ 0 0.4×2 = 0.8 < 1 ⇒ 0 ...

Observe que el proceso no ha finalizado, obteniéndose 0.1011001100··· con la secuencia 1100 repitiéndose indefinidamente. Son representaciones binarias no exactas. El número anterior es un racional, pues puede ser expresado como 0.7 = 7/10, originando secuencias que se repiten sin fin. Mientras que para los irracionales no se generan secuencias, aunque también sus representaciones no son exactas.

Desarrollar un ejemplo a modo de convertidor binario-decimal como el siguiente ayuda bastante a entender este tema:

Ejemplo: Convertidor binario-decimal

Convertir (Sólo en conversión decimal a binario)
Resultado:
Dígitos significativos:
Este ejemplo usa ES6 en modo estricto. Puedes consultar el código JS original de este ejemplo.

Con el convertidor anterior hemos de tener en cuenta que usa JavaScript para realizar los cálculos. Así que está limitado por el propio formato IEEE754 que veremos en el tema siguiente. Por ejemplo, el número 1.3 tiene por binario 1.0100110011··· repitiéndose la secuencia 0011 hasta el infinito. Si usamos el convertidor anterior para obtener el binario de 1.3 usando una fracción con 64 bits veremos que a partir de cierta posición se completa con ceros. A partir de ahí JavaScript no puede precisar más:

1.3 = 1.0100110011001100110011001100110011001100110011001100110011001100··· 1.3 ≈ 1.0100110011001100110011001100110011001100110011001100110000000000

Para estar seguros sólo deberíamos tomar los dígitos significativos, con lo que sólo podríamos contar con 55 bits significativos (o 54 fraccionarios) para ese número 1.3:

1.3 ≈ 1.0100110011001100110011001100110011001100110011001100110000000000

También se presenta la conversión de un decimal a binario usando toString(2). Pero ha de tenerse en cuenta que ese binario se obtiene del formato IEEE754 tras un redondeo interno que lleva a cabo el formato. Se observa que para el mismo decimal 1.3 se presentan 53 bits significativos, cambiando el bit menos significativo de cero a uno:

1.3 = 1.010011001100110011001100110011001100110011001100110011001100··· 1.3 ≈ 1.0100110011001100110011001100110011001100110011001101

Aunque con limitaciones, este conversor nos permitirá solucionar muchos problemas que atacaremos en este y siguientes temas.

Precisión de la representación binaria de los números decimales

Conocer que hay números que no tienen una representación binaria exacta es importante, puesto que no conseguimos la misma precisión que trabajando con decimales. Representemos el decimal 0.7 usando el valor binario 0.10110011, con sólo 8 dígitos fraccionarios. Si obtenemos el decimal de este binario será 2-1 + 2-3 + 2-4 + 2-7 + 2-8 = 0.69921875, cometiendo un error de 0.7 - 0.69921875 = 0.00078125.

El máximo error que podemos cometer usando sólo ocho dígitos binarios en la parte fraccionaria siempre será menor que 2-8 = 0.00390625. Supongamos el binario 0.00000000111111···, con ocho ceros en la parte fraccionaria representable y el resto infinitos unos, lo que es aproximadamente igual a 2-8. Así que 0.00000000111111··· ≈ 0.00000001. Si recortamos sin redondear el binario 11.00000000111111··· a sólo ocho dígitos fraccionarios tendríamos 11.00000000, siendo el error cometido 0.00000000111111··· cuyo valor es 2-8 = 0.00390625. Denominemos a este valor como Epsilon, así que ε = 2-8 para este sistema binario de 8 bits.

Otro problema con la representación binaria de fracciones tiene que ver con la precisión. Sigamos con el número real 3.00390625 cuyo binario es exactamente 11.00000001, con un número exacto de ocho dígitos fraccionarios. Si incrementamos un bit tendríamos 11.00000010, cuyo valor decimal es 3.0078125 = 3.00390625 + 2-8. Usando la la denominación anterior sería 3.00390625 + ε.

Esto quiere decir que todos los reales en el rango r ≥ 3.00390625 y r < 3.0078125 tienen la misma representación binaria 11.00000001 usando sólo ocho bits fraccionarios. Esto lo puede ver en este esquema donde partimos de ese binario y no cambia hasta alcanzar 3.0078125 = 3.00390625 + ε. Por lo tanto hay 390625 valores no representables entre r > 3.00390625 y r < 3.00781250 usando la misma cantidad de dígitos decimales en la parte fraccionaria del número real decimal.

3.00390625 ⇒ 11.00000001 3.00390626 ⇒ 11.0000000100000000000000000010101··· 3.00390627 ⇒ 11.0000000100000000000000000101010··· 3.00390628 ⇒ 11.0000000100000000000000001000000··· ... 3.00781249 ⇒ 11.00000001111111111111111111010··· 3.00781250 ⇒ 11.00000010

En general para cualquier número real de la forma r = n + ε siendo n un entero, tendrá una representación exacta binaria fraccionaria. Y que entre r ≥ n + ε y r < n + 2ε todos los reales tendrán la misma representación binaria. Otra forma de expresar esto es que el máximo espacio entre cualquier real r y el más cercano valor binario exacto no estará más lejos de |r| + ε.

En los temas siguientes veremos que para el formato IEEE754 se define EPSILON (ε) como la diferencia mínima entre 1 y el siguiente número representable. Así en nuestro formato de 8 bits podemos representar los binarios 1.00000000 y el siguiente 1.00000001 que se corresponden con los decimales 1 y 1+ε = 1.00390625. Pero cualquier número en medio no es representable.

Para ocho bits fraccionarios tenemos ε = 2-8 = 0.00390625 = 3.90625×10-3. El exponente de la potencia de la base decimal nos da una idea de que sólo podemos contar con no más de tres decimales reales para manejar con precisión su representación binaria. De hecho haciendo el cálculo 8×log10(2) ≈ 2.408 vemos que el valor de decimales necesarios no será mayor que tres. Sólo podemos contar con dos dígitos para obtener con seguridad una total precisión. En la siguiente serie se observa que hay precisión sólo para los valores 3.00 y 3.01, pues si agregamos otro dígito decimal vemos que hay más de un binario para representarlo. Así entre 3.002 y 3.007 se representan con el mismo binario 11.00000001 usando sólo ocho bits fraccionarios.

3.000 ⇒ 11.00000000 3.001 ⇒ 11.00000000 3.002 ⇒ 11.00000001 3.003 ⇒ 11.00000001 3.004 ⇒ 11.00000001 3.005 ⇒ 11.00000001 3.006 ⇒ 11.00000001 3.007 ⇒ 11.00000001 3.008 ⇒ 11.00000010 3.009 ⇒ 11.00000010 3.010 ⇒ 11.00000010 3.011 ⇒ 11.00000010 3.012 ⇒ 11.00000011 3.013 ⇒ 11.00000011 ...

Dos dígitos decimales reales son muy pocos para hacer cálculos precisos. Si utilizáramos 31 bits fraccionarios tendríamos 31×log10(2) ≈ 9.332, por lo que dispondríamos de una precisión de 9 dígitos fraccionarios decimales. Si además para la parte entera usáramos 32 bits podríamos representar números hasta 232 = 4294967296. Podríamos construir un registro de 64 bits así:

  • 1 bit para el signo. Si es 0 será positivo y si es 1 será negativo.
  • 32 bits para la parte entera.
  • 31 bits para la parte fraccionaria.

Con eso tendríamos las siguientes limitaciones:

  • Rango real: ±4.294967296999999999 × 109
  • Valor absoluto real mínimo: 10-9
  • Número de decimales reales significativos: 9
  • Valor absoluto entero máximo: 232 = 4.294967296 × 109
  • ε = 2-31 = 4.656612873077393 × 10-10

Con el formato de coma flotante IEEE754, que veremos en un tema posterior, usa también un registro de 64 bits con los siguientes límites:

  • Rango real: ±1.7976931348623157 × 10308
  • Valor absoluto real mínimo: 5 × 10-324
  • Número de decimales reales significativos: de 15 a 17
  • Valor absoluto entero máximo: 9.007199254740991 × 1015
  • ε = 2-52 = 2.220446049250313 × 10-16

Es evidente la sustancial mejora en los rangos y la precisión usando una misma cantidad de 64 bits. Hemos mejorado mucho, pero aún así el formato IEEE754 tiene también limitaciones como veremos en temas posteriores.

Operaciones aritméticas binarias

Es una obviedad decir que JavaScript realiza las operaciones aritméticas en base binaria. Pues los ordenadores sólo pueden trabajar con binarios. Pero aún siendo obvio es de una importancia capital para entender el comportamiento de los números en JavaScript.

En los siguientes temas veremos como la ejecución de las operaciones aritméticas básicas pueden producir un resultado que no esperaríamos si hiciéramos esas operaciones en base decimal con una calculadora.

Y hablando de calculadoras, también necesitaremos una calculadora binaria básica. Su propósito es poder usarla en los temas siguientes.

Ejemplo: Calculadora binaria

Operación
Resultado:
Convertido a decimal:
Calculado en decimal:
Este ejemplo usa ES6 en modo estricto. Puedes consultar el código JS original de este ejemplo.

En los siguientes apartados se expone de forma esquemática como llevar a cabo las operaciones que se implementan en el ejemplo. Puede ver el código en el enlace al pie del mismo.

Sumar números binarios

Para sumar binarios partimos de una tabla base que realiza la suma de tres bits p + q + a, siendo los dos primeros los operandos de la suma y el tercero el arrastre de la suma anterior. Disponemos en el código de la siguiente tabla de resultados en forma de objeto:

let pqa = {
//   pqa     r    a
    "000": ["0", "0"],
    "001": ["1", "0"],
    "010": ["1", "0"],
    "011": ["0", "1"],
    "100": ["1", "0"],
    "101": ["0", "1"],
    "110": ["0", "1"],
    "111": ["1", "1"]
};
    

Luego iremos sumando los bits de los dos operandos usando la tabla anterior. Vamos a hacer un ejemplo sumando 11.1 + 1 = 100.1. Lo primero que tenemos que hacer es alinear por el separador de fracción los dígitos de tal forma que ambos operandos tengan el mismo número de caracteres. Para ello completaremos con ceros a la derecha e izquierda donde sea necesario:

11.1 01.0

Ahora iteramos por ambos números de derecha a izquierda sumando bit a bit, tal y como hacemos usualmente una suma a mano. El arrastre inicial es cero. Obtenemos la cadena "100" que buscamos en la tabla anterior para obtener el resultado (r) y el nuevo arrastre (a).

11.1 ⇒ p = "1" 01.0 ⇒ q = "0" a = "0" pqa["100"] ⇒ r = "1" a = "0"

Pasamos al siguiente bit. Obtenemos el resultado agregando el punto separador de fracción, pues es en este momento cuando lo hemos superado.

11.1 ⇒ p = "1" 01.0 ⇒ q = "1" a = "0" pqa["110"] ⇒ r = "0." a = "1"

Vamos al último bit. Observe como sumamos también el arrastre uno anterior:

11.1 ⇒ p = "1" 01.0 ⇒ q = "0" a = "1" pqa["101"] ⇒ r = "0" a = "1"

Tomamos los resultados r parciales desde el último al primero para obtener el orden izquierda a derecha del binario final. Si hay arrastre al final lo anteponemos al inicio. El resultado final es 100.1

Restar números binarios

Podemos restar binarios usando el complemento a uno del segundo y sumándolo al primero. Para entenderlo primero aplicaremos esta técnica con números en base decimal. Supongamos que vamos a restar 718 - 46.5 = 671.5. En primer lugar alineamos ambos números:

718.0 046.5

Obtenemos el complementario a nueve del segundo operando, poniendo en cada dígito la diferencia a 9, resultando 953.4. Ahora sumamos ambos operandos y un acarreo de un uno en el dígito más a la derecha:

718.0 953.4 000.1

Si sumamos esos tres números resultaran 1671.5. Despreciando el dígito 1 de la izquierda tendremos el resultado de la resta 671.5, para lo cual hemos usado una suma. Veáse que la resta parece que no existe, pero está implícita cuando complementamos a nueve cada dígito. En el fondo estamos haciendo estas operaciones:

718 - 46.5 = 718 + (1000 - 46.5) - 1000 = 718 + 953.5 - 1000 = 718 + 953.4 + 0.1 - 1000 = 671.5

Observe como complementar a nueve cada dígito de 046.5 resultan los 953.4 señalados en rojo en el cálculo anterior, tras haber separado una unidad en el dígito menos significativo 0.1.

Esa técnica es adecuada para aplicarla a la resta binaria, pues sólo necesitamos definir una función para obtener el complemento a uno de un binario y usar la misma función suma que ya hemos definido. Y es que obtener el complementario de un número binario es muy sencillo: cambiar los ceros por unos y los unos por ceros.

Vamos a hacer un ejemplo con la resta binaria 10 - 0.1 = 1.1. Primero alineamos ambos números:

10.0 00.1

Ahora obtenemos el complementario de 00.1 resultando 11.0. Sumamos teniendo en cuenta un arrastre inicial de uno:

10.0 ⇒ p = "0" 11.0 ⇒ q = "0" a = "1" pqa["001"] ⇒ r = "1" a = "0"

Segundo bit:

10.0 ⇒ p = "0" 11.0 ⇒ q = "1" a = "0" pqa["010"] ⇒ r = "1." a = "0"

Tercer bit:

10.0 ⇒ p = "1" 11.0 ⇒ q = "1" a = "0" pqa["110"] ⇒ r = "0" a = "1"

Si en la suma poníamos en primer lugar el acarreo uno final, en este caso con resta por complementario lo despreciamos. Así que obtenemos el resultado 01.1, que tras quitar el cero inicial no significativo queda 1.1.

Multiplicar números binarios

Vamos a multiplicar binarios como por ejemplo 11 × 10.1 = 111.1. Primero ponemos ambos números con punto de fracción 11.0 × 10.1. Los números no tienen que tener el mismo tamaño en caracteres. El hecho de incluir puntos en ambos es sólo por una comodidad a la hora de implementar el código. Pues para simplificarlo multiplicaremos ambos números como enteros, para lo cual tendremos que quitar ambos puntos de fracción. Veáse que 11.0 × 10.1 = 110/10 × 101/10 = 110×101 × 1/100. Así haremos la multiplicación 110×101 y al final situaremos el punto de fracción dos lugares desde la derecha, pues es el equivalente a multiplicar por 1/100.

Desde el punto de vista de la implementación usaremos el código siguiente para guardar el número de posiciones donde habremos de poner el punto de fracción tras el resultado de multiplicar los dos enteros. Siendo x, y los operandos (ambos con punto de fracción) obtenemos:

let k = x.length-1-x.indexOf(".") + y.length-1-y.indexOf(".")

Tras guardar esa posición eliminamos los puntos de fracción de los dos operandos y procedemos a realizar la multiplicación, que la haremos con la técnica tradicional para multiplicar dos números en base decimal:

110
×101
110
000
110
11110

Lo mejor de multiplicar en binario ese que sólo hay dos dígitos. Multiplicar por cero o por uno no podría ser más simple. Realmente en el código lo que hacemos es ir realizando una suma acumulada del primer operando 110, desplazando en cada paso un lugar si el bit del segundo operando es un uno. O más lugares si el bit o los bits anteriores fueran cero. Esquemáticamente el bucle del código itera por el segundo operando de esta forma:

z = 0
Bucle i:
101 ⇒ i=0 bit=1 ⇒ z=0+110 =110
101 ⇒ i=1 bit=0 ⇒ No hacemos nada
101 ⇒ i=2 bit=1 ⇒ z=110+11000=11110

En cada iteración agregamos tantos ceros a la derecha (resalte amarillo) del primer operando 110 como el número de iteración i. Vamos acumulando las sumas hasta obtener el entero 11110. El resultado final ha de incluir el punto de fracción, que recordamos que era dos lugares desde la derecha. Así que obtenemos 111.10 como el resultado de la multiplicación de 11×10.1.

Dividir números binarios

Para la división de binarios también usamos la división de decimales que siempre hemos conocido. Vamos a dividir 1100 ÷ 10.1 = 100.110011001100···. Como hicimos con la multiplicación, guardaremos la posición de los puntos de fracción primero para convertir a entero ambos números 1100 y 101, pues vamos a dividir enteros y al final repondremos el punto de fracción.

El algoritmo de división clásico usa la operación resta que ya habíamos definido previamente. En el siguiente ejemplo podrá ver paso a paso la división 1100 ÷ 101 hasta llegar a siete dígitos fraccionarios:

Ejemplo: División binaria

Registro 0 de 0
1100 101
0

1 < 101 ⇒ q = 0: Mientras los dígitos que vayamos tomando en el dividendo compongan un número menor que el divisor vamos poniendo ceros en el cociente.

1100 101
00

11 < 101 ⇒ q = 00: Aún no cabe, ponemos otro cero.

1100 101
-101 001
1

110 ≥ 101 ⇒ q = 001 ⇒ Restamos: Ahora los dígitos 110 tomados en el dividendo si es mayor que el divisor 101. Multiplicaríamos 101 × 1, pero en binario es suficiente con restar el divisor 101 de los dígitos 110 tomados en el dividendo.

1100 101
-101 0010
10

10 < 101 ⇒ q = 0010: Como el resto 1 anterior es menor que el divisor, tomamos el último dígito del dividendo.

1100 101
-101 0010.0
100

100 < 101 ⇒ q = 0010.0: Necesitamos otro dígito más. Como ya no hay más disponibles en el dividendo agregamos otro cero al resto y ponemos un punto de fracción seguido de un cero en el cociente.

1100 101
-101 0010.01
1000
-101
11

1000 ≥ 101 ⇒ q = 0010.01 ⇒ Restamos: Necesitamos otro dígito más. Ponemos otro cero y ahora 1000 > 101 por lo que ponemos un uno en el cociente y procedemos a restar el divisor. El nuevo resto es 11.

1100 101
-101 0010.011
1000
-101
110
-101
1

110 ≥ 101 ⇒ q = 0010.011 ⇒ Restamos: Mientras el resto no sea cero podemos seguir dividiendo. Agregamos otro cero y vemos que 110 ≥ 101, por lo que ponemos un uno en el cociente y restamos.

1100 101
-101 0010.0110
1000
-101
110
-101
10

10 < 101 ⇒ q = 0010.0110: No cabe, agregamos un cero.

1100 101
-101 0010.01100
1000
-101
110
-101
100

100 < 101 ⇒ q = 0010.01100: No cabe, agregamos otro cero.

1100 101
-101 0010.011001
1000
-101
110
-101
1000
-101
11

1000 ≥ 101 ⇒ q = 0010.011001 ⇒ Restamos: Ahora si cabe, restamos.

1100 101
-101 0010.0110011
1000
-101
110
-101
1000
-101
110
-101
1

110 ≥ 101 ⇒ q = 0010.0110011 ⇒ Restamos: Se observa que volvemos a repetir la secuencia anterior, obteniendo una secuencia de restos 1, 11, 1, 11, ... que da lugar al cociente 10.0110011001100··· repitiéndose la secuencia 1100 en la fracción de forma infinita, pues el resto nunca será cero.

El resultado de enteros 1100 ÷ 101 resulta 0010.0110011. Procedemos a eliminar los ceros no significativos a la izquierda quedando 10.0110011. A continuacuón tendremos que reponer el punto de fracción inicial. Como la división inicial era 1100 ÷ 10.1, hay que desplazar el punto de fracción un lugar a la derecha. O visto de esta forma:

1100 ÷ 10.1 = 1100 ÷ (101/10) = (1100 ÷ 101) × 10 = 10.0110011 × 10 = 100.110011

En la calculadora binaria podemos especificar un número determinado de dígitos fraccionarios. En el resultado se indicará con puntos suspensivos si el resultado contiene más dígitos fraccionarios. Para ello disponemos que el número máximo de dígitos fraccionarios que permitimos es 100.

Pero al realizar la división la hacemos con 1000 dígitos fraccionarios. Luego guardamos ese resultado (w) y lo recortamos al número de dígitos especificado (z). Ajustamos ceros de ambos números, es decir, eliminamos ceros no significativos a la derecha de la parte fraccionaria. Si tras eso resulta que w === z es que el resultado z es exacto. En otro caso el resultado z contendrá más dígitos fraccionarios y lo devolvemos como z + "···".

Comparando números binarios

Algo que necesitamos en las operaciones es la comparación de binarios. En la implementación JavaScript de la calculadora los binarios se tratan como string. Pero podemos usar los operadores de comparación de JavaScript aplicado a string siempre que ambos términos estén alineados por el punto de fracción y contengan la misma cantidad de dígitos numéricos. El código es el siguiente:

function comparar(x, compara, y){
    [x, y] = alinear(x, y);
    return (compara==="==") ? x===y :
           (compara==="!=") ? x!==y :
           (compara===">=") ? x>=y :
           (compara==="<=") ? x<=y :
           (compara===">") ? x>y :
           (compara==="<") ? x<y :
           false;
}
    

La función alinear(x, y) realiza esa alineación de términos. Por ejemplo si queremos hacer la comparacion 1001 > 11.01 primero alineamos ambos números por el punto de fracción rellenando con ceros donde sea necesario:

1001.00 0011.01

Si ahora comparamos estos string resultará que "1001.00" > "0011.01" será cierto:

//sin alinear
console.log("1001" > "11.01"); // false
//alineados
console.log("1001.00" > "0011.01"); // true
    

Al alinear por el punto de fracción y rellenar con ceros nos aseguramos la comparación de los bits en su posición en ambos números, pues siempre el caracter "0" se comparará como inferior al caracter "1".

Vea que no podemos usar la comparación como números, pues aunque esto es posible con enteros:

console.log(Number("0b1001") > Number("0b11")); // true
console.log(Number("0b11") > Number("0b1001")); // false
    

Sin embargo JavaScript no maneja números binarios fraccionarios. Observe que al tratar de convertir a binario un número con punto de fracción obtendremos NaN, tras lo cual la comparación no es necesariamente la esperada. Resultará siempre falso a excepción de cuando comparamos si son distintos:

console.log(Number("0b11.01")); // NaN
console.log(Number("0b1001") !== Number("0b11.01")); // true
console.log(Number("0b1001") === Number("0b11.01")); // false
console.log(Number("0b1001") > Number("0b11.01")); // false
console.log(Number("0b1001") < Number("0b11.01")); // false
console.log(Number("0b1001") >= Number("0b11.01")); // false
console.log(Number("0b1001") <= Number("0b11.01")); // false