使用 PHP 截断浮点数
当一个浮点数需要截断为浮点后的某个数字时,事实证明这并不容易做到。例如,如果必须截断到点后第二位数字,则数字应为
45.8976 => 45.89, 0.0185 => 0.01
(点后第二位数字不会根据点后第三位数字进行舍入)。
像 round()
、number_format()
、sprintf()
这样的函数对数字进行四舍五入并打印出来
45.8976 => 45.90, 0.0185 => 0.02
我遇到了两种解决方案,我想知道是否它们都足够好,哪一个更适合使用
1.
function truncNumber( $number, $prec = 2 )
{
return bccomp( $number, 0, 10 ) == 0 ? $number : round( $number - pow( 0.1, bcadd( $prec, 1 ) ) * 5, $prec );
}
2.
function truncNumber($number, $prec = 2 )
{
return sprintf( "%.".$prec."f", floor( $number*pow( 10, $prec ) )/pow( 10, $prec ) );
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
要准确地对 +ve 和 -ve 数字执行此操作,您需要使用:
- 用于 +ve 数字的 php
floor()
函数- php
ceil()
函数用于 -ve 数字,其原因是
floor()
总是将数字向下< /strong>,不为零。即
floor()
有效地将-ve数字朝更大的绝对值舍入例如
floor(1.5) = 1
whilefloor(-1.5) = 2
因此,对于使用
乘以幂、舍入、然后除以幂的截断方法
代码>:-
floor()
仅适用于正数-
ceil()
仅适用于负数要测试这一点,请将以下代码复制到 http 的编辑器中://phpfiddle.org/lite (或类似网站):
To do this accurately for both +ve and -ve numbers you need use:
- the php
floor()
function for +ve numbers- the php
ceil()
function for -ve numbersthe reason for this is that
floor()
always rounds the number down, not towards zero.ie
floor()
effectively rounds -ve numbers towards a larger absolute valueeg
floor(1.5) = 1
whilefloor(-1.5) = 2
Therefore for the truncate method using
multiply by power, round, then divide by power
:-
floor()
only works for positive numbers-
ceil()
only works for negative numbersTo test this, copy the following code into the editor of http://phpfiddle.org/lite (or similar):
我看到另一种方法来执行此操作:
但它并不比任何其他方法更好...
round
也可以替换为sprintf
。I'm seeing another method to perform this:
But it's not better than any other...
round
can be replaced withsprintf
too.round() 函数确实有一个精度参数以及用于指定舍入方法的第三个参数,但你是对的,它不进行截断。
您正在寻找的是
floor()
和ceil()
函数。缺点是它们没有精度参数,所以你必须这样做:The
round()
function does have a precision paramter as well as a third parameter for specifying the rounding method, but you're right, it doesn't do truncating.What you're looking for are the
floor()
andceil()
functions. The downside is that they don't have a precision parameter, so you'll have to do something like this:其原因是浮点数的内部二进制表示形式。上面的给定值
10.04
必须在内部表示为以 2 为底的幂。浮点部分的潜在指数为
ln(0,04)/ln(2) = -4,64...
。这已经表明10.04
不能精确地表示为二进制数。我们最终得到类似10.04 ~ 2^3 + 2^1 + 2^-5 + 2^-7 + 2^-11 + ...
的浮点数最大精度点部分。这就是
10.04
内部实际上要少一些的原因,可能会表示为10.039...
。要解决此问题,有两种可能性 - 直接使用字符串操作或使用任意精度库,例如 PHP 的
BcMath
。将生成上述代码的输出(为了便于阅读,我添加了分隔换行符):
The reason for this is the internal binary representation of floating-point numbers. The given value from above
10.04
has to represented internally as a power of the base 2.The potential exponent for the floating-point part is
ln(0,04)/ln(2) = -4,64...
. This already shows that10.04
cannot be represented exactly as a binary number. We end up having something like10.04 ~ 2^3 + 2^1 + 2^-5 + 2^-7 + 2^-11 + ...
with having a maximum precision for the floating-point part.This is the reason that
10.04
is internally actually a bit less and might be represented as10.039...
.To work around this behavior there are two possibilities - either fiddle around directly with string operations or use an arbitrary precision library like
BcMath
for PHP.The output for the code above will generate (I added separating newlines for readability):
我想用我满足我的需求的函数来添加我对这个答案的贡献,该函数考虑了负值和正值的截断位置。
...我认为它使用起来简单快捷。
I would like to add my contribute to this answer with the function I use for my needs, that considers truncate positions both for negative and positive values.
...I think it's simple and fast to use.
你可以试试这个:
You can try this:
要解决下限为正数和ceil为负数的问题,您只需将数字除以一并使用
intdiv()
。而浮动问题可以通过使用round()
乘法之后。所以,这应该有效:
Repl.it
输出:
To overcome the problem with floor for positives and ceil for negatives, you can just divide the number by one and get the integer part using
intdiv()
. And the float problem can be solved by usinground()
after the multiplication.So, this should work:
Repl.it
Outputs:
我用这个:
输出是0.66
i use this:
the output is 0.66
floor
会按照你的要求做。您不会找到更直接的函数来实现此目的,因为这是一种奇怪的问题。通常你会在数学上正确舍入。出于好奇 - 你需要这个做什么?
floor
will do as you ask.You won't find a more direct function for this, since it's a kind of odd thing to ask. Normally you'll round mathematically correct. Out of curiosity - What do you need this for?
这是我的解决方案:
this is my solution:
为什么 PHP 不按照我们期望的方式处理数学公式,例如
floor(10.04 * 100) = 1003
,而我们都知道它应该是1004
。此问题不仅存在于 PHP 中,还存在于所有语言中,具体取决于所使用语言的相对错误。
PHP 使用 IEEE 754 双精度格式,相对误差约为 1.11e-16。 (resource)
真正的问题是floor函数转换了float值转换为 int 值,例如
(int)(10.04 * 100) = 1003
正如我们之前在 Floor 函数中看到的那样。 (资源)因此,为了解决这个问题,我们可以将float转换为字符串,字符串可以准确地表示任何值,那么floor函数会将字符串准确地转换为int。
Why does PHP not handle mathamatic formulas the way we would expect, e.g.
floor(10.04 * 100) = 1003
when we all know it should be1004
.This issue is not just in PHP but can be found in all langauges, depending on the relative error in the langauge used.
PHP uses IEEE 754 double precision format which has a relative error of about 1.11e-16. (resource)
The real issue is that the floor function casts the float value into an int value, e.g.
(int)(10.04 * 100) = 1003
as we see in the floor function earlier. (resource)So to overcome this issue we can cast the float to a string, a string can represent any value accurately, then the floor function will cast the string to an int accurately.
要截断数字,“最好”是使用(我在这里取了一个适用于我的示例的数字):
希望这个帮助比这里的其他答案更容易,但仅适用于它适用的情况。这是一个不起作用的情况(demo):
number_format 函数很棘手,你可以这样解决你的问题方式(来自php.net):
为了防止当最后一个有效小数后的下一个数字为5时发生舍入(下面几个人提到):
To truncate numbers the "best" is to use (I took a number here that works for my example):
Hope this help is easy than other answers here, but it is easy only for the cases it works for. Here is a case it does not work for (demo):
The number_format function is tricky, you can solve your problem this way (from php.net):
To prevent the rounding that occurs when next digit after last significant decimal is 5 (mentioned by several people below):
虽然这个问题很旧,但我会发布另一个答案,以防有人像我一样偶然发现这个问题。正数的一个简单解决方案是:
我针对具有 1000 万个随机分数的基于字符串的解决方案进行了测试,它们始终匹配。它不会出现基于
floor
的解决方案所存在的精度问题。将其扩展到零和负数非常简单。
Though this question is old, I'll post another answer in case someone stumbles upon this problem like I did. A simple solution for positive numbers is:
I tested it against a string-based solution with 10 million random fractions and they always matched. It doesn't exhibit the precision issue that
floor
-based solutions do.Extending it for for zero and negatives is quite simple.