Глава 2 Числа

Введение

Числа составляют основные типы данных практически в любом языке программирования, однако даже с ними могут возникнуть неожиданные сложности. Случайные числа, числа с дробной частью, числовые последовательности и преобразования строк в числа - все это вызы Peri старается по возможности облегчить вам жизнь, и его средства для работы с числами не являются исключением из этого правила. Если скалярное значение интерпретируется в программе как число, то Peri преобразует его в числовую форму. Читаете ли вы числов Если строка используется в числовом контексте (например, в математическом выражении), Peri старается интерпретировать ее как число, однако у него нет возможности сообщить о том, что строка в действительности не соответствует числу. Встречая не-числовой си В рецепте 2.16 объясняется, как получить число из строк с шестнадцатерич-ными или восьмеричными представлениями чисел - например, "Oxff". Peri автоматически преобразует литералы в программном коде (поэтому $а = 3 + Oxff присвоит $а значений 258), но это н А если трудностей с целыми числами окажется недостаточно, числа с плавающей запятой преподнесут целый букет новых проблем. Во внутреннем представлении дробные числа хранятся в формате с плавающей запятой. Они представляют вещественные числа лишь приближен Числа, прочитанные из файла или встретившиеся в программе в виде литералов, преобразуются из десятичного представления (например, 0.1) во внутреннее. Невозможно точно представить 0.1 в виде двоичного числа с плавающей запятой - подобно тому, как 1/3 невоз При выполнении арифметических операций с двоичными представлениями чисел с плавающей запятой накапливаются ошибки. Значение выражения 3*0.1 не совпадает с двоичной кодировкой числа 0.3. Это означает, что числа с плавающей запятой в Peri нельзя просто срав В рецепте 2.4 показано, как преобразовать ASCII-строку с двоичным представлением числа (например, "1001") в целое (9 для приведенного примера) и обратно. Рецепт 2.5 описывает три способа выполнения некоторой операции с каждым элементом последовательного м Случайным числам посвящено сразу несколько рецептов. Функция Peri rand возвращает число с плавающей запятой от 0 до 1 или от 0 до своего аргумента. Мы покажем, как получить случайное число в конкретном интервале, как сделать их "еще более случайными" и ка Глава завершается рецептами, относящимися к тригонометрии, логарифмам, умножению матриц, комплексным числам. Заодно вы найдете ответ на часто встречающийся вопрос: "Как включить в выводимое число запятую?"

2.1. Проверка строк на соответствие числам

Проблема

Требуется проверить, соответствует ли строка допустимому числу. Эта проблема часто возникает при проверке входных данных (например, в сценариях CGI).

Решение

Сравните строку с регулярным выражением, которое совпадает со всеми интересующими вас разновидностями чисел:
if ($stnng =~ /PATTERN/) {
# является числом
} else {
# не является числом
}
.

Комментарий

