Je veux utiliser dc
pour gérer certains nombres de base 16 avec des points hexadécimaux, mais je rencontre des problèmes de précision. Par exemple, ci-dessous, je multiplie F423F.FD
par 100
, les deux hex. La réponse attendue est F423FFD
, au lieu de cela, elle donne F423FFA.E1
, proche mais pas assez précise même après l'arrondissement.
$ dc
16 d i o F423F.FD 100 * p
F423FFA.E1
J'ai lu que dc
c'était une calculatrice de précision illimitée, et ce n'est en aucun cas un grand nombre. Y a-t-il quelque chose que je fais mal?
Merci pour vos réponses. Étant donné les problèmes avec dc
, j'ai mordu la balle et j'ai écrit mon propre analyseur pour des nombres réels dans d'autres bases. Si quelqu'un s'intéresse au code, je peux le poster ici.
dc
à utiliser puis pour écrire directement un analyseur! (L'entrée peut avoir ou non une décimale, et peut être dans d'autres bases, donc la quantité de remplissage varie.)dc
répondu: Pour gérer correctement les chiffres fractionnaires non décimaux, il faudrait un modèle complètement différent du modèle à échelle décimale utilisé par dc et bc (comme dicté par POSIX pour bc et par la tradition historique pour les deux). , donc techniquement, il pourrait être corrigédc
, mais cela casserait probablementbc
, donc classé comme WONTFIX.Exprimé en décimal (en utilisant
dc
pour convertir), cela correspond à 999999,98 (arrondi vers le bas) × 256, soit 255999994.88, qui est F423FFA.E1 en hexadécimal.La différence vient donc du
dc
comportement d'arrondi de: au lieu de calculer 256 × (999999 + 253 ÷ 256), ce qui donnerait 255999997, il arrondit 253 ÷ 256 vers le bas et multiplie le résultat.dc
est une calculatrice de précision arbitraire , ce qui signifie qu'il peut calculer avec la précision que vous voulez, mais vous devez lui dire ce que c'est. Par défaut, sa précision est 0, ce qui signifie que la division produit uniquement des valeurs entières et que la multiplication utilise le nombre de chiffres dans l'entrée. Pour définir la précision, utilisezk
(et gardez à l'esprit que la précision est toujours exprimée en chiffres décimaux, quel que soit le radix d'entrée ou de sortie):(Une précision de 8 chiffres serait suffisante car c'est ce dont vous avez besoin pour représenter 1 ÷ 256 en décimal.)
la source
k
est défini:10 k 16 d i o F423F.FD p
→F423F.FA
, donc je devrais augmenter tous les nombres avant de les utiliserdc
. Fondamentalement, cela revient à les pré-analyser de toute façon.dc
échelle son entrée en utilisant uniquement le nombre de chiffres, ce qui me semble être un bug (car le nombre de chiffres est calculé en utilisant le radix d'entrée, mais appliqué à la valeur décimale).bc
, quidc
est basé sur): "Les calculs internes doivent être effectués comme s'ils étaient en décimal, quelles que soient les bases d'entrée et de sortie, jusqu'au nombre spécifié de chiffres décimaux."20 k 16 d i o 0.3 1 / p
(qui imprime .1999999999999999999). Comprenez que l'opération ne fait que diviser0.2
par1
(ce qui en théorie ne devrait pas changer la valeur). Tandis que20 k 16 d i o 0.3000 1 / p
(correctement) s'imprime.30000000000000000
. (Suite)Le problème
Le problème est la manière dont dc (et bc) comprennent les constantes numériques.
Par exemple, la valeur (en hexadécimal)
0.3
(divisée par 1) est transformée en une valeur proche de0.2
En fait, la constante simple est
0.3
également modifiée:Il semble que ce soit d'une manière étrange, mais ce n'est pas le cas (plus tard).
L'ajout de zéros permet à la réponse d'approcher la valeur correcte:
La dernière valeur est exacte et restera exacte, quel que soit le nombre de zéros ajoutés.
Le problème est également présent dans bc:
Un chiffre par bit?
Le fait très intuitif pour les nombres à virgule flottante est que le nombre de chiffres requis (après le point) est égal au nombre de bits binaires (également après le point). Un nombre binaire 0,101 est exactement égal à 0,625 en décimal. Le nombre binaire 0,0001110001 est (exactement) égal à
0.1103515625
(dix chiffres décimaux)De plus, pour un nombre à virgule flottante comme 2 ^ (- 10), qui en binaire n'a qu'un seul bit (défini):
Formé du même nombre de chiffres binaires
.0000000001
(10) que de chiffres décimaux.0009765625
(10). Ce n'est peut-être pas le cas dans d'autres bases, mais la base 10 est la représentation interne des nombres en dc et en bc et est donc la seule base dont nous devons vraiment nous préoccuper.La preuve mathématique se trouve à la fin de cette réponse.
échelle bc
Le nombre de chiffres après le point peut être compté avec la
scale()
forme de fonction intégrée bc:Comme indiqué, 2 chiffres sont insuffisants pour représenter la constante
0.FD
.De plus, le simple fait de compter le nombre de caractères utilisés après le point est une manière très incorrecte de signaler (et d'utiliser) l'échelle du nombre. L'échelle d'un nombre (dans n'importe quelle base) doit calculer le nombre de bits nécessaires.
Chiffres binaires dans un flotteur hexadécimal.
Comme on le sait, chaque chiffre hexadécimal utilise 4 bits. Par conséquent, chaque chiffre hexadécimal après le point décimal nécessite 4 chiffres binaires, qui en raison du fait (impair?) Ci-dessus nécessitent également 4 chiffres décimaux.
Par conséquent, un nombre comme
0.FD
nécessitera 8 chiffres décimaux pour être représenté correctement:Ajouter des zéros
Le calcul est simple (pour les nombres hexadécimaux):
h
) après le point.h
par 4.h×4 - h = h × (4-1) = h × 3 = 3×h
zéros.En code shell (pour sh):
Qui s'imprimera (correctement en dc et en bc):
En interne, bc (ou dc) pourrait faire correspondre le nombre de chiffres requis au nombre calculé ci-dessus (
3*h
) pour convertir les flottants hexadécimaux en représentation décimale interne. Ou une autre fonction pour d'autres bases (en supposant que le nombre de chiffres est fini par rapport à la base 10 (interne de bc et dc) dans cette autre base). Comme 2 i (2,4,8,16, ...) et 5,10.posix
La spécification posix indique que (pour bc, sur lequel dc est basé):
Mais "... le nombre spécifié de chiffres décimaux." pourrait être compris comme "… le nombre nécessaire de chiffres décimaux pour représenter la constante numérique" (comme décrit ci-dessus) sans affecter les "calculs internes décimaux"
Car:
bc n'utilise pas vraiment 50 ("le nombre spécifié de chiffres décimaux") comme défini ci-dessus.
Seulement s'il est divisé, il est converti (toujours de manière incorrecte car il utilise une échelle de 2 pour lire la constante
0.FD
avant de l'étendre à 50 chiffres):Cependant, c'est exact:
Encore une fois, la lecture de chaînes numériques (constantes) doit utiliser le nombre correct de bits.
Preuve mathématique
En deux étapes:
Une fraction binaire peut s'écrire a / 2 n
Une fraction binaire est une somme finie de puissances négatives de deux.
Par exemple:
= 0 + 0 × 2 -1 + 0 × 2 -2 + 1 × 2 -3 + 1 × 2 -4 + 0 × 2 -5 + 1 × 2-6 + 0 × 2-7 + 1 × 2-8 + 1 × 2-9 + 0 × 2-10 + 1 × 2-11
= 2 -3 + 2 -4 + 2 -6 + 2 -8 + 2 -9 + 2 -11 = (avec les zéros supprimés)
Dans une fraction binaire de n bits, le dernier bit a une valeur de 2 -n , ou 1/2 n . Dans cet exemple: 2 -11 ou 1/2 11 .
= 1/2 3 + 1/2 4 + 1/2 6 + 1/2 8 + 1/2 9 + 1/2 11 = (avec inverse)
En général, le dénominateur pourrait devenir 2 n avec un exposant numérateur positif de deux. Tous les termes peuvent ensuite être combinés en une seule valeur a / 2 n . Pour cet exemple:
2 = 8 /2 11 + 2 7 /2 11 + 2 cinq / 2 11 + 2 3 /2 11 + 2 2 /2 11 + 1/2 11 = (exprimé avec deux 11 )
= (2 8 + 2 7 + 2 5 + 2 3 + 2 2 + 1) / 2 11 = (extraction du facteur commun)
= (256 + 128 + 32 + 8 + 4 + 1) / 2 11 = (converti en valeur)
= 429/2 11
Chaque fraction binaire peut être exprimée en b / 10 n
Multipliez a / 2 n par 5 n / 5 n , en obtenant (a × 5 n ) / (2 n × 5 n ) = (a × 5 n ) / 10 n = b / 10 n , où b = a × 5 n . Il a n chiffres.
Pour l'exemple, nous avons:
(429 · 5 11 ) / 10 11 = 20947265625/10 11 = 0,20947265625
Il a été démontré que chaque fraction binaire est une fraction décimale avec le même nombre de chiffres.
la source