数值字面量

Perl中的数值有三种保存方式:整数方式、双精度浮点数方式和十进制字符串方式。

并且,Perl允许如下几种方式的数值字面量:

  • 允许使用下划线作为分隔符(主要用作千分位分隔符)
  • 允许定义二进制(前缀0b)、八进制(前缀0)、十六进制整数(前缀0x)
  • 允许使用指数形式定义数值
  • 允许定义为字符串格式的数值,这一点需要特别注意,因为其他语言几乎都不认为字符串格式的数值是数值类型
    • 允许有任意前缀空白
    • 不考虑前缀空白和前缀负号,从第一个数字开始到第一个非数字字符,中间的部分被当作该字符串的数值,但如果有非数字字符,在开启了warnings时将给出警告
$n = 1234;              # 十进制整数
$n = 34_123_456;
$n = 33_22_56;
$n = 0b1110011;         # 二进制整数
$n = 01234;             # 八进制整数
$n = 0x1234;            # 十六进制整数
$n = 12.34e-56;         # 指数定义方式
$n = "-12.34e56";       # 字符串方式定义的数值
$n = "1234";            # 字符串方式定义的数值
$n = "   1234";         # 有前缀空白的字符串数值
$n = "  123a";          # 可以当数值123使用,但warnings时会警告

有必要多提一句,Perl可以将字符串格式的数值当作数值来使用,是perl在内部做了很多工作之后的成果。字符串格式的数值仍然是按照字符串方式存储的(因为它本身就是字符串),但在需要将它当作数值使用时,perl会将它转换为合理的数值并将转换后的数值在该字符串的内存中缓存下来,下次再需要使用它的数值格式时,直接从缓存字段中取出数值来。也就是说,转换之后,在内存中同时保存了这份字符串数据以及它的数值格式。

算数运算和类型转换

数值支持多种算术运算方式,其中几种如下:

+ - * / %     加、减、乘、除、取模
++ --         自增、自减
-             加负号
**            幂运算
abs           取绝对值

对于这些算术操作,有以下几点需要注意:

  • perl会尝试将它们的运算结果转换为整数,但如果运算结果会丢失精度,结果将转换为浮点数以避免丢失精度

    5 / 2.5    # 结果为2,而不是浮点数的2.0
    
  • + - * / % < > <= >= == != -(负号) ++ -- abs,会尝试将操作数转换为整数

    "33" + 1   # 结果为34,因为字符串数值也是数值
    
  • **做幂运算时,运算顺序是从右向左,并且会强制将其右边的数转换为浮点数后进行运算

    2 ** 3 ** 2  # 结果是512而不是64,等价于2 ** (3 ** 2)
    
  • ++ --,不仅会对整数值进行自增自减,还可以对浮点数进行自增自减,但不能直接对数值字面量进行运算,它们只能对变量(左值)进行操作。另外,++还可以对由[a-zA-Z0-9]组成的字符串进行自增,但--没有该功能

    my $x = 1.2;
    ++$x;  # 2.2,会尝试转换为整数,
           # 但会丢失精度,因此保留浮点数
    
    # 对字符或字符串自增,但不能自减
    my $str = "abc";
    ++$str;    # abd
    

判断变量是否是数值

由于Perl的变量只区分标量、数组、hash三种类型,其中数值、字符串等都属于标量,而Perl没有内置的用于检测变量是否是数值的方案。

但有时候确实需要判断一个变量是否是数值,此时可以通过算术运算时的隐式类型转换判断,也可以通过Regexp::Common正则库来判断。

例如,下面是通过隐式类型转换的方式进行的简单判断:

my $a;          # undef
my $b = "";     # 空字符串
my $c = 33;     # 数值
my $d = "33";   # 字符串数值
my $e = "3a";   # 字符串

if($a + 0 ne $a){ say "a not numeric"; }
if($b + 0 ne $b){ say "b not numeric"; }
if($c + 0 ne $c){ say "c not numeric"; }
if($d + 0 ne $d){ say "d not numeric"; }
if($e + 0 ne $e){ say "e not numeric"; }

将输出:

a not numeric
b not numeric
e not numeric

有时候,undef和空字符串也可以当作数值0来使用,如果把它们也当作数值来看待,则加上条件:

if($a + 0 ne $a && $a + 0 != 0){ say "a not numeric"; }
if($b + 0 ne $b && $b + 0 != 0){ say "b not numeric"; }
if($c + 0 ne $c && $c + 0 != 0){ say "c not numeric"; }
if($d + 0 ne $d && $d + 0 != 0){ say "d not numeric"; }
if($e + 0 ne $e && $e + 0 != 0){ say "e not numeric"; }

将输出:

e not numeric