Table of Contents
PHP浮点数的一个常见问题,PHP浮点数常见问题
您可能感兴趣的文章:
Home php教程 php手册 PHP浮点数的一个常见问题,PHP浮点数常见问题

PHP浮点数的一个常见问题,PHP浮点数常见问题

Jun 13, 2016 am 08:44 AM
php floating point number floating point number

PHP浮点数的一个常见问题,PHP浮点数常见问题

PHP是一种弱类型语言, 这样的特性, 必然要求有无缝透明的隐式类型转换, PHP内部使用zval来保存任意类型的数值, zval的结构如下(5.2为例):

struct _zval_struct {
 /* Variable information */
 zvalue_value value;  /* value */
 zend_uint refcount;
 zend_uchar type; /* active type */
 zend_uchar is_ref;
};
Copy after login

上面的结构中, 实际保存数值本身的是zvalue_value联合体:

typedef union _zvalue_value {
 long lval;     /* long value */
 double dval;    /* double value */
 struct {
  char *val;
  int len;
 } str;
 HashTable *ht;    /* hash table value */
 zend_object_value obj;
} zvalue_value;
Copy after login

今天的话题, 我们只关注其中的俩个成员, lval和dval, 我们要意识到, long lval是随着编译器, OS的字长不同而不定长的, 它有可能是32bits或者64bits, 而double dval(双精度)由IEEE 754规定, 是定长的, 一定是64bits.

请记住这一点, 造就了PHP的一些代码的”非平台无关性”. 我们接下来的讨论, 除了特别指明, 都是假设long为64bits

IEEE 754的浮点计数法, 我这里就不引用了, 大家有兴趣的可以自己查看, 关键的一点是, double的尾数采用52位bit来保存, 算上隐藏的1位有效位, 一共是53bits.

在这里, 引出一个很有意思的问题, 我们用c代码举例(假设long为64bits):

 long a = x;
 assert(a == (long)(double)a);
Copy after login

请问, a的取值在什么范围内的时候, 上面的代码可以断言成功?(留在文章最后解答)

现在我们回归正题, PHP在执行一个脚本之前, 首先需要读入脚本, 分析脚本, 这个过程中也包含着, 对脚本中的字面量进行zval化, 比如对于如下脚本:

<&#63;php
$a = 9223372036854775807; //64位有符号数最大值
$b = 9223372036854775808; //最大值+1
var_dump($a);
var_dump($b);
Copy after login

输出:

int(9223372036854775807)
float(9.22337203685E+18)
Copy after login

也就说, PHP在词法分析阶段, 对于一个字面量的数值, 会去判断, 是否超出了当前系统的long的表值范围, 如果不是, 则用lval来保存, zval为IS_LONG, 否则就用dval表示, zval IS_FLOAT.

凡是大于最大的整数值的数值, 我们都要小心, 因为它可能会有精度损失:

<&#63;php
$a = 9223372036854775807;
$b = 9223372036854775808;
 
var_dump($a === ($b - 1));
Copy after login

输出是false.

现在接上开头的讨论, 之前说过, PHP的整数, 可能是32位, 也可能是64位, 那么就决定了, 一些在64位上可以运行正常的代码, 可能会因为隐形的类型转换, 发生精度丢失, 从而造成代码不能正常的运行在32位系统上.

所以, 我们一定要警惕这个临界值, 好在PHP中已经定义了这个临界值:

<&#63;php
 echo PHP_INT_MAX;
 &#63;>
Copy after login

当然, 为了保险起见, 我们应该使用字符串来保存大整数, 并且采用比如bcmath这样的数学函数库来进行计算.

另外, 还有一个关键的配置, 会让我们产生迷惑, 这个配置就是php.precision, 这配置决定了PHP再输出一个float值的时候, 输出多少有效位.

最后, 我们再来回头看上面提出的问题, 也就是一个long的整数, 最大的值是多少, 才能保证转到float以后再转回long不会发生精度丢失?

比如, 对于整数, 我们知道它的二进制表示是, 101, 现在, 让我们右移俩位, 变成1.01, 舍去高位的隐含有效位1, 我们得到在double中存储5的二进制数值为:

0/*符号位*/ 10000000001/*指数位*/ 0100000000000000000000000000000000000000000000000000
5的二进制表示, 丝毫未损的保存在了尾数部分, 这个情况下, 从double转会回long, 不会发生精度丢失.

我们知道double用52位表示尾数, 算上隐含的首位1, 一共是53位精度.. 那么也就可以得出, 如果一个long的整数, 值小于:

2^53 - 1 == 9007199254740991; //牢记, 我们现在假设是64bits的long
那么, 这个整数, 在发生long->double->long的数值转换时, 不会发生精度丢失.

关于浮点数,还有一点,就是对于如下的这个常见问题的回答:

<&#63;php
 $f = 0.58;
 var_dump(intval($f * 100)); //为啥输出57
&#63;>
Copy after login

为啥输出是57啊? PHP的bug么?

我相信有很多的同学有过这样的疑问, 因为光问我类似问题的人就很多, 更不用说bugs.php.net上经常有人问…

要搞明白这个原因, 首先我们要知道浮点数的表示(IEEE 754):

浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).

符号位:最高位表示数据的正负,0表示正数,1表示负数。

指数位:表示数据以2为底的幂,指数采用偏移码表示

尾数:表示数据小数点后的有效数字.

这里的关键点就在于, 小数在二进制的表示, 关于小数如何用二进制表示, 大家可以百度一下, 我这里就不再赘述, 我们关键的要了解, 0.58 对于二进制表示来说, 是无限长的值(下面的数字省掉了隐含的1)..

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111
0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101

