当字符串为十六进制且前缀为“0x”时,字符串转换为整数时不一致;

发布于 2024-11-16 18:22:01 字数 2915 浏览 2 评论 0 原文

使用 PHP 5.3.5。不确定这在其他版本上如何工作。

我对使用包含数字的字符串感到困惑,例如 '0x4B0''1.2e3'。 PHP 处理此类字符串的方式对我来说似乎不一致。只有我一个人吗?或者这是一个错误?或者未记录的功能?或者我只是错过了文档中的一些神奇句子?

<?php

echo $str = '0x4B0', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1           -> ", var_dump($str * 1);         // int(1200)
echo "(int)        -> ", var_dump((int)$str);        // int(0)
echo "(float)      -> ", var_dump((float)$str);      // float(0)
echo PHP_EOL;

echo $str = '1.2e3', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1           -> ", var_dump($str * 1);         // float(1200)
echo "(int)        -> ", var_dump((int)$str);        // int(1)
echo "(float)      -> ", var_dump((float)$str);      // float(1200)
echo PHP_EOL;

在这两种情况下,is_numeric() 都会返回 true。此外,在这两种情况下,$str * 1 都会解析字符串并返回有效数字(一种情况为整数,另一种情况为浮点数)。

使用 (int)$str(float)$str 进行转换会产生意想不到的结果。

  • (int)$str 在任何情况下都只能解析数字,数字前面带有可选的“+”或“-”。
  • (float)$str 更高级,可以解析类似 ^[+-]?\d*(\.\d*)?(e[+-]?\d* )?,即可选的“+”或“-”,后跟可选的数字,后跟可选的小数点和可选的数字,后跟由“e”和可选的“+”或“-”组成的可选指数" 后跟可选数字。但在十六进制数据上失败。

相关文档:

  • is_numeric() - 指出“也允许使用十六进制表示法 (0xFF),但仅限没有符号、小数和指数部分”。如果用于测试字符串是否包含数字数据的函数返回 true,我希望 PHP 能够将此类字符串转换为数字。这似乎适用于 $str * 1,但不适用于强制转换。为什么?
  • 转换为整数 - 状态“在大多数情况下不需要强制转换,因为如果运算符、函数或控制结构需要整数参数,则会自动转换值”。在这样的语句之后,我希望 $s * 10(int)$s * 10 表达式以相同的方式工作并返回相同的结果。不过,如示例所示,这些表达式的计算方式不同。
  • 字符串转换为数字 -指出“有效的数字数据是一个可选的符号,后跟一个或多个数字(可选地包含小数点),后跟一个可选的指数”。 “指数”是“e”或“E”,后跟数字,例如,1.2e3 是有效的数值数据。没有提及符号(“+”或“-”)。它没有提到十六进制值。这与 is_numeric() 中使用的“数字数据”的定义冲突。然后,有建议“有关此转换的更多信息,请参阅 strtod(3) 的 Unix 手册页”,并且 man strtod 描述了其他数值(包括十六进制表示法) )。那么,读完本文后,十六进制数据应该是有效还是无效的数值数据?

那么...

  • is_numeric() 与 PHP 将字符串用作数字时的处理方式之间是否存在(或者应该存在)任何关系?
  • 为什么 (int)$s(float)$s$s * 1 的工作方式不同,即。当 $s0x4B01.2e3 时,给出完全不同的结果?
  • 如果字符串写为0x4B01.2e3,有什么方法可以将字符串转换为数字并保留其值吗? floatval() 根本不适用于十六进制,intval() 需要将 $base 设置为 16要使用十六进制,使用 (int)$str(float)$str 进行类型转换有时有效,有时无效,因此这些不是有效选项。我也不考虑 $n *= 1;,因为它看起来更像是数据操作而不是转换。在这种情况下,也不考虑自写函数,因为我正在寻找本机解决方案

Using PHP 5.3.5. Not sure how this works on other versions.

