6

可能重复:
为什么十进制数不能用二进制精确表示?
浮动值的问题

$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 和 > 代替 == 就可以了。粗糙而不是世界上最好的代码,但它确实解决了最初的问题。感谢大家的其他回复,我将阅读并跟进以改进我的编码。

再次,非常感谢。

4

7 回答 7

5

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

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

于 2012-07-06T17:03:53.920 回答
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/>';
}
于 2012-07-06T17:12:12.793 回答
3

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

您需要发明自己的以 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;
  }
}
于 2012-07-06T17:15:31.887 回答
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;
  }
}
于 2012-07-06T17:27:12.247 回答
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');
}

这将输出正确的结果

于 2012-07-06T17:19:16.743 回答
0

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

于 2012-07-06T17:05:09.977 回答
-1

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

例如,您从 1.1 开始

为什么不:

for ( $i=1; $i<4; $i++ ) {
  for ( $j=0; $j<5; $j++ ) {
    $v = $i + ($j/10);
    # ...
  }
}
于 2012-07-06T17:08:45.067 回答