¿Entiende "ibase" y "obase" en caso de conversiones con bc?

22

A menudo uso la bcutilidad para convertir hexadecimal a decimal y viceversa. Sin embargo, siempre es un poco de prueba y error cómo ibasey obasedebe configurarse. Por ejemplo, aquí quiero convertir el valor hexadecimal C0 a decimal:

$ echo "ibase=F;obase=A;C0" | bc
180
$ echo "ibase=F;obase=10;C0" | bc
C0
$ echo "ibase=16;obase=A;C0" | bc
192

¿Cuál es la lógica aquí? obase( Aen mi tercer ejemplo) debe estar en la misma base que el valor que se convierte ( C0en mis ejemplos) y ibase( 16en mi tercer ejemplo) debe estar en la base a la que me estoy convirtiendo.

Martín
fuente
1
para cálculos hexadecimales (entrada y salida en hexadecimal) ¡Tengo que establecer obase antes de ibase!
Paschalis

Respuestas:

36

Lo que realmente quieres decir es:

$ echo "ibase=16; C0" | bc
192

para hexadecimal a decimal y:

$ echo "obase=16; 192" | bc
C0

para decimal a hexadecimal.

No necesita dar ambas ibasey obasepara cualquier conversión que implique números decimales, ya que esta configuración predeterminada es 10.

Usted no necesita dar tanto para las conversiones, como binario-a-hexadecimal. En ese caso, me resulta más fácil entender las cosas si das obaseprimero:

$ echo "obase=16; ibase=2; 11000000" | bc
C0

Si da ibaseprimero, cambia la interpretación de la siguiente obaseconfiguración, por lo que el comando debe ser:

$ echo "ibase=2; obase=10000; 11000000" | bc
C0

Esto se debe a que en este orden, el obasevalor se interpreta como un número binario, por lo que debe dar 10000₂ = 16 para obtener la salida en hexadecimal. Eso es torpe.


Ahora veamos por qué sus tres ejemplos se comportan como lo hacen.

  1. echo "ibase=F;obase=A;C0" | bc

    180

    Eso establece la base de entrada en 15 y la base de salida en 10, ya que un valor de un solo dígito se interpreta en hexadecimal, de acuerdo con POSIX . Esto le pide bcque le diga qué C0₁₅ está en la base A₁₅ = 10, y está respondiendo correctamente 180₁₀, aunque ciertamente esta no es la pregunta que quería hacer.

  2. echo "ibase=F;obase=10;C0" | bc

    C0

    Esta es una conversión nula en la base 15.

    ¿Por qué? Primero, porque el único Fdígito se interpreta en hexadecimal, como señalé en el ejemplo anterior. Pero ahora que lo configuró en base 15, la siguiente configuración base de salida se interpreta de esa manera, y 10₁₅ = 15, por lo que tiene una conversión nula de C0₁₅ a C0₁₅.

    Así es, la salida no está en hexadecimal como suponías, ¡está en la base 15!

    Puedes probarte esto a ti mismo intentando convertir en F0lugar de C0. Como no hay un Fdígito en la base 15, bcsujételo E0y da E0como salida.

  3. echo "ibase=16; obase=A; C0"

    192

    Este es el único de sus tres ejemplos que probablemente tenga algún uso práctico.

    Está cambiando la base de entrada a hexadecimal primero , de modo que ya no hay necesidad de cavar en la especificación POSIX de entender por qué Ase interpreta como hexagonal, 10 en este caso. El único problema es que es redundante establecer la base de salida en A₁₆ = 10, ya que ese es su valor predeterminado.

Warren Young
fuente
7

Configuración ibasesignifica que necesita establecer obaseen esa misma base. Explicar sus ejemplos mostrará esto:

echo "ibase=F;obase=A;C0" | bc

Establece bcconsiderar los números de entrada como se representa en la base 15 con el "ibase = F". "obase = A" establece los números de salida en base 10, que es el valor predeterminado.

bc lee C0 como un número base 15: C = 12. 12 * 15 = 180.


echo "ibase=F;obase=10;C0" | bc

En este, establece la entrada en la base 15 y la salida en 10, en la base 15, por lo que la base de salida es 15. La entrada C0 en la base 15 es la salida C0 en la base 15.


echo "ibase=16;obase=A;C0" | bc

Establezca la entrada en la base 16, la salida en la base 10 (A en la base 16 es 10 en la base 10).

C0 convertido a base 10 es: 12 * 16 = 192


Mi regla personal es establecer obase primero, para que pueda usar la base 10. Luego establezca ibase, también usando la base 10.

Tenga en cuenta que bctiene una excepción irónica: ibase=Ay obase=Asiempre establece la entrada y la salida en la base 10. Desde la bcpágina del manual:

Single digit numbers always have the value of the digit 
regardless of the value of ibase.

Este comportamiento está consagrado en la especificación de bc: De la especificación OpenGroup 2004bc :

