J'ai besoin de calculer une expression qui ressemble à:,
A*B - C*D
où sont leurs types: signed long long int A, B, C, D;
Chaque nombre peut être vraiment grand (ne pas déborder de son type). Bien que cela A*B
puisse provoquer un débordement, l'expression A*B - C*D
peut en même temps être très petite. Comment puis-je le calculer correctement?
Par exemple:, MAX * MAX - (MAX - 1) * (MAX + 1) == 1
où MAX = LLONG_MAX - n
et n - un nombre naturel.
c++
c
integer-overflow
NGix
la source
la source
A - C
pourrait déborder. Est-ce un problème à considérer ou savez-vous que cela ne se produira pas avec vos données?Réponses:
Cela semble trop trivial, je suppose. Mais
A*B
c'est celui qui pourrait déborder.Vous pouvez faire ce qui suit, sans perdre en précision
Cette décomposition peut être faite plus loin .
Comme @Gian l'a souligné, des précautions peuvent être nécessaires lors de l'opération de soustraction si le type est unsigned long long.
Par exemple, avec le cas que vous avez dans la question, cela ne prend qu'une itération,
la source
C*D
A,B,C,D
sont négatifs? Ne sera pasE
ouF
sera encore plus grand alors?La solution la plus simple et la plus générale est d'utiliser une représentation qui ne peut pas déborder, soit en utilisant une bibliothèque d'entiers longs (par exemple http://gmplib.org/ ) soit en représentant en utilisant une structure ou un tableau et en implémentant une sorte de multiplication longue ( c'est-à-dire séparer chaque nombre en deux moitiés de 32 bits et effectuer la multiplication comme ci-dessous:
En supposant que le résultat final tient dans 64 bits, vous n'avez pas vraiment besoin de la plupart des bits de R3 et d'aucun de R4
la source
Notez que ce n'est pas standard car il repose sur un dépassement de capacité signé. (GCC a des indicateurs de compilateur qui permettent cela.)
Mais si vous ne faites que tous les calculs dans
long long
, le résultat de l'application directe de la formule:(A * B - C * D)
sera précis tant que le résultat correct tient dans unlong long
.Voici une solution de contournement qui ne repose que sur le comportement défini par l'implémentation consistant à convertir un entier non signé en entier signé. Mais on peut s'attendre à ce que cela fonctionne sur presque tous les systèmes aujourd'hui.
Cela convertit les entrées
unsigned long long
là où le comportement de débordement est garanti pour être enveloppé par la norme. La conversion à un entier signé à la fin est la partie définie par l'implémentation, mais fonctionnera sur presque tous les environnements aujourd'hui.Si vous avez besoin d'une solution plus pédante, je pense que vous devez utiliser "l'arithmétique longue"
la source
long long
.Cela devrait fonctionner (je pense):
Voici ma dérivation:
la source
Vous pouvez envisager de calculer le plus grand facteur commun pour toutes vos valeurs, puis de les diviser par ce facteur avant d'effectuer vos opérations arithmétiques, puis de multiplier à nouveau. Cela suppose que le facteur un tel existe, cependant (par exemple, si
A
,B
,C
etD
arriver à être premiers entre eux , ils ne seront pas un facteur commun).De même, vous pourriez envisager de travailler sur des échelles logarithmiques, mais cela va être un peu effrayant, sous réserve de précision numérique.
la source
long double
est disponible. Dans ce cas, un niveau de précision acceptable peut être atteint (et le résultat peut être arrondi).Si le résultat tient dans un long long int alors l'expression A * BC * D est correcte car elle exécute le mod arithmétique 2 ^ 64, et donnera le résultat correct. Le problème est de savoir si le résultat tient dans un long long int. Pour détecter cela, vous pouvez utiliser l'astuce suivante en utilisant des doubles:
Le problème avec cette approche est que vous êtes limité par la précision de la mantisse des doubles (54bits?) Donc vous devez limiter les produits A * B et C * D à 63 + 54 bits (ou probablement un peu moins).
la source
puis
la source
Vous pouvez écrire chaque nombre dans un tableau, chaque élément étant un chiffre et effectuer les calculs sous forme de polynômes . Prenez le polynôme résultant, qui est un tableau, et calculez le résultat en multipliant chaque élément du tableau par 10 à la puissance de la position dans le tableau (la première position étant la plus grande et la dernière étant zéro).
Le nombre
123
peut être exprimé comme suit:pour lequel vous venez de créer un tableau
[1 2 3]
.Vous faites cela pour tous les nombres A, B, C et D, puis vous les multipliez sous forme de polynômes. Une fois que vous avez le polynôme résultant, vous en reconstruisez simplement le nombre.
la source
Même si un
signed long long int
ne tiendra pasA*B
, deux d'entre eux le feront. AinsiA*B
pourrait être décomposé en termes d'arbre d'exposant différent, chacun d'entre eux convenant à unsigned long long int
.Pareil pour
C*D
.En suivant la voie directe, la sous-action pourrait être effectuée sur chaque paire de
AB_i
et deCD_i
même, en utilisant un bit de report supplémentaire (exactement un entier de 1 bit) pour chacun. Donc, si nous disons E = A * BC * D, vous obtenez quelque chose comme:On continue en transférant la moitié supérieure de
E_10
àE_20
(décaler de 32 et ajouter, puis effacer la moitié supérieure deE_10
).Vous pouvez maintenant vous débarrasser du bit de retenue
E_11
en l'ajoutant avec le bon signe (obtenu à partir de la partie non-retenue) àE_20
. Si cela déclenche un débordement, le résultat ne conviendra pas non plus.E_10
a maintenant assez d'espace pour prendre la moitié supérieure deE_00
(décalage, ajouter, effacer) et le bit de reportE_01
.E_10
peut être plus grand maintenant, nous répétons donc le transfert versE_20
.À ce stade,
E_20
doit devenir zéro, sinon le résultat ne correspondra pas. La moitié supérieure deE_10
est également vide suite au transfert.La dernière étape consiste à transférer la moitié inférieure de
E_20
àE_10
nouveau.Si l'attente qui
E=A*B+C*D
conviendrait auxsigned long long int
prises, nous avons maintenantla source
Si vous savez que le résultat final est représentable dans votre type entier, vous pouvez effectuer ce calcul rapidement en utilisant le code ci-dessous. Étant donné que la norme C spécifie que l'arithmétique non signée est une arithmétique modulo et ne déborde pas, vous pouvez utiliser un type non signé pour effectuer le calcul.
Le code suivant suppose qu'il existe un type non signé de même largeur et que le type signé utilise tous les modèles de bits pour représenter les valeurs (pas de représentation d'interruption, le minimum du type signé est le négatif de la moitié du module du type non signé). Si cela ne tient pas dans une implémentation C, de simples ajustements peuvent être apportés à la routine ConvertToSigned pour cela.
Les utilisations suivantes
signed char
etunsigned char
pour illustrer le code. Pour votre implémentation, modifiez la définition deSigned
totypedef signed long long int Signed;
et la définition deUnsigned
totypedef unsigned long long int Unsigned;
.la source
Vous pouvez essayer de diviser l'équation en composants plus petits qui ne débordent pas.
Si les composants débordent encore, vous pouvez les diviser en composants plus petits de manière récursive, puis les recombiner.
la source
K
etJ
, pourquoi pasN
etM
. De plus, je pense que vous enfreignez l'équation en plus gros morceaux. Puisque votre étape 3 est la même que la question du PO, sauf plus compliquée(AK-CJ)
->(AB-CD)
Je n'ai peut-être pas couvert tous les cas marginaux, ni testé rigoureusement cela, mais cela implémente une technique que je me souviens avoir utilisée dans les années 80 en essayant de faire des calculs entiers 32 bits sur un processeur 16 bits. Essentiellement, vous divisez les 32 bits en deux unités 16 bits et travaillez avec eux séparément.
Impressions:
qui me semble que ça marche.
Je parie que j'ai manqué certaines subtilités telles que la surveillance du débordement des signes, etc., mais je pense que l'essence est là.
la source
Par souci d'exhaustivité, puisque personne ne l'a mentionné, certains compilateurs (par exemple GCC) vous fournissent actuellement un entier de 128 bits.
Ainsi, une solution simple pourrait être:
la source
AB-CD = (AB-CD) * AC / AC = (B/C-D/A)*A*C
. NiB/C
niD/A
peut déborder, alors calculez d'(B/C-D/A)
abord. Étant donné que le résultat final ne débordera pas selon votre définition, vous pouvez effectuer en toute sécurité les multiplications restantes et calculer(B/C-D/A)*A*C
quel est le résultat requis.Notez que si votre entrée peut également être extrêmement petite , le
B/C
ouD/A
peut déborder. Si c'est possible, des manipulations plus complexes peuvent être nécessaires en fonction de l'inspection d'entrée.la source
Choisissez
K = a big number
(par exempleK = A - sqrt(A)
)Pourquoi?
Notez que parce que A, B, C et D sont de grands nombres, donc
A-C
etB-D
sont de petits nombres.la source
A-C+B-D
n'est pas un petit nombre. Parce que A, B, C et D sont de grands nombres, AC est donc un petit nombre.A - sqrt(A)
:)