Comprendre «ibase» et «obase» en cas de conversions avec bc?

22

J'utilise souvent un bcutilitaire pour convertir hexadécimal en décimal et vice versa. Cependant, il s'agit toujours d'essais et d'erreurs sur la manière ibaseet obasela configuration. Par exemple ici, je veux convertir la valeur hexadécimale C0 en décimal:

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

Quelle est la logique ici? obase( Adans mon troisième exemple) doit être dans la même base que la valeur qui est convertie ( C0dans mes exemples) et ibase( 16dans mon troisième exemple) doit être dans la base où je convertis?

Martin
la source
1
pour les calculs hexadécimaux (entrée et sortie en hexadécimal), je dois définir obase avant ibase!
Paschalis

Réponses:

36

Ce que vous voulez vraiment dire, c'est:

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

pour hex-décimal, et:

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

pour décimal-hex.

Vous n'avez pas besoin de donner à la fois ibaseet obasepour toute conversion impliquant des nombres décimaux, car ces paramètres par défaut sont 10.

Vous ne devez donner à la fois pour les conversions telles que binaire à six pans. Dans ce cas, je trouve plus facile de donner un sens aux choses si vous donnez d' obaseabord:

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

Si vous donnez d' ibaseabord à la place, cela modifie l'interprétation du obaseparamètre suivant , de sorte que la commande doit être:

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

En effet, dans cet ordre, la obasevaleur est interprétée comme un nombre binaire, vous devez donc donner 10000₂ = 16 pour obtenir une sortie en hexadécimal. C'est maladroit.


Voyons maintenant pourquoi vos trois exemples se comportent comme ils le font.

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

    180

    Cela définit la base d'entrée à 15 et la base de sortie à 10, car une valeur à un chiffre est interprétée en hexadécimal, selon POSIX . Cela demande bcde vous dire ce que C0₁₅ est dans la base A₁₅ = 10, et il répond correctement à 180₁₀, bien que ce ne soit certainement pas la question que vous vouliez poser.

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

    C0

    Il s'agit d'une conversion nulle en base 15.

    Pourquoi? Tout d'abord, parce que le Fchiffre unique est interprété en hexadécimal, comme je l'ai souligné dans l'exemple précédent. Mais maintenant que vous l'avez défini sur la base 15, le paramètre de base de sortie suivant est interprété de cette façon, et 10₁₅ = 15, vous avez donc une conversion nulle de C0₁₅ à C0₁₅.

    C'est vrai, la sortie n'est pas en hexadécimal comme vous le supposiez, elle est en base 15!

    Vous pouvez le prouver par vous-même en essayant de convertir F0au lieu de C0. Puisqu'il n'y a pas de Fchiffre dans la base 15, bcil le serre E0et donne E0comme sortie.

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

    192

    C'est le seul de vos trois exemples qui a probablement une utilité pratique.

    Il est en train de changer la base d'entrée à six pans premier , de sorte que vous ne avez plus besoin de creuser dans la spécification Posix de comprendre pourquoi Aest interprété comme hex, 10 dans ce cas. Le seul problème avec cela est qu'il est redondant de définir la base de sortie sur A₁₆ = 10, car c'est sa valeur par défaut.

Warren Young
la source
7

Le réglage ibasesignifie que vous devez définir obasecette même base. Expliquer vos exemples montrera ceci:

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

Vous définissez bcde considérer les numéros d'entrée comme représentés dans la base 15 avec le "ibase = F". "obase = A" définit les numéros de sortie à la base 10, qui est la valeur par défaut.

bc lit C0 comme un nombre de base 15: C = 12. 12 * 15 = 180.


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

Dans celui-ci, vous définissez l'entrée sur la base 15 et la sortie sur 10 - dans la base 15, donc la base de sortie est 15. L'entrée C0 dans la base 15 est la sortie C0 dans la base 15.


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

Réglez l'entrée sur la base 16, la sortie sur la base 10 (A dans la base 16 est 10 dans la base 10).

C0 converti en base 10 est: 12 * 16 = 192


Ma règle personnelle est de définir d'abord obase, afin que je puisse utiliser la base 10. Ensuite, définissez ibase, en utilisant également la base 10.

Notez que bccela a une exception ironique: ibase=Aet obase=Adéfinit toujours l'entrée et la sortie sur la base 10. Depuis la bcpage de manuel:

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

Ce comportement est inscrit dans la spécification de bc: à partir de la spécification 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.

C'est pourquoi le ibase=Fparamètre a changé votre base d'entrée en base 15, et pourquoi j'ai recommandé de toujours définir la base en utilisant la base 10. Évitez de vous embrouiller.

