Newsgroup: it.comp.lang.c
Titolo: Formattazione di un numero in modo generico

x e div sono due variabili intere che insieme rappresentano un valore
numerico reale.
Per esempio:
  x = 123, div = 0 --> valore reale 123
  x = 123, div = 1 --> valore reale 1230
  x = 123, div = -1 --> valore reale 12.3
e così via (in realtà in div potrei memorizzare anche il divisore vero
e proprio (1, 10, -10) anzichè l'esponente del 10 se più conveniente).
Vorrei costruirmi una funzione simile alla sprintf() che trasformi
questa coppia di variabili intere (x, div) in una stringa a seconda di
una stringa di formattazione.
  void sprintf(char *str, const char *fmt, int x, int div);
In pratica dovrebbe fare:
  sprintfnum(str, ":%d:", 100, 1) --> :1000:
  sprintfnum(str, ":%5d:", 100, 1) --> : 1000:
  sprintfnum(str, ":%+6d:", 100, 1) --> : +1000:
  sprintfnum(str, ":%+06d:", 100, 1) --> :+01000:
  sprintfnum(str, ":%d:", 100, -1) --> :10.0:
  sprintfnum(str, ":%5d:", 100, -1) --> : 10.0:
  sprintfnum(str, ":%+6d:", 100, -1) --> : +10.0:
  sprintfnum(str, ":%+06d:", 100, -1) --> :+010.0:
Il campo width della sprintfnum() dovrebbe specificare, come per la
sprintf(), la larghezza del campo numerico comprensivo degli zeri
aggiuntivi (div > 0) o della parte decimale (div > 0).
Inoltre vorrei usare solo int (16 bit) e non long (anche per scrivere
1000000 con x=1 e div=6) ed evitare operazioni in virgola mobile.
Concettualmente non è molto difficile: si tratta di aggiungere zeri
(div > 0) oppure spostare le cifre decimali più a destra di un posto
(div < 0). Ci ho provato, ma non ne ho ricavato molto.
Siccome la stringa di formattazione non può essere usata con una
sprintf() così come è passata alla sprintfnum(), per evitare il suo
parsing e manipolazione stavo pensando di dividere prefisso e
suffisso:
  sprintfnum(str, ":", "%d", ":", 100, 1) --> :1000:
  ...
In questo modo il terzo argomento è sicuramente del tipo:
  %szwd
dove s è vuoto oppure '+'; z è vuoto oppure '0'; w è vuoto oppure un
numero (anche ad una sola cifra per semplificare).
Ma anche così mi sembra complicato, soprattutto nei casi del tipo:
  sprintfnum(str, ":", "%+9d", ":", 1, -5);  --> : +0.00001:
  sprintfnum(str, ":", "%+09d", ":", 1, -5);  --> :+00.00001:
Quindi stavo ancora pensando di passare direttamente gli argomenti s
(0 o 1), z (0 o 1) e w (numero). I due esempi sopra diventano:
  sprintfnum(str, ":", 1, 0, 9, ":", 1, -5);
  sprintfnum(str, ":", 1, 1, 9, ":", 1, -5);
Mi sto complicando la vita? Esiste un modo più semplice per risolvere
questo problema?

 [...]
Ho creato questa schifezza, che ne dite?
---
void
sprintfnum(char *str,
	const char *prefix,
	const char *num_fmt,
	const char *suffix,
	int x,
	int div)
{
	int n, nn;
	int sign = 0, zero = 0, width = 0;
	n = sprintf(str, "%s", prefix);
	/* Parsing num_fmt che deve essere del tipo
	 *   %szwd
	 * dove
	 *   % è il carattere '%' (anche se non è usato),
	 *   s (opzionale) è il carattere '+',
	 *   z (opzionale) è il carattere '0',
	 *   w (opzionale) è un numero (anche a più cifre),
	 *   d è il carattere 'd' (anche se non è usato).
	 */
	nn = 1;
	if (num_fmt[nn] == '+') {	/* C'è il carattere '+'? */
		sign = 1;
		nn++;
	}
	if (num_fmt[nn] == '0') {	/* C'è il carattere '0'? */
		zero = 1;
		nn++;
	}
	width = strtol(&num_fmt[nn], NULL, 10);	/* Se non c'è numero, width =
0 */
	if (!width) {
		width = 1;
	}
	if (div > 0) {		/* Aggiungere zeri in coda */
		char fmt[7];
		sprintf(fmt, "%%%s%s%dd",
				sign ? "+" : "", zero ? "0": "", width - div);
		n += sprintf(&str[n], fmt, x);
		/* Aggiunge tanti zeri quanto è div */
		while(div--) {
			str[n++] = '0';
		}
		str[n] = '\0';
	} else if (div < 0) {
		char fmt[7];
		div = -div;
		sprintf(fmt, "%%%s%s%dd",
				sign ? "+" : "", zero ? "0": "",
				width - 1 < div + 1 + sign ? div + 1 + sign : width - 1);
		n += sprintf(&str[n], fmt, x);
		/* Sposta le cifre decimali verso destra e aggiunge il punto.
		 * Se c'è lo zero-padding a sinistra non c'è problema, ma se
		 * non c'è gli spazi devono essere trasformati in '0' e il
		 * segno spostato a sinistra. */
		str[n + 1] = '\0';
		while (div--) {
			if (str[n - 1] == ' ') {
				str[n] = '0';
			} else if ((str[n - 1] == '+') || (str[n - 1] == '-')) {
				str[n] = '0';
				str[n - 2] = str[n - 1];
			} else {
				str[n] = str[n - 1];
			}
			n--;
		}
		str[n] = '.';
		/* Prima del punto non può esserci nè uno spazio nè il segno */
		if (str[n - 1] == ' ') {
			str[n - 1] = '0';
		} else if ((str[n - 1] == '+') || (str[n - 1] == '-')) {
			str[n - 2] = str[n - 1];
			str[n - 1] = '0';
		}
	}
	/* Se mult < 0 l'indice n non punta più a fine stringa. Quindi devo
	 * per forza fare una strcat(). */
	strcat(str, suffix);
}
---


mmhhh... dividere e stampare il float? :-/
Ciao!

Ciao,
solo un'opinione: prima di lanciarmi nella scrittura di una tal 
funzione, darei un'occhiata alle "funzioni a parametri variabili" del C. 
Ti semplificherebbe non poco l'analisi.
saluti

 mmhhh... dividere e stampare il float? :-/
Beh, uno dei requisiti era non usare long int e float. Su un povero
processore
a 16 bit i float sono una tortura (sia in termini di tempo di
esecuzione che di
spazio codice con le librerie per la gestione dei float).
Comunque ho anche un altro dubbio.
  int x = 123;
  float f = (float)x / 10;
Quanto vale esattamente f? Sicuramente non 12.3, anche se un numero
molto
simile. Quando utilizzo la
  printf("%3.1f", f)
sono sicuro di avere 12.3 e non un altro numero?


che cosa complicata.
hai 123 e vuoi scrivere 12.3.
123/10 = 12
123 - (12*10) = 3
poi crei la stringa 12 + "." + 3
fine.
Ciao Jack

 fine.
ok, scusa, l'avevi scritto sopra in maniera molto piu' complicata.
Ciao Jack


Chiamasi "modulo":
resto = (123 % 10);
a.

 a.
visto che sembra essere su un micro con risorse limitare a volte
passare per la trada lunga risulta essere piu' efficiente.
Ciao Jack


Non direi proprio: di solito l' implementazione del
linguaggio è quella migliore: in ambiente embedded
poi e' quasi sicuramente cosi'.
Ciao
A.

 float).
beato te, quello su cui sto lavorando è a 8 bit e ci sto
impementando un algoritmo prettamente matematico.
Convertire quasi tutto in aritmetica intera è stata una
vera tragedia :O
-- 
Lei e io ci siamo guardati, abbiamo capito al
volo e poi di corsa a letto. Ciascuno nel proprio.

 poi e' quasi sicuramente cosi'.
no.
e' quasi mai cosi. In ambiente embedded le librerie devono tener conto
dell'utilizzo generale della funzione.
Nel caso della divisione deve tener conto se e' tra numeri positivi,
negativi, ecc. Se poi sei sfigato e il micro non ha l'istruzione per
le divisioni tra negativi e/o tra variabili a piu' di 8bit e/o ecc.
allora tutti questi casi devono essere gestivi via SW (dalla
libreria).
Quindi spesso e' meglio fare altre operazioni piuttosto di una
divisione, se non e' proprio necessario.
Senza contare che spesso il compilatore non sfrutta appieno
l'architettura e/o l'instruction set del micro.
Quindi spesso, se serve l'efficienza si scrive la parte di codice in
maiera che il complatore capisca meglio quello che vuoi fare e
ottimizzi di conseguenza oppure si scrive direttamente in assembly.
Ciao Jack