Числа с плавающей запятой, хранящиеся в компьютере, состоят из двух частей: целого и экспоненты, в которых база берется и умножается на целую часть.
Если компьютер работал в базе 10, 0.1
будет 1 x 10⁻¹
, 0.2
будет 2 x 10⁻¹
, а 0.3
будет 3 x 10⁻¹
. Целочисленная математика проста и точна, поэтому добавление 0.1 + 0.2
, очевидно, приведет к 0.3
.
Компьютеры обычно не работают в базе 10, они работают в базе 2. Вы все равно можете получить точные результаты для некоторые значения, например 0.5
, равны 1 x 2⁻¹
, а 0.25
- 1 x 2⁻²
, а их добавление приводит к 3 x 2⁻²
или 0.75
. Точно.
Проблема связана с числами, которые могут быть представлены точно в базе 10, но не в базе 2. Эти цифры должны округляться до их ближайшего эквивалента. Предполагая, что для 64-битного формата с плавающей точкой IEEE используется очень общий формат, ближайшим номером к 0.1
является 3602879701896397 x 2⁻⁵⁵
, а ближайшим номером к 0.2
является 7205759403792794 x 2⁻⁵⁵
; добавление их результатов в 10808639105689191 x 2⁻⁵⁵
или точное десятичное значение 0.3000000000000000444089209850062616169452667236328125
. Номера с плавающей запятой, как правило, округлены для отображения.
Здесь нужно объяснить две вещи: поведение количественных групп и дизайн метода findall()
.
В вашем первом примере [abc]
соответствует a
, который фиксируется в группе №1. Затем он соответствует b
и фиксирует его в группе №1, переписывая a
. Затем снова с c
, и это то, что осталось в группе №1 в конце матча.
Но он делает в соответствии со всей строкой. Если вы использовали search()
или finditer()
, вы могли бы посмотреть на MatchObject и увидеть, что group(0)
содержит abc
и group(1)
содержит c
. Но findall()
возвращает строки, а не MatchObjects. Если групп нет, он возвращает список общих совпадений; если есть группы, список содержит все захваты, но не общее совпадение.
Таким образом, оба ваших регулярных выражения соответствуют всей строке, но первая также захватывает и отбрасывая каждый символ в отдельности (что бесполезно). Только неожиданное поведение findall()
делает его похожим на то, что вы получаете разные результаты.
Вот как я мог бы подумать об этом. ([abc])+
пытается повторить захваченную группу. Когда вы используете «+» после группы захвата, это не означает, что вы собираетесь получить две захваченные группы. То, что заканчивается, по крайней мере для регулярного выражения Python и большинства реализаций, заключается в том, что «+» заставляет итерацию, пока группа захвата не содержит только последнее совпадение.
Если вы хотите захватить повторяющееся выражение, вам нужно для отмены порядка «(...)» и «+», например вместо ([abc])+
используйте ([abc]+)
.
Группирование просто дает разные предпочтения.
([abc])+
=> Найти один из выбора. Может соответствовать одному или нескольким. Он находит, что все условия соблюдены как + означает 1 или более. Это разлагает регулярное выражение на две стадии.
Пока негруппированный рассматривается как целое.
В первом примере у вас есть повторная захваченная группа, которая захватывает только последнюю итерацию. Здесь c
.
([abc])+
[/g2]
Во втором примере вы сопоставляете один символ в списке одно и неограниченное время.
[abc]+
[/g3]
вход «abc»
[abc]
соответствует одному символу => «a»
[abc]+
+ Между одним и неограниченным временем, как можно дольше => «abc»
([abc])
Группа захвата ([abc]) => «a»
([abc])+
+ Повторная группа захвата будет захватывать только последнюю итерацию => «c «