基本数学的PHP浮点错误

时间:2012-07-06 16:57:05

标签: php math

  

可能重复:
  Why can't decimal numbers be represented exactly in binary?
  problem with floating values

$var1 = 1;

for ( $i=0; $i<30; $i++ ) {
  $var1 += 0.1;
  $var2 = floor($var1);
  $var3 = $var1-$var2;
  if ( $var3 == 0.5 ) {
    $var1 = $var2+1;
  }
}

这个循环的意图是计算1.0,1.1,1.2,1.3,1.4,然后跳到2.0,2.1,2.2等

我遇到的问题是if语句永远不会成立。此外,每十分之一的计算都会解决一些疯狂的科学答案。

我该如何解决这个问题?请帮忙!

编辑:我写了一个令人沮丧的匆忙的问题,这不止一个,我现在看到了。

问题的第一部分真的是“我怎样才能通过这个浮动点查询来完成这项工作”和“为什么这个问题甚至会发生!”

感谢所有好的回复,我将答案正确,这很容易回答“如何使这项工作”的核心问题。

使用0.49而不是0.5和&gt;而不是==做到了。原油而不是世界上最好的代码,但确实解决了原始问题。感谢大家,我将阅读其他回复并跟进以改进我的编码。

再一次,非常感谢。

7 个答案:

答案 0 :(得分:5)

计算机无法准确表示浮点数,因为它们存储在二进制文件(基数2)中。所有浮标都会有一点点调整+/-。

见这里:http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

答案 1 :(得分:5)

这个给出了预期的结果:

$var1 = 1;
for ( $i=0; $i<30; $i++ ) {
    $var1 += 0.1;
    $var2 = (int)($var1);
    $var3 = $var1-$var2;
    if ( $var3 >= 0.45 ) {
        $var1 = $var2+1;
    }
    echo $var1.' '.$var2.' '.$var3.'<br/>';
}

答案 2 :(得分:3)

二进制浮点数将选择近似值来表示它不能代表的基数10浮点数。因此,将float与float进行比较是不准确的(因为您无法确定其行为)。

您需要创建自己的base-10浮点数。如果您知道自己想要哪种精确度,就不难发明。在您的情况下,您只需要一个精确的数字。我们的基数为10的整数的公式为:value / 10表示精确位数的幂,即1,因此您的公式为= value / 10

执行算术运算与执行常规算术一样简单。只需将正常表示(指计算机使用的表示)转换为我们发明的表示。在您的情况下,$var1 += 0.1会转向$var1 += 1

输出就像将表示转换为正常表示一样简单。在您的情况下,echo $var1 . '<br>';会转向echo $var1 / 10 . '<br>';

$var1 = 10;

for ( $i=0; $i<30; $i++ ) {
  echo $var1 / 10 . '<br>';
  $var1 += 1;
  if ($var1 % 10 == 5) {
    $var1 = $var1+5;
  }
}

答案 3 :(得分:2)

关于为什么会发生这种情况以及许多解决方案的大量信息。 PHP的基本设计目标之一是它的松散类型设计和隐式转换。本着这种精神,我认为解决这个问题的最简单方法之一是使用字符串比较。在你的情况下,这是一个很好的解决方案:

<?php
$var1 = 1;

for ( $i=0; $i<30; $i++ ) {
  $var1 += 0.1;
  $var2 = floor($var1);
  $var3 = $var1 - $var2;
  echo "$var1 | $var2 | $var3 \n";
  if (number_format($var3, 1) == '0.5') {
    echo "It's true\n";
    $var1 = $var2 + 1;
  }
}

答案 4 :(得分:1)

正确的解决方案是使用bcmath包:

$var1 = '1.0'; bcscale(1);

for ( $i = 0; $i < 30; $i++) {
    $var2 = $var1;
    for( $j = 0; $j < 5; $j++) {
        echo $var2 . "\n"; 
        $var2 = bcadd( $var2, '0.1');
    }
    $var1 = bcadd( $var1, '1');
}

outputs the correct result

答案 5 :(得分:0)

if语句可能永远不会仅仅由于浮点运算而执行 - 测试与浮点值的严格相等总是会带来这种风险。为什么不更改条件以检查变量是否位于以0.5为中心的小间隔内,以确保您捕获它???

答案 6 :(得分:-1)

保持简单,愚蠢并使用整数运算。

例如,您从1.1

开始

为什么不:

for ( $i=1; $i<4; $i++ ) {
  for ( $j=0; $j<5; $j++ ) {
    $v = $i + ($j/10);
    # ...
  }
}