Analizzare la notazione scientifica in modo sensato?
-
10-07-2019 - |
Domanda
Voglio essere in grado di scrivere una funzione che riceve un numero in notazione scientifica come una stringa e divide da esso il coefficiente e l'esponente come elementi separati. Potrei semplicemente usare un'espressione regolare, ma il numero in entrata potrebbe non essere normalizzato e preferirei essere in grado di normalizzare e quindi suddividere le parti.
Un collega ha ottenuto una soluzione parziale utilizzando VB6 ma non è del tutto lì, come mostra la trascrizione di seguito.
cliVe> a = 1e6
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 10 exponent: 5
avrebbe dovuto essere 1 e 6
cliVe> a = 1.1e6
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.1 exponent: 6
corretta ??em>
cliVe> a = 123345.6e-7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: -2
corretta ??em>
cliVe> a = -123345.6e-7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: -2
dovrebbe essere -1.233456 e -2
cliVe> a = -123345.6e+7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: 12
corretta ??em>
Qualche idea? A proposito, Clive è una CLI basata su VBScript e può essere trovata sul mio weblog .
Soluzione
Google su " scientific notation regexp " mostra un numero delle partite, tra cui questo ( non utilizzarlo !!! ! ) che utilizza
*** warning: questionable ***
/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/
che include casi come -.5e7 e + 00000e33 (entrambi i quali potresti non voler consentire).
Raccomanderei invece vivamente di utilizzare la sintassi sul sito Web JSON di Doug Crockford a> che documenta esplicitamente ciò che costituisce un numero in JSON. Ecco il diagramma di sintassi corrispondente preso da quella pagina:
(fonte: json.org )
Se guardi la riga 456 del suo json2.js script (conversione sicura da / a JSON in javascript), vedrai questa porzione di regexp:
/-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/
che, ironia della sorte, non corrisponde al suo diagramma di sintassi .... (sembra che dovrei presentare un bug) Credo che una regexp che implementa quel diagramma di sintassi sia questa:
/-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/
e se vuoi consentire anche un iniziale +, ottieni:
/[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/
Aggiungi parentesi di cattura a tuo piacimento.
Consiglio vivamente anche di approfondire una serie di casi di test, per assicurarti di includere quelle possibilità che vuoi includere (o non includere), come:
allowed:
+3
3.2e23
-4.70e+9
-.2E-4
-7.6603
not allowed:
+0003 (leading zeros)
37.e88 (dot before the e)
Buona fortuna!
Altri suggerimenti
Partendo dalla risposta con il punteggio più alto, ho modificato leggermente la regex in / ^ [+ \ -]? (? =.) (?: 0 | [1-9] \ d *)? ( ?: \ \ d *) (:.????. \ d [ee] [+ \ -] \ d +) $ /
I vantaggi che offre sono:
- consente numeri corrispondenti come
.9
(ho reso(?: 0 | [1-9] \ d *)
opzionale con?
) - impedisce la corrispondenza solo dell'operatore all'inizio e impedisce la corrispondenza di stringhe di lunghezza zero (utilizza lookahead,
(? =.)
) - impedisce la corrispondenza di
e9
perché richiede il\ d
prima della notazione scientifica
Il mio obiettivo in questo è di usarlo per catturare figure significative e fare calcoli significativi. Quindi lo suddivido anche in gruppi di acquisizione come questo: /^[+\-[?(?=.)(0|[1-9 lasting\d*)?(\.\d ? (?: (\ d) *) [ee] [+ \ -] \ d +) $ /
Una spiegazione di come ottenere cifre significative da questo:
- L'intera acquisizione è il numero che puoi consegnare a
parseFloat()
- Le corrispondenze 1-3 verranno visualizzate come non definite o come stringhe, quindi combinandole (sostituisci
non definito
con''
) dovresti fornire il numero originale da cui cifre significative può essere estratto.
Questa regex impedisce anche la corrispondenza degli zeri con riempimento a sinistra, che a volte accetta JavaScript ma che ho visto causare problemi e che non aggiunge nulla a cifre significative, quindi vedo la prevenzione degli zeri con riempimento a sinistra come un vantaggio (specialmente nei moduli). Tuttavia, sono sicuro che il regex potrebbe essere modificato per inghiottire zeri con riempimento a sinistra.
Un altro problema che vedo con questo regex è che non corrisponderà a 90.e9
o ad altri numeri simili. Tuttavia, trovo altamente improbabile questo o simili abbinamenti in quanto è la convenzione nella notazione scientifica per evitare tali numeri. Sebbene sia possibile inserirlo in JavaScript, è possibile inserire facilmente 9.0e10
e ottenere le stesse cifre significative.
Aggiorna
Nei miei test, ho anche riscontrato l'errore che poteva corrispondere a '.'
. Quindi il look-ahead dovrebbe essere modificato in (? = \. \ D | \ d)
che porta alla regex finale:
/^[+\-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/
Ecco un po 'di codice Perl che ho appena hackerato insieme rapidamente.
my($sign,$coeffl,$coeffr,$exp) = $str =~ /^\s*([-+])?(\d+)(\.\d*)?e([-+]?\d+)\s*$/;
my $shift = length $coeffl;
$shift = 0 if $shift == 1;
my $coeff =
substr( $coeffl, 0, 1 );
if( $shift || $coeffr ){
$coeff .=
'.'.
substr( $coeffl, 1 );
}
$coeff .= substr( $coeffr, 1 ) if $coeffr;
$coeff = $sign . $coeff if $sign;
$exp += $shift;
say "coeff: $coeff exponent: $exp";