Numeri troppo grandi o troppo piccoli – 1

Scrooge-McDuck-Money-Bin-1000x562

Alle volte capita di avere a che fare con numeri che sono non come quelli normali, immaginabili facilmente dagli umani (anche da me).

Escluderei subito Paperon de’ Paperoni che, secondo Don Rosa, possiede five multiplujillion, nine impossibidillion, seven fantasticatrillion dollars and sixteen cents e per Carl Banks il deposito (Bin) contiene three cubic acres of money e qui non è solo questione di ordini di grandezza ma peggio, roba che in confronto gli stringhisti sono sensati, per cui salto e ricomincio.

Prendiamo per esempio gli astronomi, loro trattano roba come:

Sono parenti stretti dei fisici, a occhio nudo non si riesce (almeno io non ne sono capace) a distinguere gli uni dagli altri. Ma anche loro hanno (danno) numeri che –per esempio:

  • Massa del protone: 1.67262171(29)e-27 kg ossia 0.938 GeV/c2;
  • L’elettrone è ancora più piccolo (ma siamo lì) invece il neutrino (uno a caso, non facciamo i difficili) andiamo su 0.05 eV/c2 o 8.913309e-38 kg.

 Senza senso  Per fortuna esistono i multipli e i sottomultipli delle unità di misura, almeno per SI, il Sistema internazionale di unità di misura.

Quindi usare SI, unità e multipli; poi quando servono se ne fanno altre, come già visto per il parsec. Per esempio i fisici usano il barn (b = 10e-28 m2). Umorismo qui: un fienile di quelle dimensioni non ha senso (tranne per i fisici, ovviamente, Marco lo usa a volte, anche inverso).

Senza arrivare a questi estremi anch’io (con l’aiuto di più amici, questi script sono nati come un gioco, facendo altro) sto tentando di addomesticarli per quanto possibile. Il lavoro non è concluso ma un paio di script consentono di visualizzare il numero normale in formato scientifico (2sc) e viceversa (2ns).

Rendere il numero in formato scientifico – 2sc

#!/bin/bash
N=$@
if [ -z $NFORM ]; then
    NFORM="%.4e"
fi
N=$(echo $N | sed 's/\./\,/g')
N=$(printf "$NFORM  " $N)
N=$(echo $N | sed 's/\,/\./g')
echo $N | xclip -f -selection clipboard

Il lavoro viene svolto da printf, on base al formato, %.ne dove n è un intero positivo non troppo grande; di deafault vale 4, cioè quattro cifre decimali ma è possibile cambiarlo facilmente senza modificare lo script (come vedremo tra breve).

C’è un problema, per alcuni di noi, quelli abituati a programmare: Bash vuole la virgola come simbolo di definizione per la parte decimale (per noi italiani) contrariamente all’abitudine e a (quasi) tutti i linguaggi di programmazione. Niente panico con sed si risolve prima di subito.

Visto che il risultato di queste trasformazioni è spesso da usare immediatamente entra in gioco xclip che inserisce nella clipboard il risultato visualizzato.

$ 2sc 9460700000000000
9.4607e+15
$ 2sc 30857000000000000
3.0857e+16
$ 2sc 149597870700
1.4960e+11

e, ovviamente

$ 2sc 9460700000000000 30857000000000000 49597870700
9.4607e+15 3.0857e+16 4.9598e+10

Il numero di cifre decimali è facilmente ridefinibile: se non va bene il alore 4 di default si può ridefinire la variabile d’ambiente NFORM, con

export NFORM='%.3e'

e si può verificare con

$ echo $NFORM
%.3e

e si avrà

$ 2sc 9460700000000000 30857000000000000 49597870700
9.461e+15 3.086e+16 4.960e+10

NFORM conserva il valore all’interno del terminale, chiudendolo diventa indefinita. È possibile annullarla (sdefinirla?, undefinirla?) con export NFORM=

Ma si può semplificare, con uno script (particolare, in qualche misura atipico), ecco nprec

#!/bin/bash
if [ -z $1 ]; then
    export NFORM=''
else
    export NFORM='%.'$1'e'
fi
echo $NFORM

e ottengo

$ source nprec 2
%.2e
$ echo $NFORM
%.2e
$ source nprec

$ echo $NFORM

$

La particolarità è che lo script va eseguito con source.

Da formato scientifico all’usuale – 2ns

#!/bin/bash
R=""
while [[ $# -gt 0 ]]; do
    N=$(echo $1 | sed -e 's/\./\,/')
    N=$(printf "%.30f" $N)
    ye=$(echo $N | grep -c "\,")
    if [[ $ye == 1 ]]; then
        N=$(echo $N | sed -e 's/[0]*$//')
        N=$(echo $N | sed -e 's/\,$//')
    fi
    N=$(echo $N | sed -e 's/\,/\./')
    R=$R' '$N
    shift
done
echo $R  | xclip -f -selection clipboard

La trasformazione della rappresentazione del numero è svolta da printf.

Notare l’if che controlla se il numero ha una parte decimale; se sì sed elimina gli 0 finali e se il piunto è finale anche questo viene eliminato. Queste operazioni richiedono di trattare un numero alla volta, sempre il primo ($1) perché l’ultima istruzione del loop while è shift che, appunto, elimina l’elemento appena trattato.

$ 2ns 9.4607e15
9460700000000000
$ 2ns 3.0857e16
30857000000000000
$ 2ns 1.49597870700e+11
149597870700

OK, funziona, con numeri non troppo grandi (sorry Mr Scrooge!)

$ 2ns 1.23e+25
12300000000000000000000000
$ 2ns 1.23e+29
123000000000000000002281701376
$ 2ns 1.23e+30
1229999999999999999988457275392
$ 2ns 1.23e+35
122999999999999999998102457678823424
$ 2ns 1.23e+40
12300000000000000000542639153683841941504

OOPS! la precisione, a un certo punto non sono più 0 tondi ma altre cifre 😡 Ma tanto questi numeri sono illeggibili (o meglio io non riesco a leggerli). Da fare (prossimamente?) per renderli più amichevoli: separare con spazi le cifre in gruppi di 3 come usano i commerciali, volendo anche con i punti proprio come fanno i Rag.

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: