0

I stumbled on the difference between the result of Machine epsilon calculation. When compared to 0 PHP yields 4.9406564584125E-324. While for 1 it pops up with 1.1102230246252E-16.

Quite a difference. Guess it's something with the type of data initially set by default in PHP.

The code is:

<?php
//Machine epsilon calculation

$e = 1;
$eTmp = null;

for ($i = 0; 0 != 0 + $e; $i++){ //Changing 0 by 1 produces absolutely different result
     $e = $e/2;
    if ($e != 0) {$eTmp = $e;}
}

echo $eTmp;
//var_dump($eTmp);

?>

Any clarification on the difference between the two? And how can a variable or value by assigned manually to float in PHP? Many thanks for your ideas!

Charles
  • 947
  • 1
  • 15
  • 39
Sam
  • 41
  • 1
  • Please see [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) because you are trying to do floating point calculations using native constructs, which is bad. Look into the [bcmath](http://php.net/manual/en/book.bc.php) library. – MonkeyZeus Feb 16 '18 at 16:19
  • @MonkeyZeus: There is nothing wrong with the question or examining or using floating point mathematics in “native constructs.” – Eric Postpischil Feb 16 '18 at 16:26

1 Answers1

1

Your PHP implementation appear to be using the common IEEE 754 64-bit binary format. In this format, finite values are represented, effectively, as a sign, an integer M less than 253, and an integer exponent e between −1074 and 971, inclusive. The value represented is +M•2e or −M•2e, according to the sign. (This format will often be described with M being a fraction between 1 and 2 with a certain number of bits after the radix point. The descriptions are mathematically equivalent, just useful for different purposes.)

Given this, we can easily see that the next representable number after 0 is +1•2−1074, which is approximately 4.94065645841246544•10−324.

In this format as stated, 1 can be represented as +1•20. However, to see what the smallest change that can be made to 1 is, we must normalize it by making M as large as possible. 1 can also be represented with M = 252 and e = −52, resulting in +252•2−52. In this form, we can see the next representable value greater than 1 is achieved by adding 1 to M, mkaing the number +(252+1)•2−52.

The difference between 1 and +(252+1)•2−52 is of course 1•2−52, which is exactly 2.220446049250313080847263336181640625•10−16.

Note that your code records $eTmp (if $e is not zero) after reducing $e with $e = $e/2, which means it reports the first value of $e that does not cause a change. Thus it reports 1.11e-16 rather than 2.22e-16, which is the last value that does cause a change to 1.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • That decimal value for `2^-1074` deserves an "approximately". I'm shocked that you didn't give all 751 digits of the decimal value. (Well, okay, not that shocked.) – Mark Dickinson Feb 16 '18 at 16:32
  • @MarkDickinson: Thanks, fixed. I am sure I have given all the digits in some answer somewhere. :-) – Eric Postpischil Feb 16 '18 at 16:45
  • Eric, thank you so much for your answer. once you stated that, a value can be represented like +M*2^(e) or -M*2^e, where e=-1074...971, integer. still vague why in this case we can't represent 1 as +2^(971)*2^(-971)? – Sam Feb 16 '18 at 16:56
  • @Sam: The integer *M* must be less than 2^53 in magnitude. 2^971 is too large. (The format for 64-bit floating-point has 52 bits for *M*, but the first bit is 1 for normalized numbers, so it does not need to be stored explicitly. So *M* is represented by 53 bits, so it must be less than 2^53.) – Eric Postpischil Feb 16 '18 at 17:11
  • @Eric Postpischil: More or less clear so far. Thank you again for your time devoted and explanations given. – Sam Feb 16 '18 at 19:20