浮点数乘法和计算问题

5

这个问题最好用代码来表达:

$var1 = 286.46; // user input data
$var2 = 3646; // user input data
$var3 = 25000; // minumum amount allowed
$var4 = ($var1 * 100) - $var2; // = 250000
if ($var4 < $var3) { // if 250000 < 250000
    print 'This returns!';
}

var_dump($var4) 的输出结果是:float(25000),当强制转换为整型时,输出结果为:int(24999) - 这就是问题所在。

不过我不知道该怎么解决。这个问题出现在乘以100的时候,虽然我可以使用一些小技巧来绕过它(例如*10*10),但我想知道是否有一个“真正”的解决方案。

谢谢 :)


推荐阅读:http://floating-point-gui.de/ - Michael Borgwardt
5个回答

1

这是一个可怕的hacky解决方案,我有点讨厌自己这样做,但这确实可以得到预期的行为:

<?php

  $var1 = 286.46; // user input data
  $var2 = 3646; // user input data
  $var3 = 25000; // minumum amount allowed
  $var4 = ($var1 * 100) - $var2; // = 250000
  if ((string) $var4 < (string) $var3) { // if 250000 < 250000
      print 'This returns!';
  }

将它们转换为字符串,然后根据比较的情况将它们转换回int/float。我不喜欢这样做,但它确实有效。

如果您需要在PHP中进行精确的浮点数计算,真正需要的是BC Math


1

当使用浮点数作为整数时,始终使用 ceil(或 floor,根据您想要的)是一个好主意。在您的情况下,在比较之前尝试使用 ceil($var4)!


0

我觉得你可以使用bccomp来比较浮点数值,但是我认为这个函数不在PHP核心里。

另外,我在这里找到了一个函数这里,但是我无法测试它是否有效。

function Comp($Num1,$Num2,$Scale=null) {
  // check if they're valid positive numbers, extract the whole numbers and decimals
  if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)||
     !preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0');

  // remove leading zeroes from whole numbers
  $Num1=ltrim($Tmp1[1],'0');
  $Num2=ltrim($Tmp2[1],'0');

  // first, we can just check the lengths of the numbers, this can help save processing time
  // if $Num1 is longer than $Num2, return 1.. vice versa with the next step.
  if(strlen($Num1)>strlen($Num2)) return(1);
  else {
    if(strlen($Num1)<strlen($Num2)) return(-1);

    // if the two numbers are of equal length, we check digit-by-digit
    else {

      // remove ending zeroes from decimals and remove point
      $Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):'';
      $Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):'';

      // if the user defined $Scale, then make sure we use that only
      if($Scale!=null) {
        $Dec1=substr($Dec1,0,$Scale);
        $Dec2=substr($Dec2,0,$Scale);
      }

      // calculate the longest length of decimals
      $DLen=max(strlen($Dec1),strlen($Dec2));

      // append the padded decimals onto the end of the whole numbers
      $Num1.=str_pad($Dec1,$DLen,'0');
      $Num2.=str_pad($Dec2,$DLen,'0');

      // check digit-by-digit, if they have a difference, return 1 or -1 (greater/lower than)
      for($i=0;$i<strlen($Num1);$i++) {
        if((int)$Num1{$i}>(int)$Num2{$i}) return(1);
        else
          if((int)$Num1{$i}<(int)$Num2{$i}) return(-1);
      }

      // if the two numbers have no difference (they're the same).. return 0
      return(0);
    }
  }
}

0

这就是浮点数有时会出现的情况,这是由于浮点数有时无法精确表示整数。

与其将其转换为int,不如将数字四舍五入到整数值,然后再将其转换为int。(可能这个转换是不必要的,但PHP对这种内部发生的事情并不清楚,即使您现在知道它们如何发生,它们未来也可能不会发生。)


0
问题在于浮点数无法表示某些数字。由于PHP没有“十进制”(或其他固定点)类型,因此您基本上只能通过各种方式解决这些问题。
假设您示例中的第一个数字$var1 = 286.46表示某种货币,您可以在用户输入后直接将其转换为分(例如通过去掉小数点并将其读取为整数),从而使用整数计算所有内容。
这不是通用解决方案 - 我怀疑是否存在这样的解决方案(除非使用任意精度数字,一些PHP扩展提供此功能 - 但我认为这太过复杂了)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接