web-dev-qa-db-fra.com

Représentant des nombres de 128 bits en C ++

Quelle est la meilleure façon de représenter un nombre de 128 bits en C++? Il doit se comporter le plus près possible des types numériques intégrés (c'est-à-dire prendre en charge tous les opérateurs arithmétiques, etc.).

Je pensais à construire une classe qui aurait 2 nombres 64 bits ou 4 nombres 32 bits. Ou peut-être simplement créer un bloc de mémoire de 128 bits et faire tout moi-même.

Existe-t-il un moyen plus simple/plus standard, ou quelque chose que je suis moins susceptible de bousiller lors de sa mise en œuvre moi-même? :)

Ce serait aussi bien s'il pouvait être étendu à 256 bits, 512 bits, etc ...

55
David Coufal

Regardez dans d'autres bibliothèques qui ont été développées. Beaucoup de gens ont voulu faire ça avant vous. :RÉ

Essayez bigint C++

13
Gordon Gustafson

EDIT: la première fois que j'ai écrit ceci boost::multiprecision::uint128_t n'était pas encore une chose. Garder cette réponse pour des raisons historiques.


J'ai déjà créé une classe uint128, vous pouvez la consulter sur: http://www.codef00.com/code/uint128.h .

Il dépend de boost pour fournir automatiquement toutes les variantes des opérateurs mathématiques, il devrait donc prendre en charge tout un _ unsigned int le type fait.

Il existe quelques extensions mineures aux types intégrés, telles que l'initialisation avec une chaîne comme celle-ci:

uint128_t x("12345678901234567890");

Il existe une macro pratique qui fonctionne de manière similaire à celles de C99 que vous pouvez utiliser comme ceci:

uint128_t x = U128_C(12345678901234567890);
65
Evan Teran

C'est un peu un cas particulier, d'autant plus que vous n'avez pas spécifié la ou les plates-formes que vous recherchez, mais avec GCC vous pouvez utiliser ce qu'on appelle le mode (TI) pour obtenir (synthétisé) des opérations 128 bits, pour exemple:

   typedef unsigned int uint128_t __attribute__((mode(TI)));

   uint64_t x = 0xABCDEF01234568;
   uint64_t y = ~x;

   uint128_t result = ((uint128_t) x * y);

   printf("%016llX * %016llX -> ", x, y);

   uint64_t r1 = (result >> 64);
   uint64_t r2 = result;

   printf("%016llX %016llX\n", r1, r2);

Cela ne fonctionne cependant que sur les processeurs 64 bits.

D'une manière ou d'une autre, vous cherchez une arithmétique à précision multiple pour résoudre ce problème. mode (TI) fera que le compilateur générera les opérations pour vous, sinon elles doivent être écrites explicitement.

Vous pouvez utiliser un package général bigint; ceux en C++ que je connais incluent les packages de théorie des nombres LiDIA et NTL , et les packages bigint utilisés pour le code cryptographique dans - Crypto ++ et Botan ). De plus, bien sûr, il y a GnuMP , qui est la bibliothèque canonique C MPI (et elle a également un wrapper C++, bien qu'elle semblait mal documentée la dernière fois que j'ai regardé). Tous ces éléments sont conçus pour être rapides, mais sont également probablement réglés pour des nombres plus importants (plus de 1000 bits), donc à 128 bits, vous pouvez avoir à faire face à beaucoup de frais généraux. dire si cela importe ou non.) Et tous (contrairement au package bigint-cpp, qui est GPL, sont soit BSD ou LGPL) - je ne sais pas si cela importe - mais cela pourrait avoir beaucoup d'importance.

Vous pouvez également écrire un type de type uint128_t personnalisé; généralement, une telle classe implémenterait à peu près les mêmes algorithmes qu'une classe MPI classique, juste codée en dur pour n'avoir que 2 ou 4 éléments. Si vous êtes curieux de savoir comment implémenter de tels algorithmes, une bonne référence est - Chapitre 14 du Handbook of Applied Cryptography

Bien sûr, le faire à la main est plus facile si vous n'avez pas réellement besoin de toutes les opérations arithmétiques (la division et le modulo, en particulier, sont plutôt délicats). Par exemple, si vous avez juste besoin de garder une trace d'un compteur qui pourrait hypothétiquement dépasser 64 bits, vous pouvez simplement le représenter comme une paire de longs 64 bits et effectuer le transport à la main:

unsigned long long ctrs[2] = { 0 };

void increment() {
   ++ctrs[0];
   if(!ctrs[0]) // overflow
     ++ctrs[1];
}

Ce qui, bien sûr, sera beaucoup plus simple à gérer qu'un package général MPI ou une classe uint128_t personnalisée.

20
Jack Lloyd

Boost a des types de données dans la bibliothèque multiprecision pour les types allant de 128 à 1024 bits.

#include <boost/multiprecision/cpp_int.hpp>

using namespace boost::multiprecision;

int128_t mySignedInt128 = -1;
uint128_t myUnsignedInt128 = 2;
int256_t mySignedInt256 = -3;
uint256_t myUnsignedInt256 = 4;
int512_t mySignedInt512 = -5;
uint512_t myUnsignedInt512 = 6;
int1024_t mySignedInt1024 = -7;
uint1024_t myUnsignedInt1024 = 8;
9
Tuxdude

GCC prend en charge un type entier 128 bits pour les processeurs qui le prennent en charge. Vous pouvez y accéder en utilisant:

__int128          a;
unsigned __int128 b;
4
Richard

Ne réinventez pas la roue - je suis certain que d'autres personnes ont déjà résolu ce problème, bien que je ne puisse citer aucune solution du haut de ma tête. GMP peut sûrement résoudre votre problème, bien que ce soit exagéré pour les entiers de taille fixe, et c'est aussi un peu lourd à utiliser (c'est une bibliothèque C, pas C++) .

2
Adam Rosenfield

Vous voudrez peut-être essayer GMP

2
Burkhard

Voici une bibliothèque que j'ai trouvée sur google.

http://sourceforge.net/projects/cpp-bigint/

0
Daniel A. White

Vous pourriez être mieux avec une classe entière de précision infinie, plutôt qu'une séquence de taille croissante. Certaines langues (comme Common LISP et IIRC Python) les ont nativement. Je ne sais pas trop ce qui est disponible pour C++; la dernière fois que j'ai regardé, il n'y avait pas de version Boost.

0
David Thornley

La bibliothèque graphique cairo contient deux fichiers qui implémentent une arithmétique portable de 128 bits: cairo-wideint-private.h, cairo-wideint.c. Nous incluons seulement ces deux dans notre projet pour obtenir 128 bits.

0
pdbj