I'm confused about using strings that hold numbers, e.g., '0x4B0' or '1.2e3'. The way how PHP works with such strings seems inconsistent to me. Is it only me? Or is it a bug? Or undocumented feature? Or am I just missing some magic sentence in docs?

<?php

echo $str = '0x4B0', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1           -> ", var_dump($str * 1);         // int(1200)
echo "(int)        -> ", var_dump((int)$str);        // int(0)
echo "(float)      -> ", var_dump((float)$str);      // float(0)
echo PHP_EOL;

echo $str = '1.2e3', PHP_EOL;
echo "is_numeric() -> ", var_dump(is_numeric($str)); // bool(true)
echo "*1           -> ", var_dump($str * 1);         // float(1200)
echo "(int)        -> ", var_dump((int)$str);        // int(1)
echo "(float)      -> ", var_dump((float)$str);      // float(1200)
echo PHP_EOL;

In both cases, is_numeric() returns true. Also, in both cases, $str * 1 parses string and returns valid number (integer in one case, float in another case).

Casting with (int)$str and (float)$str gives unexpected results.

  • (int)$str in any case is able to parse only digits, with optional "+" or "-" in front of them.
  • (float)$str is more advanced and can parse something like ^[+-]?\d*(\.\d*)?(e[+-]?\d*)?, i.e., optional "+" or "-", followed by optional digits, followed by optional decimal point with optional digits, followed by optional exponent which consists of "e" with optional "+" or "-" followed by optional digits. Fails on hex data though.

Related docs:

  • is_numeric() - states that "Hexadecimal notation (0xFF) is allowed too but only without sign, decimal and exponential part". If function, meant to test if a string holds numeric data, returns true, I expect PHP to be able to convert such string to a number. This seems to work with $str * 1, but not with casting. Why?
  • Converting to integer - states that "in most cases the cast is not needed, since a value will be automatically converted if an operator, function or control structure requires an integer argument". After such statement, I expect both $s * 10 and (int)$s * 10 expressions to work the same way and to return the same result. Though, as shown in example, those expressions are evaluated differently.
  • String conversion to numbers - states that "Valid numeric data is an optional sign, followed by one or more digits (optionally containing a decimal point), followed by an optional exponent". "Exponent" is "e" or "E", followed by digits, e.g., 1.2e3 is valid numeric data. Sign ("+" or "-") is not mentioned. It does not mention hexidecimal values. This conflicts with definition of "numeric data" used in is_numeric(). Then, there is suggestion "For more information on this conversion, see the Unix manual page for strtod(3)", and man strtod describes additional numeric values (including HEX notation). So, after reading this, is hexidecimal data supposed to be valid or invalid numeric data?

So...

  • Is there (or, rather, should there be) any relation between is_numeric() and the way how PHP treats strings when they are used as numbers?
  • Why do (int)$s, (float)$s and $s * 1 work differently, i.e,. give completely different results, when $s is 0x4B0 or 1.2e3?
  • Is there any way to convert a string to a number and keep its value, if it is written as 0x4B0 or as 1.2e3? floatval() does not work with HEX at all, intval() needs $base to be set to 16 to work with HEX, typecasting with (int)$str and (float)$str sometimes works, sometimes does not work, so these are not valid options. I'm also not considering $n *= 1;, as it looks more like data manipulation rather than converting. Self-written functions also are not considered in this case, as I'm looking for native solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一瞬间的火花 2024-11-23 18:22:01

直接转换 (int)$str(float)$str 的工作原理根本没有什么不同:它们都从字符串中读取它们可以解释的尽可能多的字符作为相应类型的编号。

对于“0x4B0”,int 转换读取“0”(正常),然后读取“x”并停止,因为它无法将“x”转换为整数。对于浮点转换也是如此。

对于“1.2e3”,int 转换读取“1”(OK),然后读取“.”。并停止。浮点转换将整个字符串识别为有效的浮点表示法。

$str * 1 这样的表达式的自动类型识别比显式强制转换更加灵活。显式转换本质上要求整数和浮点数采用 printf 中的 %i%f 生成的格式。