而两者的二进制, 如果只是通过这52位计算的话,分别是:

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995

至于0.58 * 100的具体浮点数乘法, 我们不考虑那么细, 有兴趣的可以看(Floating point), 我们就模糊的以心算来看… 0.58 * 100 = 57.999999999

那你intval一下, 自然就是57了….

可见, 这个问题的关键点就是: “你看似有穷的小数, 在计算机的二进制表示里却是无穷的”

so, 不要再以为这是PHP的bug了, 这就是这样的…..

您可能感兴趣的文章:

  • PHP数据类型之整数类型、浮点数的介绍
  • php的sprintf函数的用法 控制浮点数格式
  • PHP中浮点数计算比较及取整不准确的解决方法
  • php判断两个浮点数是否相等的方法
  • PHP浮点数精度问题汇总
  • 你应该知道PHP浮点数知识
  • PHP中两个float(浮点数)比较实例分析
  • 简单谈谈php浮点数精确运算
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to convert string to decimal in php How to convert string to decimal in php Mar 22, 2023 pm 03:22 PM

PHP is a powerful programming language that is widely used in the field of web development. One of the very common situations is the need to convert a string to a decimal. This is very useful when doing data processing. In this article, we will explain how to convert string to decimal in PHP.

PHP floating point number rounding method PHP floating point number rounding method Mar 21, 2024 am 09:21 AM

This article will explain in detail the PHP floating point number rounding method. The editor thinks it is very practical, so I share it with you as a reference. I hope you can gain something after reading this article. PHP Floating Point Rounding Overview Floating point numbers are represented in computers as a decimal point followed by an exponent, however, they are often stored in approximations with a limited number of digits. When you need to round a floating point number to a specific precision, there are several ways to do it. Method 1. round() function The round() function rounds a floating point number to the nearest integer. It accepts floating point numbers and optional precision parameters. For example: $num=1.55;echoround($num);//Output: 2echoround($num,1)

Convert floating point number to string using strconv.FormatFloat function Convert floating point number to string using strconv.FormatFloat function Jul 25, 2023 am 11:45 AM

Use the strconv.FormatFloat function to convert floating point numbers into strings. In the Go language, we often need to convert floating point numbers into string types for output or storage needs. The strconv package is provided in the Go language, and the FormatFloat function in it can convert floating point numbers into string types. The FormatFloat function takes three parameters: f represents the floating point number to be converted, fmt represents the format, and prec represents the number of decimal places to retain. Among them, the f parameter

Causes and avoidance strategies of PHP floating point calculation errors Causes and avoidance strategies of PHP floating point calculation errors Feb 27, 2024 pm 06:33 PM

As a popular server-side scripting language, PHP often encounters problems of loss of precision or calculation errors when performing floating-point calculations. These problems may affect the accuracy and stability of the program. This article will explore the causes of PHP floating point calculation errors, propose some avoidance strategies, and give specific code examples for reference. 1. Reasons for PHP floating-point calculation errors. In computers, floating-point numbers are represented in binary form, and binary cannot accurately represent all decimal decimals, which leads to the inaccuracy of floating-point numbers.

An in-depth explanation of PHP BCMath: Unleashing the potential of number operations An in-depth explanation of PHP BCMath: Unleashing the potential of number operations Feb 23, 2024 am 09:10 AM

:1. Introduction to BCMath BCMath is an extension library built into PHP, which is specially used to handle large integer and floating point number operations. It provides a wealth of functions to perform various mathematical operations such as addition, subtraction, multiplication, division, square, and square root, and supports digital representation in multiple bases. 2. Advantages of BCMath Compared with the arithmetic operators and functions natively provided by PHP, BCMath mainly has the following advantages: Higher precision: BCMath’s operation results can retain more significant digits, which is useful for calculations involving large numbers. scenes are particularly important. Wider range: BCMath can handle larger numbers than PHP's native data types, thus avoiding overflow or loss of precision issues. Richer features: BCMath provides

How to convert string to float in PHP How to convert string to float in PHP Mar 27, 2024 pm 12:48 PM

Converting a string to a floating point number is a common operation in PHP and can be accomplished through built-in methods. First make sure that the string is in a legal floating point format before it can be successfully converted to a floating point number. The following will detail how to convert a string to a floating point number in PHP and provide specific code examples. 1. Use (float) cast In PHP, the simplest way to convert a string into a floating point number is to use cast. The way to force conversion is to add (float) before the string, and PHP will automatically convert it

Round floating point numbers using Math.Round function in C# Round floating point numbers using Math.Round function in C# Nov 18, 2023 pm 02:17 PM

Using the Math.Round function in C# to round floating-point numbers requires specific code examples. In the C# programming language, sometimes we need to round floating-point numbers. At this time, we can use the Math.Round function to achieve this function. The Math.Round function is a built-in function in C# used for mathematical calculations. Its main function is to round the specified floating point number. The following is the common format of the Math.Round function: Math.Round(doub

Display scientific notation as floating point number in Python Display scientific notation as floating point number in Python Sep 04, 2023 pm 08:29 PM

Scientific notation is the standard way of representing numbers in science and mathematics. However, in some cases it may be more convenient to display these numbers in traditional decimal format (also known as floating point format). Python provides several methods for converting scientific notation to floating point format. Floating point representation of scientific notation in Python One way to display scientific notation as floating point numbers in Python is to use the float() function. The float() function accepts a string as input and returns a floating point number. To convert a number in scientific notation to floating point using the float() function, you simply pass the number as a string to the function. The different methods used for scientific notation in Python are- Float method Format method

See all articles