Analizando la notación científica con sensatez?
-
10-07-2019 - |
Pregunta
Quiero poder escribir una función que reciba un número en notación científica como una cadena y separe el coeficiente y el exponente como elementos separados. Simplemente podría usar una expresión regular, pero el número entrante puede no estar normalizado y preferiría poder normalizar y luego dividir las partes.
Un colega tiene parte de una solución usando VB6 pero no está del todo, como lo muestra la transcripción a continuación.
cliVe> a = 1e6
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 10 exponent: 5
debería haber sido 1 y 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
debe ser -1.233456 y -2
cliVe> a = -123345.6e+7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: 12
correct
¿Alguna idea? Por cierto, Clive es una CLI basada en VBScript y se puede encontrar en mi weblog .
Solución
Google en " notación científica regexp " muestra un número de coincidencias, incluido este ( ¡no lo uses! ! ) que usa
*** warning: questionable ***
/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/
que incluye casos como -.5e7 y + 00000e33 (los cuales es posible que no desee permitir).
En cambio, recomendaría altamente que use la sintaxis en el sitio web JSON de Doug Crockford que documenta explícitamente lo que constituye un número en JSON. Aquí está el diagrama de sintaxis correspondiente tomado de esa página:
(fuente: json.org )
Si mira la línea 456 de su json2.js script (conversión segura a / desde JSON en javascript), verá esta parte de una expresión regular:
/-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/
que, irónicamente, no coincide con su diagrama de sintaxis ... (parece que debería presentar un error) Creo que una expresión regular que implementa ese diagrama de sintaxis es esta:
/-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/
y si desea permitir también un + inicial, obtendrá:
/[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/
Agregue paréntesis de captura a su gusto.
También recomendaría encarecidamente que desarrolle un montón de casos de prueba, para asegurarse de incluir las posibilidades que desea incluir (o no incluir), como:
allowed:
+3
3.2e23
-4.70e+9
-.2E-4
-7.6603
not allowed:
+0003 (leading zeros)
37.e88 (dot before the e)
¡Buena suerte!
Otros consejos
Partiendo de la respuesta mejor calificada, modifiqué un poco la expresión regular para que sea / ^ [+ \ -]? (? =.) (?: 0 | [1-9] \ d *)? ( ?: \. \ d *)? (?: \ d [eE] [+ \ -]? \ d +)? $ /
.
Los beneficios que esto proporciona son:
- permite números coincidentes como
.9
(hice el(?: 0 | [1-9] \ d *)
opcional con?
) - evita la coincidencia solo con el operador al principio y evita la coincidencia de cadenas de longitud cero (usa lookahead,
(? =.)
) - evita la coincidencia de
e9
porque requiere el\ d
antes de la notación científica
Mi objetivo en esto es usarlo para capturar figuras significativas y hacer cálculos matemáticos significativos. Así que también voy a dividirlo con grupos de captura de la siguiente manera: /^[+\-font>?(?=.)(0|[1-9font>\d*)?(\.\d *)? (?: (\ d) [eE] [+ \ -]? \ d +)? $ /
.
Una explicación de cómo obtener cifras significativas de esto:
- La captura completa es el número que puede entregar a
parseFloat()
- Las coincidencias 1-3 se mostrarán como indefinidas o cadenas, por lo que combinarlas (reemplazar
undefined
's con' '
) debería dar el número original del que se obtienen cifras significativas se puede extraer.
Esta expresión regular también evita la coincidencia de ceros con margen izquierdo, que JavaScript a veces acepta pero que he visto causar problemas y que no agrega nada a las cifras significativas, por lo que veo la prevención de ceros con margen izquierdo como un beneficio (especialmente en los formularios). Sin embargo, estoy seguro de que la expresión regular podría modificarse para engullir ceros con relleno izquierdo.
Otro problema que veo con esta expresión regular es que no coincidirá con 90.e9
u otros números similares. Sin embargo, considero que esto o coincidencias similares son altamente improbables ya que es la convención en notación científica evitar tales números. Aunque puede ingresarlo en JavaScript, puede ingresar fácilmente 9.0e10
y lograr las mismas cifras significativas.
UPDATE
En mis pruebas, también detecté el error de que podría coincidir con '.'
. Por lo tanto, el look-ahead debe modificarse a (? = \. \ D | \ d)
que conduce a la expresión regular final:
/^[+\-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/
Aquí hay un código Perl que acabo de piratear juntos rápidamente.
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";