PHP 舍入浮点数

问题描述 投票:0回答:5

我正在开发一个系统,我需要四舍五入到最接近的便士财务付款。我天真地以为我会乘以 100,发言,然后再除以。然而下面的例子是错误的:

echo 1298.34*100;

正确显示:

129834

但是

echo floor(1298.34*100);

意外地显示:

129833

例如,我使用

intval
也遇到同样的问题。

我怀疑乘法与浮点舍入相冲突。但如果我不能依赖乘法,我该怎么做呢?我总是想可靠地向下舍入,并且不需要考虑负数。

要明确的是,我希望去掉任何零碎的便士金额:

1298.345 should give 1298.34
1298.349 should give 1298.34
1298.342 should give 1298.34
php precision
5个回答
1
投票

由于您提到您仅将其用于显示目的,因此您可以获取金额,将其转换为字符串并截断第二位小数之后的任何内容。正则表达式可以完成这项工作:

preg_match('/\d+\.{0,1}\d{0,2}/', (string) $amount, $matches);

此表达式适用于任意位数的小数(包括零)。详细工作原理:

  • \d+
    匹配任意数量的数字
  • \.{0,1}
    匹配 0 或 1 个文字点
  • \d{0,2}
    匹配点后零个或两位数字

您可以运行以下代码来测试它:

$amounts = [
    1298,
    1298.3,
    1298.34,
    1298.341,
    1298.349279745,
];
foreach ($amounts as $amount) {
    preg_match('/\d+\.{0,1}\d{0,2}/', (string) $amount, $matches);
    var_dump($matches[0]);
}

也可以在this fiddle中进行现场测试。


1
投票

您可以使用

round()
舍入到所需的精度,并在舍入最后 5 时采用预期行为(这是您可能遇到的另一个财务障碍)。

  $display = round(3895.0 / 3.0, 2);

另外,提醒一下,我有总是用最后一个点或“.0”来书写浮点整数的习惯。这可以防止某些语言推断错误的类型并进行整数除法,这样 5 / 3 将产生 1

如果您需要“自定义舍入”并想确定,那么它不起作用的原因是因为并非所有浮点数都存在于机器表示中。 1298.34不存在;确实存在的(我正在编出精确的数字!)代替它的位置可能是 1298.33999999999999124。

因此,当您将其乘以 100 并得到 129833.999999999999124 时,

当然 截断它会得到 129833。

然后您需要做的是添加少量数量,该数量必须“足以覆盖机器错误”,但“不足以在财务计算中产生影响”。有一种算法可以确定这个数量,但您可能可以得到“放大后的千分之一”。

所以: $display = floor((3895.0 / 3.0)*100.0 + 0.001);

请注意,

这个

数字,您将“看到”为1234.56,
可能又不准确地存在
。它实际上可能是 1234.5600000000000123 或 1234.559999999999876。这可能会对复杂的复合计算产生影响。

由于您正在从事金融工作,因此您应该使用某种 Money 库(https://github.com/moneyphp/money

)。几乎所有其他解决方案都是自找麻烦。

0
投票

我不推荐的其他方法是:a)仅使用整数,b)使用 bcmath 进行计算或 c)使用 Money 库中的 Number 类,例如:


function getMoneyValue($value): string { if (!is_numeric($value)) { throw new \RuntimeException(sprintf('Money value has to be a numeric value, "%s" given', is_object($value) ? get_class($value) : gettype($value))); } $number = \Money\Number::fromNumber($value)->base10(-2); return $number->getIntegerPart(); }


首先您需要阅读 PHP 官方页面上的红色部分
https://www.php.net/manual/en/language.types.float.php

0
投票
浮点运算并不精确,需要使用特殊函数

https://www.php.net/manual/en/ref.bc.php

为什么?因为

var_dump(1298.34*100);

//输出浮点数(129833.99999999999)


如何用官方数学函数解决这个问题? 
bcmul(1298.34, 100);

//输出“129834”


如果您想设置小数精度,请执行以下操作:
bcmul(1298.34, 100, 2);

//输出“129834.00”


最后你可以将其投射到浮动
$result = (float)bcmul(1298.34, 100, 2);

如果您想向上或向下舍入您的$结果,请使用:

round($result,2) //round UP to 2 decimals round($result,2,PHP_ROUND_HALF_DOWN) //round DOWN to 2 decimals
    

另一个可用的函数是 round(),它有两个参数 -
要舍入的数字以及要舍入的小数位数。如果
一个数字正好是两个整数之间的一半,round() 总是
向上舍入。

-1
投票
使用圆形:

echo round (1298.34*100);

结果:

129834
    

© www.soinside.com 2019 - 2024. All rights reserved.