Разумно разбирается научная нотация? - PullRequest
39 голосов
/ 12 марта 2009

Я хочу иметь возможность написать функцию, которая получает число в научной нотации в виде строки и выделяет из нее коэффициент и показатель степени как отдельные элементы. Я мог бы просто использовать регулярное выражение, но входящий номер не может быть нормализован, и я предпочел бы иметь возможность нормализовать, а затем разбить части.

У коллеги есть часть пути решения с использованием VB6, но это не совсем так, как видно из стенограммы ниже.

cliVe> a = 1e6
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 10 exponent: 5 

должно было быть 1 и 6

cliVe> a = 1.1e6
cliVe> ? "coeff: " & o.spt(a) & " exponent: " & o.ept(a)
coeff: 1.1 exponent: 6

правильный

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

правильный

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

должно быть -1,233456 и -2

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

правильный

Есть идеи? Кстати, Clive - это CLI на основе VBScript, его можно найти в моем блоге .

Ответы [ 3 ]

71 голосов
/ 18 марта 2009

Google на "научное примечание регулярное выражение" показывает количество совпадений, в том числе этот ( не используйте его !!!! ), который использует

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

, который включает такие случаи, как -.5e7 и + 00000e33 (оба из которых вы, возможно, не захотите).

Вместо этого я бы настоятельно рекомендовал бы использовать синтаксис на сайте JSON Дуга Крокфорда , который явно документирует, что составляет число в JSON. Вот соответствующая синтаксическая диаграмма, взятая с этой страницы:

alt text
(источник: json.org )

Если вы посмотрите на строку 456 его скрипта json2.js (безопасное преобразование в / из JSON в javascript), вы увидите эту часть регулярного выражения:

/-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/

, что, по иронии судьбы, не соответствует его синтаксической диаграмме .... (похоже, я должен сообщить об ошибке) Я считаю, что регулярное выражение, которое реализует эту синтаксическую диаграмму, это:

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

и если вы хотите разрешить начальный +, вы получите:

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

Добавьте захватывающие скобки по своему вкусу.

Я также настоятельно рекомендую вам детализировать несколько тестовых случаев, чтобы убедиться, что вы включаете те возможности, которые вы хотите включить (или не включать), такие как:

allowed:
+3
3.2e23
-4.70e+9
-.2E-4
-7.6603

not allowed:
+0003   (leading zeros)
37.e88  (dot before the e)

Удачи!

2 голосов
/ 10 августа 2018

Опираясь на ответ с наивысшим рейтингом, я слегка изменил регулярное выражение, чтобы оно стало /^[+\-]?(?=.)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/.

Это дает следующие преимущества:

  1. позволяет сопоставлять числа типа .9 (я сделал (?:0|[1-9]\d*) необязательным с ?)
  2. предотвращает сопоставление только оператора в начале и предотвращает сопоставление строк нулевой длины (использует lookahead, (?=.))
  3. предотвращает сопоставление e9, поскольку требует \d перед научной нотацией

Моя цель в этом состоит в том, чтобы использовать его для захвата значимых цифр и выполнения значительных математических операций. Поэтому я также собираюсь разделить его на группы захвата, например: /^[+\-]?(?=.)(0|[1-9]\d*)?(\.\d*)?(?:(\d)[eE][+\-]?\d+)?$/.

Объяснение того, как получить значимые цифры из этого:

  1. Весь захват - это число, которое вы можете передать parseFloat()
  2. Совпадения 1-3 будут отображаться как неопределенные или как строки, поэтому их объединение (замените undefined на '') должно дать исходный номер, из которого можно извлечь значимые цифры.

Это регулярное выражение также предотвращает сопоставление левых нулей, которые иногда допускает JavaScript, но которые, как я видел, вызывают проблемы, и которые ничего не добавляют к значимым цифрам, поэтому я считаю предотвращение левыми нулями как преимущество (особенно в формах). Тем не менее, я уверен, что регулярное выражение можно изменить так, чтобы оно поглощало левые нули.

Другая проблема, которую я вижу с этим регулярным выражением, состоит в том, что он не будет соответствовать 90.e9 или другим подобным числам. Тем не менее, я считаю, что это или аналогичные совпадения крайне маловероятны, поскольку в научных обозначениях принято избегать таких чисел. Хотя вы можете ввести его в JavaScript, вы также можете легко ввести 9.0e10 и получить те же значимые цифры.

UPDATE

В моем тестировании я также обнаружил ошибку, которая может соответствовать '.'. Таким образом, прогноз следует изменить на (?=\.\d|\d), что приведет к окончательному регулярному выражению:

/^[+\-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:\d[eE][+\-]?\d+)?$/
1 голос
/ 18 марта 2009

Вот некоторый Perl-код, который я быстро взломал.

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";
...