Mi servono 6-7 decimali ma float non va....

Il problema è noto, la cifra 2749999.9925 trattata con float diventa 2750000.0000, perché non riesce a gestire più di 7 cifre totali.
Nick Gammon qui ha risolto il problema con la sua lib; il link è proprio all'esempio che farebbe al caso mio, l'ho testato e funziona. Ora iniziano i miei guai, perché speravo di dover semplicemente aggiungere la lib ed uno-due comandi di attivazione e conversione al mio sketch per risolvere, invece mi trovo un casino di roba e non riesco a discernere cosa serve e cosa non mi serve.
La condizione del mio sketch è estremamente semplice: leggo un valore digitale e lo assegno alla variabile "valore", se uso una float ottengo risultati stravaganti, quindi sono costretto ad una unsigned long e "tagliare" i decimali. Se potessi recuperarli ne sarei felice, ho speranza di non dover usare tutti quei comandi, perché mi pare servano alla fine a generare quel cavolo di numero immenso, ma invece temo che quei comandi li dovrò usare al posto dei normali operatori aritmetici, ogni volta che eseguo un calcolo su tali numeri; in effetti ho necessità di fare un paio di divisioni ed un paio di moltiplicazioni per fattori numerici fissi (quindi non altre variabili), tutto qui :~
Qualcuno ha mai usato questa lib e può darmi un minimo di dritte, risparmiandomi un a settimana di studio, tempo che non ho e che non so nemmeno se mi basterà, viste le mie "conoscenze"?

Non l'ho mai usata, ma guardando gli esempi di @gammon, si direbbe che gestisce lui con dei puntatori a zone di memoria che allocherà dinamicamente questi numeri big.

primo dichiara quanti digit usare (e mi pare poteva usare byte invece di int), nel tuo caso 4 cifre decimali

#include "BigNumber.h"
static int DIGITS=4;

Poi dichiara le variabili di tipo bc_num e li inizializza a NULL, perciò presumo siano puntatori.

bc_num a=NULL, b = NULL, c = NULL;

Ma sembra che puoi anche dichiarate un bc_num x("123"); oppure bc_num y(123); ma solo int in questo caso

Per inizializzare passa da stringa:

bc_str2num(&a, "42",DIGITS);
bc_str2num(&b, "18254546", DIGITS);

e per i calcoli usa solo sue funzioni che probabilmente sono le uniche che conoscono che struttura c'e' dietro a quei pointer.

bc_multiply(a,b,&c,DIGITS);

Nella libreria ultima versione vedo che ha anche creato l'overload per le operazioni più comuni tra due BignNumber, ovvero c=ab; o altre operazioni +-/%

Ha anche fatto overload dei confronti perciò puoi fare
if(a>=b) dove
Una volta che le variabili big non gli servono più si ricorda di liberare la memoria:

bc_free_num (&b);a e b sono numeri big

bc_free_num (&c);

EDIT: nella ultima versione della libreria ha messo un pò di esempi (pde invece di ino e sono un pò diversi dagli esempi del link che hai postato). La libreria ora deriva da Printable e perciò puoi fare direttamente Serial.printl(c); dove c è un bignumber.
L'ha migliorata parecchio.

// BigNumber test: multiplication
#include "BigNumber.h"

void setup ()
{ Serial.begin (115200);
  Serial.println ();
  BigNumber::begin ();  // initialize library
  BigNumber::setScale (5);
  // test multiplication  
  BigNumber a = "564328376.33";
  BigNumber b = "18254546.22";
  BigNumber c = a + b;
  Serial.println (c);
}  // end of setup

void loop () { }

Se però usi cifre che assegni a dei bc, devono essere int, questo non mi funziona, sembra che b lo consideri troncandolo a int:

BigNumber a = "564328376";
BigNumber b =  111111111.22;
BigNumber c = a + b;

Qui se devi passargli un float:
http://forum.arduino.cc/index.php?topic=85692.msg1127105#msg1127105

E mi sembra importante leggere le note al fondo qui e la funzione "setScale" (impossibile fare un riassunto):

Ciao Michele.

Ho letto che la DUE gestisce variabili double "vere" a 8 byte e questa sarebbe la soluzione al tuo problema.

Vuoi vedere che tra un po' anche la UNO e la MEGA avranno le variabili di questo tipo?

