Why cannot compare double values directly for equality?

Summary

Directly comparing floating-point values for equality often yields unexpected results, even when debugging tools show them as identical. This behavior arises because floating-point numbers, typically stored using the IEEE 754 standard for double precision, cannot precisely represent many decimal fractions in binary format, leading to approximations. Consequently, a direct equality check will fail if these underlying binary approximations differ, however slightly. A reliable method for comparison involves checking if the absolute difference between the two floating-point numbers is less than a small, predefined epsilon value. This principle applies broadly across programming languages, not just PHP.

A question in PHP: Get some value from the database and use floatval() to convert it to floatint point value, then compare it with another value which is also converted to floating point value with floatval(). These two values are the same when using var_dump() to output, the two outputs are float(8.87), they are the same. But when comparing them for equality, the result is weird., they are not equal. Why?

To analyze this question, we need to start with PHP data types. PHP uses weak types. In PHP, floating point values are stored using C's double precision type, so we need to understand how double precision values are stored in memory in C. Floating-point value storage is represented conforming to the IEEE 754 standard (V = (-1)s x M x 2e ).
  • sign : s determines the negativeness or positiveness of the number
  • significant : M represents the precision bits of the number
  • exponent : e is the exponential

The bits to represent a floating-point number are divided into 3 sections.

  • 1 bit for the s
  • k bits for the e
  • n bits for the M

For double precision value in C, s, e and M are 1 bit, 11 bits and 52 bits respectively, in total it's 64 bits( 8 bytes).


After we know how double precision values are stored in memory, we need to check how M is to store the fraction with binary bits.

For a decimal number d, it can be expressed as


while a decimal number b expressed in binary is:

Assume we just consider limited length coding (32 bits or 64 bits), then in decimal format, we cannot accurately represent 1/3 or 3/5. Similarly, only those can be written as x*2y format can be expressed accurately in binary format. Other values can only be represented approximately.

Since double values can only be stored approximately, it's unsafe to directly compare two double values for equality.

For example.

<?php
  $a = 2.2;
  $d = 0.3 + 1.9;
  var_dump($a);
  var_dump($d);
  //Wrong, double values cannot be compared directly
  var_dump($a == $d);
  //The proper way to compare two double values
  //0.000001 is a threshold value
  var_dump(abs($a - $d) < 0.000001);
?>

The output

float(2.2)
float(2.2)
bool(false)
bool(true)

For other languages, the same problem for representing floating-point number exists. So never trust floating numbers, do not compare floating point numbers directly for equality.

Reference : http://sunkid.me/blog/2012/06/26/why-can-not-compare-the-double/

PHP FLOATING PRECISION COMPARE EQUALITY

  RELATED

  COMMENTS

0

No comment for this article.