也许您可以使用 intvalfloatval 而不是显式转换为 int 以提高灵活性。

最后,您的问题“十六进制数据应该是有效还是无效的数值数据?”很尴尬。不存在“十六进制数据”这样的东西。十六进制只是一个数字基数。 可以做的就是获取像“4B0”这样的字符串,并使用strtoul等将其解析为任何数字中的整数基数在 2 到 36 之间。[抱歉,那是废话。 PHP 中没有 strtoul。但是 intval 具有等效的功能,请参见上文。]

The direct casts (int)$str and (float)$str don't really work differently at all: They both read as many characters from the string as they can interpret as a number of the respective type.

For "0x4B0", the int-conversion reads "0" (OK), then "x" and stops, because it cannot convert "x" into an integer. Likewise for the float-conversion.

For "1.2e3", the int-conversion reads "1" (OK), then "." and stops. The float-conversion recognises the entire string as valid float notation.

The automatic type recognition for an expression like $str * 1 is simply more flexible than the explicit casts. The explicit casts require the integers and floats to be in the format produced by %i and %f in printf, essentially.

Perhaps you can use intval and floatval rather than explicit casts-to-int for more flexibility, though.

Finally, your question "is hexidecimal data supposed to be valid or invalid numeric data?" is awkward. There is no such thing as "hexadecimal data". Hexadecimal is just a number base. What you can do is take a string like "4B0" and use strtoul etc. to parse it as an integer in any number base between 2 and 36.[Sorry, that was BS. There's no strtoul in PHP. But intval has the equivalent functionality, see above.]

一人独醉 2024-11-23 18:22:01

intval 使用 strtol 来识别 oct/hex 前缀当base参数为零时,所以

var_dump(intval('0xef'));     // int(0)
var_dump(intval('0xff', 0));  // int(255)

intval uses strtol which recognizes oct/hex prefixes when the base parameter is zero, so

var_dump(intval('0xef'));     // int(0)
var_dump(intval('0xff', 0));  // int(255)
快乐很简单 2024-11-23 18:22:01

is_numeric() 与 PHP 将字符串用作数字时的处理方式之间是否存在(或者应该存在)任何关系?

PHP 中没有名为 numeric 的数据类型,is_numeric() 函数更多的是测试 PHP 可以将其解释为数字的内容。

就这种数字解释而言,在值前面添加 + 实际上会使 PHP 将其转换为数字:

$int = +'0x4B0';
$float = +'1.2e3';

您会发现这样解释 在字符串手册中,查找字符串转换为数字部分。

由于它是由运算符触发的,因此我不认为 PHP 中应该有一个函数可以执行相同的操作。那是多余的。


PHP 在内部使用名为 zendi_convert_scalar_to_number 的函数作为加法运算符(假设为 +),该函数将利用 is_numeric_string 来获取数字。

当与字符串一起使用时,is_numeric() 在内部调用完全相同的函数。

因此,要触发本机转换函数,我只需使用 + 运算符。这将确保您返回数字伪类型(int 或 float)。

参考: /Zend/ zend_operators.c/ext/标准/类型.c

Is there (or, rather, should there be) any relation between is_numeric() and the way how PHP treats strings when they are used as numbers?

There is no datatype called numeric in PHP, the is_numeric() function is more of a test for something that can be interpreted as number by PHP.

As far as such number interpreting is concerned, adding a + in front of the value will actually make PHP to convert it into a number:

$int = +'0x4B0';
$float = +'1.2e3';

You find this explained in the manual for string, look for the section String conversion to numbers.

As it's triggered by an operator, I don't see any need why there should be a function in PHP that does the same. That would be superfluous.


Internally PHP uses a function called zendi_convert_scalar_to_number for the add operator (assumable +) that will make use of is_numeric_string to obtain the number.

The exact same function is called internally by is_numeric() when used with strings.

So to trigger the native conversion function, I would just use the + operator. This will ensure that you'll get back the numeric pseudo-type (int or float).

Ref: /Zend/zend_operators.c; /ext/standard/type.c

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文