Strict Comparison in PHP Explained at the Zend Engine Level

BackerLeader 2 29 51
calendar_todayschedule3 min read
— Originally published at dev.to

While browsing [bugs.php.net][1], I found an [interesting ticket][2]. Someone reported a surprising behavior, and I wanted to understand what is happening under the hood—because I think this is a really important concept to understand in PHP internals.

Consider the following script and its output:

<?php

$NaN = sqrt(-1);
$array = [$NaN];

var_dump($array === $array);   // always true
var_dump([$NaN] === [$NaN]);   // always false

At first glance, this looks confusing. Let’s investigate it line by line.

Generating NaN

The first line contains a mathematical expression that is not defined in the real numbers:

$NaN = sqrt(-1);

PHP delegates this operation to its internal math implementation in [ext/standard/math.c][3], which ultimately relies on the system’s math library (for example, glibc on Linux, Apple libc on macOS, or musl on Alpine). These libraries follow the [IEEE 754 floating-point standard][4]. According to this standard, operations that are mathematically undefined (such as the square root of a negative number) produce NaN (“Not a Number”). Importantly, NaN is never equal to anything, including itself. This is a fundamental rule of IEEE 754 floating-point arithmetic.

Putting NaN into an array

Next, we store this NaN value in an array:

$array = [$NaN];

Comparing the same array

Now we compare the array to itself:

var_dump($array === $array);

In PHP, the === operator performs a strict comparison, meaning:
same type same value Here, both operands refer to the same variable, so the comparison returns true. This is effectively an identity shortcut. Internally, both $array operands **point to the same zval**. A short note about zvals ------------------------ A **zval** (Zend value) is the fundamental data structure used by the [Zend Engine][5] to represent all PHP variables. Every variable in PHP is stored internally as a zval. PHP 7 redesigned the zval structure to be significantly more memory-efficient and faster compared to PHP 5. A zval is essentially a container that holds: - the value - the type information - flags related to references and garbage collection PHP uses a **copy-on-write** (CoW) mechanism for efficiency. Multiple variables can point to the same underlying value without duplicating memory. Only when a modification occurs does PHP create a copy, ensuring changes don’t affect other variables. Comparing two distinct arrays ----------------------------- Finally, let’s look at this line: ``` var_dump([$NaN] === [$NaN]);


Here, we are comparing two distinct arrays, even though their contents look identical. To understand why this returns false, let’s briefly follow the [relevant internal comparison logic][6] in PHP:

zend_is_identical(op1, op2), (both operands are arrays):

- zend_compare_arrays()
- zend_compare_symbol_tables()
- zend_hash_compare()
- hash_zval_identical_function()
- zend_is_identical(v1, v2)

During this process, PHP compares each element of the arrays using **strict identity comparison**. Eventually, the comparison reaches the array elements themselves: NaN === NaN
According to the IEEE 754 standard, NaN is never equal to itself, so this comparison returns false. Because the first (and only) elements of the arrays are not identical, **the entire array comparison fails**.

Conclusion
----------

This behavior is not a bug in PHP, but a direct consequence of how floating-point numbers and strict comparisons are defined and implemented.

This example highlights why understanding PHP internals—such as zvals, copy-on-write, and strict comparison semantics—is crucial when working with edge cases involving floating-point numbers. What may initially look like inconsistent behavior is actually a predictable and well-defined result of PHP’s internal design.

  [1]: https://bugs.php.net/
  [2]: https://bugs.php.net/bug.php?id=80701
  [3]: https://github.com/php/php-src/blob/master/ext/standard/math.c
  [4]: https://ethw.org/Milestones:IEEE_Standard_754_for_Binary_Floating-Point_Arithmetic,_1985
  [5]: https://www.phpinternalsbook.com/php7/zend_engine.html
  [6]: https://github.com/php/php-src/blob/master/Zend/zend_operators.c
7.6k Points82 Badges2 29 51
Hungary, Godolloen.dobrenteiistvan.hu
15Posts
53Comments
139Followers
37Connections
I am a Zend-certified full-stack (PHP) web developer.
Build your own developer journey
Track progress. Share learning. Stay consistent.

1 Comment

2 votes
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Comparison: Universal Import vs. Plaid/Yodlee

Pocket Portfolio - Mar 12

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelskiverified - Apr 23

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

Just completed another large-scale WordPress migration — and the client left this

saqib_devmorph - Apr 7
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

4 comments
3 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!