cyberhs:
Vuoi vedere che tra un po' anche la UNO e la MEGA avranno le variabili di questo tipo?

No, i float a 4 byte sono una limitazione dell'implementazione del compilatore gcc-avr per le mcu ad 8 bit.

Leo, conosci le motivazioni tecniche di una simile limitazione?

cyberhs:
Leo, conosci le motivazioni tecniche di una simile limitazione?

La potenza di calcolo dei controller a 8 Bit.
Ciao Uwe

karma Nid, nel doppio senso del termine XD XD
Mi pare che l'ultima parte del tuo intervento mi possa risolvere abbastanza rapidamente ogni problema, domattina mi metto a fare prove, quindi stay tuned perché certamente avrò bisogno...intando io visualizzo su LCD, ma vediamo una cosa per volta.
Un solo chiarimento: io ho scaricato la lib del link del primo post, tu parli di versioni diverse, è perché ti sei letto l'intero Topic (a proposito, un grosso grazie a Leo per la segnalazione :)) o ci sono link differenti? qual è quello più recente?
Grazie mille!

Io ho scaricato la libreria da link che tu hai messo. Poi sempre a quel link ho visto che gli esempi sono un pò diversi rispetto a quelli della libreria. Secondo me a quel link c'è poi il link al sito di @gammon che è sempre l'ultima versione.
Infatti nel link che hai postato a pagina 3 dice che ha migliorato la libreria rendendola "stampabile" con la print() e println()

La difficoltà della libreria secondo me è assegnare a questi numeri un valore.
Lo puoi fare o attraverso una stringa o solo da int, no da un float direttamente:

bc_num a=NULL,b=NULL;
a="12345.678";  // ok
b=32767;          // ok
b=111111111.2222;   // no, troncato a int

Nella setup() dovrai usare setScale(4) visto che vuoi 4 decimali

BigNumber::begin ();  // initialize library  
BigNumber::setScale (4);

La libreria si appoggia ad una ulteriore libreria allegata che ha le primitive tipo un bc_str2num() e bc_int2num()
Non c'e' un bc_float2num() oppure bc_long2num() purtroppo
Si potrebbe chiedere a @Gammon se potrebbe inserirla.

domattina mi metto a fare qualche piccola prova, e leggerò la tua prossima risposta, ora sto dormendo sulla tastiera :sleeping: :sleeping: :sleeping: a questa domanda: nell'esempio che ho linkato lui alla fine ottiene un numero con un intero e 103 decimali, come ha fatto?

Inoltre ora a me viene il grandioso dubbio.... attualmente leggo un valore frutto di una serie di calcoli con timer e roba varia (una ulteriore lib, ovviamente), ma se il limite è dato dagli 8 bit, il mio valore iniziale 2.749.999,25 Hz REALE, applicato al micro, lo vedrò mai lato software? cioè se alla fine dei calcoli della prima lib io non riesco in alcun modo a preservare questo valore ed i suoi decimali, come faccio a ricostruire quello originale, da cui esso deriva?

  1. usa questa -> BigNumber::setScale (103);
    mi sembra che la sua libreria internamente memorizza come stringa (vettore di char)

  2. non credo riuscirai se non passi a fare i calcoli con la BigNumber

menniti, domanda stupida stupida.. se ti servono 6/7 cifre decimali, non puoi memorizzare i valori, anzichè come 10^0, come 10^-6?

puoi usare degli uint64_t per evere spazio a sufficienza (sono dei long long :grin:), certo devi fare attenzione alla matematica.

