2010-04-30 4 views
2

J'écris une bibliothèque bignum, et je veux utiliser des types de données efficaces pour représenter les chiffres. Particulièrement entier pour le chiffre, et long (si strictement double de la taille de l'entier) pour les représentations intermédiaires lors de l'addition et de la multiplication.Avec stdint.h et ANSI printf?

Je vais utiliser certaines fonctionnalités de C99, mais en essayant de se conformer à la norme ANSI C.

Actuellement, je suis ce qui suit dans ma bibliothèque bignum:

#include <stdint.h> 

#if defined(__LP64__) || defined(__amd64) || defined(__x86_64) || defined(__amd64__) || defined(__amd64__) || defined(_LP64) 
typedef uint64_t u_w; 
typedef uint32_t u_hw; 
#define BIGNUM_DIGITS 2048 
#define U_HW_BITS 16 
#define U_W_BITS 32 
#define U_HW_MAX UINT32_MAX 
#define U_HW_MIN UINT32_MIN 
#define U_W_MAX UINT64_MAX 
#define U_W_MIN UINT64_MIN 
#else 
typedef uint32_t u_w; 
typedef uint16_t u_hw; 
#define BIGNUM_DIGITS 4096 
#define U_HW_BITS 16 
#define U_W_BITS 32 
#define U_HW_MAX UINT16_MAX 
#define U_HW_MIN UINT16_MIN 
#define U_W_MAX UINT32_MAX 
#define U_W_MIN UINT32_MIN 
#endif 

typedef struct bn 
{ 
     int sign; 
     int n_digits; // #digits should exclude carry (digits = limbs) 
     int carry; 
     u_hw tab[BIGNUM_DIGITS]; 
} bn; 

Comme je l'ai pas écrit une procédure écrire le bignum en décimal, je dois analyser le tableau intermédiaire et imprimer les valeurs de chaque chiffre. Cependant, je ne sais pas quel spécificateur de conversion utiliser avec printf. De préférence je voudrais écrire au terminal le chiffre codé en hexadécimal. Le problème sous-jacent est que je veux deux types de données, un qui est deux fois plus long que l'autre, et les utilise en outre avec printf en utilisant des spécificateurs de conversion standard. Il serait idéal si int est 32bits et long est 64bits mais je ne sais pas comment le garantir en utilisant un préprocesseur, et quand vient le temps d'utiliser des fonctions telles que printf qui reposent uniquement sur les types standard je ne sais plus quoi utilisation.

Répondre

3

Vous pouvez utiliser les macros de <inttypes.h> pour aider:

#if defined(__LP64__) || defined(__amd64) || defined(__x86_64) || defined(__amd64__) || defined(__amd64__) || defined(_LP64) 
typedef uint64_t u_w; 
typedef uint32_t u_hw; 
#define BIGNUM_DIGITS 2048 
#define U_HW_BITS 16 
#define U_W_BITS 32 
#define U_HW_MAX UINT32_MAX 
#define U_HW_MIN UINT32_MIN 
#define U_W_MAX UINT64_MAX 
#define U_W_MIN UINT64_MIN 
#define PRI_U_HW PRIu32 // use for formatting a `u_hw` type 
#define PRI_U_W PRIu64 // use for formatting a `u_w` type 
#else 
typedef uint32_t u_w; 
typedef uint16_t u_hw; 
#define BIGNUM_DIGITS 4096 
#define U_HW_BITS 16 
#define U_W_BITS 32 
#define U_HW_MAX UINT16_MAX 
#define U_HW_MIN UINT16_MIN 
#define U_W_MAX UINT32_MAX 
#define U_W_MIN UINT32_MIN 
#define PRI_U_HW PRIu16 // use for formatting a `u_hw` type 
#define PRI_U_W PRIu32 // use for formatting a `u_w` type 
#endif 

Puis:

printf("some u_w variable: %" PRI_U_W "\n", u_w_var);  
printf("some u_hw variable: %" PRI_U_HW "\n", u_hw_var); 

Ils ne sont pas assez, mais ils sont comment C99 fait.

+0

Vous voulez inclure les définitions entre guillemets? Quoi qu'il en soit, un conseil pratique. Je pensais que cela serait documenté dans la page de manuel printf, mais je me demandais comment C99 traiterait de ces types. Merci. – snap

+1

Dans la première partie de l'exemple où ils sont définis, 'PRI_U_HW' et' PRI_U_W' sont simplement des alias pour les valeurs C99 'PRIuXX', qui seront des littéraux de chaîne. Quand vous allez les utiliser (comme dans les exemples 2 'printf() dans le 2ème extrait de code) vous devez les utiliser en dehors des guillemets (ils fournissent leurs propres guillemets) et s'appuyer sur la concaténation de littéraux adjacents de C dans ' phase 6 'de la traduction. Comme je l'ai dit, c'est plutôt moche. –

+0

Je vois. Au fait, pensez-vous qu'il y a de toute façon à éviter ce gunk de processeur et s'en tenir à int/long? J'ai vraiment juste besoin d'avoir deux types, dont l'un est au moins le double de la largeur. Je suppose que char et short feraient l'affaire, mais je préfèrerais utiliser les types de données plus grands tels que int/long/long long si possible. – snap

1

ANSI C ne fournit aucune garantie sur les tailles de int et long, et je ne pense pas long long est un type ANSI. Si vous ne voulez pas ou ne pouvez pas utiliser C99, la seule solution sûre et portable est d'écrire un script configure qui créera des programmes C qui utilisent sizeof pour trouver une paire de types entiers qui ont la propriété dont vous avez besoin. Vous pouvez ensuite dans ce script générer des macros, y compris les macros au format printf.

Il est également possible que la raison pour laquelle vous n'utilisez pas C99 est que vous êtes en train de porter sur une plate-forme farfelue sans compilateur C99. Dans ce cas, vous pouvez juste comprendre ce qui fonctionne, le claquer dans un en-tête, et ne pas s'inquiéter de la portabilité.

C99 n'est pas joli mais il résout certains de ces problèmes C ennuyeux.

+0

Merci de démystifier le processus. Je suppose que c'est ce que font la plupart des programmes autoconf. Suggéreriez-vous à la main, ou avez-vous un outil pour faire le travail? Je ne connais qu'autoconf et il semble complexe de démarrer un petit projet avec. – snap

+0

@nn: Je cours en criant depuis les autotools GNU. Je suggérerais un script sh POSIX écrit à la main. –