Analyser judicieusement la notation scientifique?
-
10-07-2019 - |
Question
Je veux pouvoir écrire une fonction qui reçoit un nombre en notation scientifique sous forme de chaîne et en sépare le coefficient et l'exposant en tant qu'éléments séparés. Je pourrais simplement utiliser une expression régulière, mais le nombre entrant ne sera peut-être pas normalisé et je préférerais pouvoir normaliser puis décomposer les parties.
Un collègue a en partie trouvé une solution utilisant VB6 mais ce n’est pas tout à fait là, comme le montre la transcription ci-dessous.
cliVe> a = 1e6
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 10 exponent: 5
aurait dû être 1 et 6
cliVe> a = 1.1e6
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.1 exponent: 6
correct
cliVe> a = 123345.6e-7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: -2
correct
cliVe> a = -123345.6e-7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: -2
devrait être -1,233456 et -2
cliVe> a = -123345.6e+7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: 12
correct
Des idées? A propos, Clive est une CLI basée sur VBScript et se trouve sur mon weblog .
La solution
Google sur " notation scientifique regexp " affiche un numéro des correspondances, y compris celle-ci ( ne l'utilisez pas !!! ! ) qui utilise
*** warning: questionable ***
/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/
qui inclut des cas tels que -.5e7 et + 00000e33 (que vous ne souhaitez peut-être pas autoriser tous les deux).
Au lieu de cela, je vivement vous recommander d'utiliser la syntaxe sur le site Web JSON de Doug Crockford qui documente explicitement ce qui constitue un nombre en JSON. Voici le diagramme de syntaxe correspondant tiré de cette page:
(source: json.org )
Autres conseils
En me basant sur la réponse la mieux notée, j’ai légèrement modifié l’expression rationnelle pour qu'elle soit / ^ [+ \ -]? (? =.) (?: 0 | [1-9] \ d *)? ( ?: \. \ d *)? (?: \ d [eE] [+ \ -]? \ d +)? $ /
.
Les avantages offerts sont les suivants:
- permet de faire correspondre des nombres tels que
.9
(j'ai rendu le(?: 0 | [1-9] \ d *)
facultatif avec?
) - empêche d'apparier l'opérateur au début et les chaînes de longueur nulle (utilise lookahead,
(? =.)
) - empêche la correspondance
e9
car il nécessite le\ d
avant la notation scientifique
Mon objectif à cet égard est de l’utiliser pour capturer des chiffres significatifs et effectuer des calculs significatifs. Donc, je vais aussi le séparer en capturant des groupes comme suit: / ^ [+ \ -]? (? =.) (0 | [1-9] \ d *)? (\. \ D *)? (?: (\ d) [eE] [+ \ -]? \ d +)? $ /
.
Une explication sur la façon d’obtenir des chiffres significatifs:
- La capture complète est le numéro que vous pouvez remettre à
parseFloat ()
- Les correspondances 1 à 3 apparaîtront sous forme de chaîne ou de caractère non défini. Par conséquent, si vous les combinez (remplacez les
indéfinis
par''
) devrait donner le numéro d'origine à partir duquel les chiffres significatifs peut être extrait.
Cette expression rationnelle empêche également la correspondance des zéros remplis à gauche, ce que JavaScript accepte parfois, mais que j'ai rencontré des problèmes et qui n'ajoute rien aux chiffres significatifs. Je considère donc que la prévention des zéros remplacés à gauche est un avantage (en particulier dans les formulaires). Cependant, je suis sûr que l'expression régulière pourrait être modifiée pour engloutir des zéros remplis à gauche.
Un autre problème que je vois avec cette regex est qu'elle ne correspondra pas à 90.e9
ou à d'autres numéros similaires. Cependant, j'estime que cette correspondance ou des correspondances similaires est hautement improbable, car c'est la convention en notation scientifique d'éviter de tels nombres. Bien que vous puissiez le saisir en JavaScript, vous pouvez tout aussi bien saisir 9.0e10
et obtenir les mêmes chiffres significatifs.
MISE À JOUR
Lors de mes tests, j'ai également remarqué l'erreur de correspondance entre '.'
. Par conséquent, la prévision doit être modifiée en (? = \. \ D | \ d)
, ce qui conduit à la regex finale:
/^[+\-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/
Voici un code Perl que je viens de pirater rapidement.
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";