When either ibase or obase is assigned a single digit value from 
the list in 'Lexical Conventions in bc', the value shall be assumed
in hexadecimal. (For example, ibase=A sets to base ten, regardless 
of the current ibase value.) Otherwise, the behavior is undefined 
when digits greater than or equal to the value of ibase appear in
the input.

Es por eso que la ibase=Fconfiguración cambió su base de entrada a la base 15, y por eso recomendé establecer siempre la base usando la base 10. Evite confundirse.

Bruce Ediger
fuente
@ StéphaneChazelas - Tengo un recuerdo de "ibase = A" trabajando en una máquina SysVr3 en 1989 más o menos. Apuesto a que se remonta más atrás que Single Unix Spec. No pude buscar rápidamente en Google una referencia anterior.
Bruce Ediger
Creo que es porque hay más enlaces a las especificaciones más antiguas, ya que han existido por más tiempo. El mismo tipo de cosas suceden para apache / mysql / bugzilla ... documentación donde google le da el documento de las versiones anteriores primero en lugar de las últimas.
Stéphane Chazelas
5

GNU bc interpreta todos los números como la base de entrada actual que está vigente para la declaración en la que aparece el número. Cuando use un dígito fuera de la entrada actual, inténtelos como el dígito más alto disponible en la base (9 en decimal) cuando parte de un número de varios dígitos, o como sus valores normales cuando se usa como un número de un solo dígito ( A== 10 en decimal).

Del manual de GNU bc :

Los números de un solo dígito siempre tienen el valor del dígito, independientemente del valor de ibase . (es decir, A = 10.) Para números de varios dígitos, bccambia todos los dígitos de entrada mayores o iguales a ibase al valor de ibase -1. Esto hace que el número FFFsea ​​siempre el número más grande de 3 dígitos de la base de entrada.

Sin embargo, debe tener en cuenta que el estándar POSIX sólo define este comportamiento para las asignaciones a ibasey obase, y no en cualquier otro contexto.

De la especificación SUS en bc :

Cuando a ibase u obase se le asigna un valor de un solo dígito de la lista en Convenciones léxicas en bc, el valor se asumirá en hexadecimal. (Por ejemplo, ibase = A se establece en base diez, independientemente del valor actual de ibase ). De lo contrario, el comportamiento no está definido cuando aparecen dígitos mayores o iguales al valor de ibase en la entrada. Tanto ibase como obase tendrán valores iniciales de 10.

El factor clave que falta es que F no es de hecho dieciséis, pero en realidad es quince, por lo que cuando configura ibase = F está configurando la base de entrada en quince.

Por lo tanto, para establecer la forma portable ibase a hexadecimal de un estado desconocido, que por lo tanto es necesario utilizar dos declaraciones: ibase=A; ibase=16. Sin embargo, al comienzo del programa puede confiar en que sea decimal y simplemente usarlo ibase=16.

Aleatorio832
fuente
+1: lindo truco con ibase=A; ibase=16.
Warren Young
Eso es SUSv3, no SUSv6. SUSv4 está en pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html
Stéphane Chazelas
Siempre pensé que los 6 y 7 en los encabezados eran la versión. Nunca he visto nada diferente: ¿cuál es el problema # 1-5?
Random832
@ Random832: SUS y POSIX no son lo mismo .
Warren Young
@WarrenYoung ¿SUS no incorpora POSIX? Este párrafo no tiene etiqueta de extensión, y el documento dice cosas como "parte de este volumen de POSIX.1-2008" en todo momento.
Random832
0

Siempre se recomienda establecer ibasey obaseusar un número de un solo dígito, en lugar de un número como 16, ya que, según la bcpágina del manual,

Los números de un solo dígito siempre tienen el valor del dígito independientemente del valor de ibase.

Esto significa que A,B,...,Fsiempre tienen los valores 10,11,...,15respectivamente, independientemente de cuál sea el valor de ibase. También puede usar F+1para especificar el número 16. Por ejemplo, será mejor que escribas

echo "ibase=F+1; obase=A; C0" | bc

en lugar de escribir echo "ibase=16; obase=A; C0" | bcpara especificar que la base de entrada es 16y la base de salida es 10. O, por ejemplo, si quiere ambos ibasey obasetener 16 años, será mejor que use

ibase=F+1; obase=F+1

en lugar de utilizar ibase=16; obase=10. Del mismo modo, si va a ingresar sus números en la base 14 y generarlos en la base 16, use

ibase=E; obase=F+1

Aunque las formas de baño tienen los mismos resultados, la primera es menos propensa a errores, mientras que la segunda puede generar más confusión y error.

La diferencia entre las dos formas se hace especialmente más evidente cuando estás en el entorno de ejecución de bc, o vas a escribir tus cálculos en un archivo, y luego pasar ese archivo bccomo argumento. En tales situaciones, es posible que tenga que cambiar los valores ibasey obasevarias veces, y el uso de esta última forma puede generar graves confusiones y errores. (experimentalo)

Hedayat Mahdipour
fuente