perlnumber - Perl 中数字和数字运算的语义
$n = 1234; # decimal integer
$n = 0b1110011; # binary integer
$n = 01234; # octal integer
$n = 0x1234; # hexadecimal integer
$n = 12.34e-56; # exponential notation
$n = "-12.34e56"; # number specified as a string
$n = "1234"; # number specified as a string
本文档描述了 Perl 如何在内部处理数字值。
此处完全忽略了 Perl 的运算符重载功能。运算符重载允许用户定义数字的行为,例如对任意大整数、任意精度的浮点数、对“奇异”数字(如模算术或 p 进位算术)的运算等。有关详细信息,请参见 overload。
Perl 可以用 3 种不同的方式在内部表示数字:作为本机整数、作为本机浮点数和作为十进制字符串。十进制字符串可能具有指数表示法部分,如 "12.34e-56"
。此处“本机”表示“用于构建 perl 的 C 编译器支持的格式”。
当我们讨论原生整数时,“原生”一词的含义与涉及原生浮点数时并不完全相同。对于整数,“原生”一词的唯一含义是支持的最大和最小真整数数量的限制接近于 2 的幂。但是,“原生”浮点数有一个最基本的限制:它们只能表示那些在转换为二进制分数时具有相对“短”表示形式的数字。例如,0.9 不能用原生浮点数表示,因为 0.9 的二进制分数是无限的
binary0.1110011001100...
随着序列 1100
不断重复。除了此限制之外,当二进制数表示为浮点数时,其指数也受到限制。在典型硬件上,浮点值可以存储具有多达 53 个二进制位的数字,以及 -1024 到 1024 之间的二进制指数。在十进制表示中,这接近于 16 个十进制位和 -304..304 范围内的十进制指数。所有这一切的结果是,Perl 无法在不丢失信息的情况下将 12345678901234567 这样的数字存储为浮点数。
类似地,十进制字符串只能表示那些具有有限十进制展开的数字。作为字符串,因此具有任意长度,这些数字的指数或十进制位数没有实际限制。(但请意识到我们正在讨论的只是这些数字的存储规则。您可以存储如此“大”的数字这一事实并不意味着这些数字上的运算将使用所有有效数字。有关详细信息,请参见“数字运算符和数字转换”。)
事实上,以原生整数格式存储的数字可以存储在有符号原生形式或无符号原生形式中。因此,存储为原生整数的 Perl 数字的限制通常为 -2**31..2**32-1,对于 64 位整数,则进行适当的修改。同样,这并不意味着 Perl 只能对该范围内的整数进行运算:可以在浮点格式中存储更多整数。
总结一下,Perl 数值只能存储那些具有有限十进制展开或“短”二进制展开的数字。
如前所述,Perl 可以以三种格式中的任何一种存储数字,但大多数运算符通常只理解其中一种格式。当将数值作为参数传递给此类运算符时,它将被转换为运算符理解的格式。
可能的六种转换
native integer --> native floating point (*)
native integer --> decimal string
native floating_point --> native integer (*)
native floating_point --> decimal string (*)
decimal string --> native integer
decimal string --> native floating point (*)
这些转换受以下一般规则支配
如果源数字可以用目标形式表示,则使用该表示。
如果源数字超出目标形式中可表示的范围,则使用最接近的范围的表示。(信息丢失)
如果源数字介于目标形式中可表示的两个数字之间,则使用这些数字之一的表示。(信息丢失)
在本机浮点数 --> 本机整数
转换中,结果的大小小于或等于源的大小。(“舍入为零”。)
如果十进制字符串 --> 本机整数
转换无法在不丢失信息的情况下完成,则结果与转换序列 十进制字符串 --> 本机浮点数 --> 本机整数
兼容。特别是,舍入强烈偏向于 0,尽管像 "0.99999999999999999999"
这样的数字有可能会舍入为 1。
限制:上面标记为 (*)
的转换涉及 C 编译器执行的步骤。特别是,所用编译器的错误/特性可能会导致某些上述规则被破坏。
Perl 运算采用数字参数时,会以四种不同的方式处理该参数:它们可能会强制其采用整数、浮点数或字符串格式之一;或者它们可能会根据操作数的格式表现得不同。强制数字值采用特定格式不会改变存储在该值中的数字。
所有需要整数格式参数的操作符都将参数视为模运算,例如,在 32 位架构上为 mod 2**32
。因此,sprintf "%u", -1
提供的结果与 sprintf "%u", ~0
相同。
二元运算符 +
-
*
/
%
==
!=
>
<
>=
<=
和一元运算符 -
abs
和 --
将尝试将参数转换为整数。如果两种转换都可以在不损失精度的情况下进行,并且可以在不损失精度的情况下执行运算,则使用整数结果。否则,参数将转换为浮点格式,并使用浮点结果。转换的缓存(如上所述)意味着整数转换不会丢弃浮点数上的小数部分。
++
的行为与上述其他运算符类似,但如果它是一个匹配格式 /^[a-zA-Z]*[0-9]*\z/
的字符串,则使用 perlop 中描述的字符串增量。
use integer
期间的算术运算符在 use integer;
生效的范围内,上面列出的几乎所有运算符都会强制其参数变为整数格式,并返回一个整数结果。例外是 abs
、++
和 --
,它们的行为不会因 use integer;
而改变
诸如 **
、sin
和 exp
之类的运算符会强制参数变为浮点数格式。
如果参数不是字符串,则会强制其变为整数格式。
use integer
期间的按位运算符强制参数变为整数格式。此外,移位运算在内部使用有符号整数,而不是默认的无符号整数。
强制参数变为整数格式。例如,这适用于 sysread
的第三个和第四个参数。
强制参数变为字符串格式。例如,这适用于 printf "%s", $value
。
虽然强制参数变为特定形式不会改变存储的数字,但 Perl 会记住此类转换的结果。特别是,虽然第一次此类转换可能很耗时,但重复操作不需要重新进行转换。
Ilya Zakharevich [email protected]
Gurusamy Sarathy <[email protected]> 进行的编辑调整
Nicholas Clark <[email protected]> 进行的 5.8.0 更新