la libreria di nick l'ho usata con successo recentemente ma ti ciuccia la RAM che è un piacere, se vuoi l'esempio funzionante (us anche l'add on di un altro tipo per seno coseno etcc) segui il link in firma, tra gli arduinoSketch cerca il "testGps" e trovi tutto

leggi qui la mia avventura:
http://forum.arduino.cc/index.php?topic=106614.msg799858#msg799858

Lesto, per favore, chiariscimi questo concetto.

Ho provato a dichiarare una variabile long long, ma l'IDE 1.0.5 non me la riconosce:

long long Prova = 12345678901;

error: integer constant is too large for 'long' type

Ma se faccio così il compilatore non da errore:
long long Prova = 12345678901ULL;

cyberhs:
Ma se faccio così il compilatore non da errore:
long long Prova = 12345678901ULL;

Perché serve l'opzione "-mint8" ad avrgcc per poter utilizzare il long long int, purtroppo non è possibile fornirla tramite ide di Arduino, si può fare solo con AvrStudio, o direttamente da riga di comando, ovvero dall'ide di Arduino non è possibile utilizzare gli interi a 64 bit anche se supportati dalle avrlibc.

Allora mi sa che dovete aggiornare la vostra toolchain.
Io con la toolchain Atmel compilo senza errori:

longlong.jpg

@leo però tu usi la 1.0.5+ (la nightly build). Magari è quella che cambia ?

EDIT: a me questo lo compila senza problemi con ide 1.0.5 e non ho cambiato la toolchain (ho però installato anche AvrStudio 6.1 ma non penso influenzi l'IDE !?!)

long long x=0;
void setup() {}

void loop()
{ x++;
}

Come detto da @cyberhs da problemi con una costante senza qualificatore, ma mi sembra corretto. Comunque i long long funzionano con il semplice 1.0.5 senza cambiare toolchain. C'e' solo quell'appunto sull'inizializzazione da costante.

La 1.0.5+ è la 1.0.5 compilata dai sorgenti con alcune cose aggiunte di recente, ma si tratta di piccolezze che non riguardano altro.

Io ho Linux quindi non ho né Windows né la possibilità di usare AvrStudio per cui non so se AvrStudio c'entri qualcosa. Ma se l'IDE non trova una toolchain sul sistema dovrebbe usare quella di serie. Forse vede la toolchain di AvrStudio e compila con quella.

PS:
io comunque è dall'anno scorso che uso i long long, anche alcune vecchie versioni del mio leOS ne facevano uso. Però è anche da più di 1 anno che uso la toolchain Atmel. Sarà quello senz'altro.

long long Prova = 12345678901;

error: integer constant is too large for 'long' type

Ma se faccio così il compilatore non da errore:
long long Prova = 12345678901ULL;

scusate ma a me sembra un comportamento normalissimo, di defult i numeri "harcodati" sono intesi come int, o nel caso ci sia un punto come float. e 12345678901 è troppo grande per un int. noi vogliamo un long long, e quindi bisogna specificarlo con "LL". Occhio che "ULL" siginifica unsigned long long.

IDE 1.0.1 su linux, funziona, però mi sa che usa il compilatore di sistema avr-g++ 4.7.0 e non quello nella cartella arduino. (è installato tramite repository)

Scusate, ma io devo farvi tornare a terra :grin:
Quindi devo ricominciare dal mio dubbio. La FreqCounter, che vi quoto:

#include <FreqCounter.h>

unsigned long FreqCounter::f_freq;

volatile unsigned char FreqCounter::f_ready;
volatile unsigned char FreqCounter::f_mlt;
volatile unsigned int FreqCounter::f_tics;
volatile unsigned int FreqCounter::f_period;
volatile unsigned int FreqCounter::f_comp;

void FreqCounter::start(int ms) {

#if defined (__AVR_ATmega168__) || defined (__AVR_ATmega48__) || defined (__AVR_ATmega88__) || defined (__AVR_ATmega328P__) || (__AVR_ATmega1280__)


    TIMSK0 &=~(1<<TOIE0);       // disable Timer0  //disable  millis and delay
    delayMicroseconds(50);      // wait if any ints are pending
    
    f_period=ms;
  
  
    if (f_comp ==0) f_comp=1;  // 0 is not allowed in del us
	
    // hardware counter setup ( refer atmega168.pdf chapter 16-bit counter1)
    TCCR1A=0;                  // reset timer/counter1 control register A
    TCCR1B=0;              	   // reset timer/counter1 control register A
    TCNT1=0;           		   // counter value = 0
    // set timer/counter1 hardware as counter , counts events on pin T1 ( arduino pin 5)
    // normal mode, wgm10 .. wgm13 = 0
    
    TCCR1B |=  (1<<CS10) ;// External clock source on T1 pin. Clock on rising edge.
    TCCR1B |=  (1<<CS11) ;
    TCCR1B |=  (1<<CS12) ;
    
    // timer2 setup / is used for frequency measurement gatetime generation
    TCCR2A=0;
    TCCR2B=0;
    
    // timer 2 presaler set to 128 / timer 2 clock = 16Mhz / 256 = 62500 Hz
    TCCR2B |=  (1<<CS20) ;
    TCCR2B &= ~(1<<CS21) ;
    TCCR2B |=  (1<<CS22) ;

    //set timer2 to CTC Mode with OCR2A is top counter value
    TCCR2A &= ~(1<<WGM20) ;
    TCCR2A |=  (1<<WGM21) ;
    TCCR2A &= ~(1<<WGM22) ;
    OCR2A = 124;                // CTC divider by 125
   
    f_ready=0;                  // reset period measure flag
    f_tics=0;                   // reset interrupt counter
    GTCCR = (1<<PSRASY);        // reset presacler counting
    TCNT2=0;                    // timer2=0
    TCNT1=0;                    // Counter1 = 0
    
    TIMSK2 |=(1<<OCIE2A);       // enable Timer2 Interrupt
    
                                // External clock source on T1 pin. Clock on rising edge.
    TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);        //   start counting now     
    #endif
}
//******************************************************************
//  Timer2 Interrupt Service is invoked by hardware Timer2 every 1ms = 1000 Hz
//  16Mhz / 128 / 125 = 1000 Hz
//  here the gatetime generation for freq. measurement takes place: 

ISR(TIMER2_COMPA_vect) {
										// multiple 2ms = gate time = 100 ms
if (FreqCounter::f_tics >= FreqCounter::f_period) {         	
    // end of gate time, measurement ready
    // GateCalibration Value, set to zero error with reference frequency counter
    //  delayMicroseconds(FreqCounter::f_comp); // 0.01=1/ 0.1=12 / 1=120 sec 
    delayMicroseconds(FreqCounter::f_comp);
    TCCR1B = TCCR1B & ~7;   			// Gate Off  / Counter T1 stopped 
    TIMSK2 &= ~(1<<OCIE2A);    			// disable Timer2 Interrupt
    TIMSK0 |=(1<<TOIE0);     			// enable Timer0 again // millis and delay
    FreqCounter::f_ready=1;             // set global flag for end count period
    
                                        // calculate now frequeny value
    FreqCounter::f_freq=0x10000 * FreqCounter::f_mlt;  // mult #overflows by 65636
    FreqCounter::f_freq += TCNT1;      	// add counter1 value
    FreqCounter::f_mlt=0;
    
    }
    FreqCounter::f_tics++;            	// count number of interrupt events
    if (TIFR1 & 1) {          			// if Timer/Counter 1 overflow flag
    FreqCounter::f_mlt++;               // count number of Counter1 overflows
    TIFR1 =(1<<TOV1);        			// clear Timer/Counter 1 overflow flag
    }
    // PORTB = PORTB ^ 32;  				// int activity test
}

alla fine di tutto mi restituisce FreqCounter::f_freq che è dichiarato come UL, quindi io già qui ho perso i decimali o no? In onestà ricordo che nelle prove iniziali avevo provato una float freq a cui assegnavo il valore di FreqCounter::f_freq, ottenendo risultati indesiderati.
Le domande sono due:
1 - devo modificare questa lib per recuperare i decimali o la UL semplicemente li "nasconde" e mi basta cambiare tipo per vederli?
2 - come faccio a passare questo valore alla BigNumber?

@lesto, in svariate prove fatte con SuperLeo siamo arrivati ad usare uint64_t o come si chiama, ma il problema non si è risolto.

se è UL sei SENZA decimali.

visto che fai tampo/ripetizioni (se non erro) e che questi sono valori facilmente rappresentati senza errori da arduino, crei due BigNumber a partire da questi e poi li dividi.

BigNumber::setScale (10); //vogliamo 10 decimali
BigNumber tempo = micros()-tempoIniziale; //okok qè un esempio, lo so che non è così la lib
BigNumber ripetizioni = f_tics; //a naso
BigNumer risulato = tempo/ripetizoni;

facile no?

lesto:

BigNumber::setScale (10); //vogliamo 10 decimali

BigNumber tempo = micros()-tempoIniziale; //okok qè un esempio, lo so che non è così la lib
BigNumber ripetizioni = f_tics; //a naso
BigNumer risultato = tempo/ripetizioni;

Ocio solo che la libreria accetta assegnamenti solo da stringhe o da int.
Credo che la 2° riga non funziona , magari tempoIniziale è int ma micros() non ritorna long?
Credo che la 3° riga funziona solo f_tics è int