Все зависит от того, что именно понимать под числом. Даже простые на первый взгляд понятия - например, целое - заставят вас поломать голову над тем, какие строки следует отнести к этой категории. Например, что делать с начальным -для положительных чисел? Сначала решите, какие символы допустимы, а какие - пет. Затем сконструируйте для отобранных символов регулярное выражение. Ниже приведены некоторые стандартные конструкции для самых распространенных ситуаций (что-то вроде полуфабрикатов для нашей поваренн
# Содержит нецифровые символы
warn "has nondigits"
if /\D/;
# He является натуральным числом
warn "not a natural number"
unless /"\d+$/; # Отвергает -3
# He является целым числом
warn "not an integer"
unless /"-'''\d+$/; # Отвергает +3
warn "not an integer"
unless /"[+-]?\d+$/;
# He является десятичным числом
warn "not a decimal number"
unless /"-?\d+\.?\d*$/; # Отвергает .2
warn "not a decimal number"
unless /~-?(?:d+(?:\.\d)?|\.\d+)$/,
# He является вещественным числом С warn "not a C float"
unless /--([+-]?)(^=\d|\.\d)\d*(\.\d*)'?([Ee]([+-]Ad+))?$/:
В этих шаблонах не обрабатываются особые случаи Infinity и NaN в записи IEEE. Если вы не боитесь, что члены комитета IEEE придут к вашему компьютеру и начнут бить вас по голове копиями соответствующих стандартов, вероятно, об этих странных "числах" можно Для строк с начальными или конечными пробелами эти шаблоны не подходят. Либо вставьте в них соответствующую логику, либо вызовите функцию trim из рецепта 1.14. В POSIX-системах Peri поддерживает функцию POSIX: :strtod. Ее семантика чрезвычайно громоздка, поэтому мы приведем функцию getnum для упрощения доступа. Эта функция получает строку и возвращает либо преобразованное число, либо undef для строк, не соответс
sub getnum {
USE POSIX qw(strtod);
my $str = shift;
$str =~ s/\s+$//;
$! = 0;
my($num, $unparsed) = strtod($str);
if (($str eq '') | ($unparsed != 0) || $!) {
return;
} else {
return $num;
}
}
sub is_numeric { defined scalar &getnum }

Смотри также -----------
Описание синтаксиса регулярных выражений в perlre(1), страница руководства strtod(3); документация по стандартному модулю POSIX.

2.2. Сравнение чисел с плавающей запятой

Проблема

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

Решение

Воспользуйтесь функцией sprintf и отформатируйте числа до определенного десятичного разряда, после чего сравните полученные строки:
# equal(NUM1, NUM2, ACCURACY); возвращает true, если NUM1 и NUM2 # совпадают на ACCURACY десятичных разрядов.

sub equal {
my ($A, $B, $dp) = @_;
return sprintf("%.${dp}g", $A) eq sprintf("%.${dp}g", $A);
}
Альтернативное решение - преобразовать числа в целые, умножая их на соответствующий коэффициент. Комментарий Процедура equal понадобилась из-за того, что в компьютерах многие числа с плавающей запятой представляются с ограниченной точностью. Дополнительная информация приведена в разделе "Введение". При фиксированном количестве цифр в дробной части (например, в денежных суммах) проблему можно решить преобразованием в целое число. Если сумма 3.50 будет храниться в виде 350, а не 3.5, необходимость в числах с плавающей запятой отпадает. Десятичная точка снова появляется в выводимых данных:

$wage = 536; # $5,36/час
$week = 40 * $wage; # $214.40
printf("0ne week's wage is: \$%.2f\n", $week/100);

One week's wage is: $214.40 Редко требуется сравнивать числа более чем до 15 разряда.
> Смотри также -------------------------------- Описание функции sprintf в perlfunc(1); описание переменной $# в странице руководства perlvar(1); документация по стандартному модулю Math::BigFloat Функция sprintf используется в рецепте 2.3. Также обращайтесь к разделу 4.2.2 тома 2 "Искусство программир

2.3. Округление чисел с плавающей запятой

Проблема

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

Решение

Для получения непосредственного вывода воспользуйтесь функциями
Peri sprint или printf:
$rounded = sprintf("%FORMATf", $unrounded);

Комментарий

Округление серьезно отражается на работе некоторых алгоритмов, потому ис пользуемый метод должен быть точно указан. В особо важных приложениях (например, в финансовых вычислениях или системах наведения ракет) грамотный программист реализует свою собственн Однако во многих ситуациях можно просто воспользоваться функцией sprintf. Формат f позволяет указать количество разрядов, до которого округляется аргумент. Peri округляет последний разряд вверх, если следующая цифра равна 5 и более, и вниз в противном слу

$а = 0.255
$b = sprintf("%.2f", $a);
print "Unrounded: $a\nRounded: %.2f\n", $a;
Unrounded:
0.255 Rounded:
0.26 Unrounded:
0.255 Rounded: 0.26 Существуют три функции, предназначенные для округления чисел с плавающей запятой до целых: int, cell и floor. Встроенная функция Peri int возвращает целую часть числа с плавающей запятой (при вызове без аргумента она использует $_). Функции модуля POSIX f
use POSIX;
print "number\tint\floor\tceil\n";
@а = { 3.3 , 3.5 , 3.7 , -3,3};
foreach (@a) {
printf( "% .1f\t% .1f\t% ,1f\t% .1f\n", $_, int($_), floor($_), ceil($_) );
}
number int floor ceil

3.3        3.0 3.0 4.0
3.5         3.0 3.0 4.0
3.7        3.0 3.0 4.0
-3.3        3.0 -4.0 -3.0

> Смотри также Описание функций sprintf и int в perlfunc(1) описание функций floor и ceil в документации по стандартному модулю POSIX. Методика использования sprintf для округления представлена в рецепте 2.2.
© copyright 2000 Soft group

Используются технологии uCoz