¿Analizando la notación científica con sensatez?


Quiero ser capaz de escribir una función que recibe un número en notación científica como una cadena y divide fuera de ella el coeficiente y el exponente como elementos separados. Podría usar una expresión regular, pero el número entrante puede no ser normalizado y preferiría ser capaz de normalizar y luego romper las partes.

Un colega tiene medio camino de una solución usando VB6 pero no está del todo allí, como 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

correcto

cliVe> a = 123345.6e-7
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.233456 exponent: -2

correcto

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

correcto

¿Alguna idea? Por cierto, Clive es un CLI basado en VBScript y se puede encontrar en mi weblog .

Author: Chad Birch, 2009-03-12

3 answers

Google on "scientific notation regexp" muestra una serie de coincidencias, incluyendo esta (¡no lo uses!!!!) que utiliza

*** warning: questionable ***
/[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/

Que incluye casos tales como -.5e7 y +00000e33 (ambos pueden no querer permitir).

En su lugar, yo altamente recomendaría usar la sintaxis en el sitio web de Doug Crockford JSON que documenta explícitamente lo que constituye un número en JSON. Aquí está el diagrama de sintaxis correspondiente tomado de esa página:

Texto alternativo http://www.json.org/number.gif

Si nos fijamos en la línea 456 de su json2.js script (conversión segura a/desde JSON en javascript), verás 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 este:

/-?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/

Y si desea permitir un + inicial, así, usted obtener:

/[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/

Agrega paréntesis de captura a tu gusto.

También te recomiendo que desarrolles un montón de casos de prueba, para asegurarte de incluir esas posibilidades que quieres 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!

 66
Author: Jason S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-18 15:07:23

Aquí hay un código Perl que acabo de hackear 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";
 1
Author: Brad Gilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-18 04:01:56

Basándome en la respuesta más calificada, modifiqué ligeramente la expresión regular para que fuera /^[+\-]?(?=.)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/.

Los beneficios que esto proporciona son:

  1. permite emparejar números como .9 (hice el (?:0|[1-9]\d*) opcional con ?)
  2. evita la coincidencia solo del operador al principio y evita la coincidencia de cadenas de longitud cero (usa lookahead, (?=.))
  3. impide la coincidencia e9 porque requiere el \d antes de la notación científica

Mi objetivo en esto es para usarlo para capturar figuras significativas y hacer matemáticas significativas. Así que también voy a cortarlo con la captura de grupos como así: /^[+\-]?(?=.)(0|[1-9]\d*)?(\.\d*)?(?:(\d)[eE][+\-]?\d+)?$/.

Una explicación de cómo obtener cifras significativas de esto:

  1. La captura completa es el número que puede entregar a parseFloat()
  2. Las coincidencias 1-3 se mostrarán como cadenas indefinidas o, por lo que combinarlas (reemplazar undefined ' s con '') debería dar el número original del que se pueden obtener cifras significativas extraer.

Esta expresión regular también evita la coincidencia de ceros acolchados a la izquierda, 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 acolchados a la izquierda como un beneficio (especialmente en formas). Sin embargo, estoy seguro de que la expresión regular podría modificarse para engullir ceros acolchados a la izquierda.

Otro problema que veo con esta expresión regular es que no coincidirá con 90.e9 u otros números similares. Sin embargo, me parece que este o similares coincidencias altamente es poco probable, ya que es la convención en notación científica evitar tales números. Aunque puede introducirlo en JavaScript, puede ingresar 9.0e10 con la misma facilidad y lograr las mismas cifras significativas.

UPDATE

En mi prueba, también capté el error que podría coincidir con '.'. Así que la mirada hacia adelante debe modificarse a (?=\.\d|\d) que conduce a la expresión regular final:

/^[+\-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/
 1
Author: Troy Weber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-10 19:47:04