Bruce Ediger
la source
@ StéphaneChazelas - J'ai un souvenir de "ibase = A" travaillant sur une machine SysVr3 en 1989 environ. Je parie que cela remonte plus loin que la spécification Unix unique. Je n'ai pas pu rapidement rechercher une référence antérieure sur Google.
Bruce Ediger
Je pense que c'est parce qu'il y a plus de liens vers les anciennes spécifications car ils existent depuis plus longtemps. Le même genre de choses se produit pour la documentation apache / mysql / bugzilla ... où google vous donne d'abord le document pour les anciennes versions au lieu des dernières.
Stéphane Chazelas
5

Tous les nombres sont interprétés par GNU bc comme la base d'entrée actuelle qui est en vigueur pour l'instruction dans laquelle le nombre apparaît. Lorsque vous utilisez un chiffre en dehors de l'entrée actuelle, interprétez-les comme le chiffre le plus élevé disponible dans la base (9 en décimal) lorsque la partie d'un nombre à plusieurs chiffres, ou comme leurs valeurs normales lorsqu'il est utilisé comme un nombre à un seul chiffre ( A== 10 en décimal).

Dans le manuel GNU bc :

Les nombres à un chiffre ont toujours la valeur du chiffre quelle que soit la valeur d' ibase . (c.-à-d. A = 10.) Pour les nombres à plusieurs chiffres, bcchange tous les chiffres d'entrée supérieurs ou égaux à ibase à la valeur de ibase -1. Cela fait que le nombre est FFFtoujours le plus grand nombre à 3 chiffres de la base d'entrée.

Cependant, vous devez savoir que la norme POSIX ne définit ce comportement que pour les affectations à ibaseet obase, et dans aucun autre contexte.

De la spécification SUS sur bc :

Lorsque l' Ibase ou obase est attribué un seul chiffre la valeur de la liste dans les conventions lexicales en Colombie - Britannique, la valeur est assumée en hexadécimal. (Par exemple, ibase = A est défini sur base dix, quelle que soit la valeur ibase actuelle .) Sinon, le comportement n'est pas défini lorsque des chiffres supérieurs ou égaux à la valeur ibase apparaissent dans l'entrée. Les deux ibase et obase ont des valeurs initiales de 10.

Le facteur clé qui vous manque est que F n'est pas en fait seize, mais en fait quinze, donc lorsque vous définissez ibase = F, vous définissez la base d'entrée sur quinze.

Par conséquent, pour définir la portably ibase en hexadécimal d'un état inconnu, vous devez donc utiliser deux déclarations: ibase=A; ibase=16. Cependant, au début du programme, vous pouvez vous fier à ce qu'il soit décimal et simplement l'utiliser ibase=16.

Aléatoire832
la source
+1: Truc mignon avec ibase=A; ibase=16.
Warren Young
C'est SUSv3, pas SUSv6. SUSv4 est sur pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html
Stéphane Chazelas
J'avais toujours pensé que les 6 et 7 dans les en-têtes étaient la version. Je n'ai jamais rien vu de différent - quels sont les problèmes # 1-5?
Random832
@ Random832: SUS et POSIX ne sont pas la même chose .
Warren Young
@WarrenYoung SUS n'intègre-t-il pas POSIX? Ce paragraphe n'a pas de balise d'extension, et le document dit des choses comme "une partie de ce volume de POSIX.1-2008" tout au long.
Random832
0

Il est toujours recommandé de définir ibaseet d' obaseutiliser un nombre à un chiffre, au lieu d'un nombre tel que 16, car selon la bcpage de manuel,

Les nombres à un chiffre ont toujours la valeur du chiffre quelle que soit la valeur d'ibase.

Cela signifie que A,B,...,Fles valeurs ont toujours 10,11,...,15respectivement, quelle que soit la valeur de ibase. Vous pouvez également utiliser F+1pour spécifier le numéro 16. Par exemple, vous feriez mieux d'écrire

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

au lieu d'écrire echo "ibase=16; obase=A; C0" | bcpour spécifier que la base d'entrée est 16et la base de sortie l'est 10. Ou par exemple, si vous voulez les deux ibaseet obaseavoir 16 ans, vous feriez mieux d'utiliser

ibase=F+1; obase=F+1

au lieu d'utiliser ibase=16; obase=10. De même, si vous allez entrer vos nombres en base 14 et les sortir en base 16, utilisez

ibase=E; obase=F+1

Bien que les formes de bain aient les mêmes résultats, la première est moins sujette aux erreurs, tandis que la seconde peut conduire à plus de confusion et d'erreur.

La différence entre les deux formes devient particulièrement plus apparente lorsque vous êtes dans l'environnement d'exécution de bcou que vous allez écrire vos calculs dans un fichier, puis passer ce fichier bcen argument. Dans de telles situations, vous devrez peut-être modifier les valeurs de ibaseet obaseplusieurs fois, et l'utilisation de ce dernier formulaire peut entraîner de graves confusions et erreurs. (vis-Le)

Hedayat Mahdipour
la source