Got another math calculation problem again.
又有一道数学题了。
$a = 34.56
$b = 34.55
$a
do some calculation to get this figure
a做一些计算来得到这个数字
$b
is doing rounding nearest 0.05 to get this figure
为了得到这个数字,b美元做了接近0.05的四舍五入
what happen is
什么是发生
$c = $b - $a
supposedly I should be -0.01, but I echo out the $c
is show -0.00988888888888
假设我应该是-0.01,但我输入c美元显示- 0.009888888888888888
I try to use number_format($c, 2)
, but the output is 0.00,
我尝试使用number_format($c, 2),但是输出是0。00,
how can I make sure $a
and $b
is exactly 2 decimals, no hidden number at the back.
我怎么能确定a和b正好是2个小数,后面没有隐藏的数字。
in my php knowledge number_format only able to format the display, but the value not really 2 decimal,
在我的php知识中,number_format只能格式化显示,但值不是2小数,
I hope I can get help from here. This really frustrated me.
我希望能从这里得到帮助。这真的令我很沮丧。
7 个解决方案
#1
37
Try sprintf
("%.2f", $c);
尝试sprintf(“%。2 f ",$ c);
Floating point numbers are represented in IEEE notation based on the powers of 2, so terminating decimal numbers may not be a terminating binary number, that's why you get the trailing digits.
浮点数在IEEE表示法中是基于2的幂来表示的,所以终止十进制数可能不是终止二进制数,这就是为什么你会得到尾随数字。
As suggested by Variable Length Coder, if you know the precision you want and it doesn't change (e.g. when you're dealing with money) it might be better to just use fixed point numbers i.e. express the numbers as cents rather than dollars
正如可变长度编码器所建议的,如果你知道你想要的精度并且它不会改变(例如,当你在处理金钱时),最好只使用定点数字,例如把数字表示成美分而不是美元
$a = 3456;
$b = 3455;
$c = $b - $a;
sprintf ("%.2f", $c/100.0);
This way, you won't have any rounding errors if you do a lot of calculations before printing.
这样,如果您在打印之前进行大量计算,就不会有任何舍入错误。
#2
12
Use round()
:
使用圆():
$c = round($b - $a, 2);
Note: you can also choose the rounding mode as appropriate.
注意:您还可以根据需要选择舍入模式。
Edit: Ok I don't get what sprintf()
is doing that number_format()
isn't:
编辑:好的,我不知道sprintf()在做什么,number_format()不是:
$c = number_format($b - $a, 2);
vs
vs
$c = sprintf("%.2f", $b - $a);
?
吗?
#3
4
Native Calculation:
本地计算:
$a = 34.56;
$b = 34.55;
$c = $b - $a; // -0.010000000000005
Works as expected (! use always BC functions for real number calculations, the issue is for all C based platforms):
像预期的那样工作(!始终使用BC函数进行实数计算,问题是针对所有基于C的平台):
$a = '34.56';
$b = '34.55';
$c = bcsub($b, $a, 4); // -0.0100
#4
3
You've run into one of the traps of floating point numbers; that they cannot always represent exact decimal fractions. If you want exact decimal values, you're better off using integers and then dividing by the precision you want at the end.
你遇到了浮点数的一个陷阱;它们不能总是表示精确的小数部分。如果你想要精确的十进制值,你最好使用整数然后除以最后你想要的精度。
For example, if you're doing calculations in floats represeting Dollars (or your favorite currency), you may want to actually do your calculations in integer cents.
例如,如果您在浮动中进行计算,重新预置美元(或您最喜欢的货币),您可能希望实际以整数美分进行计算。
#5
3
You can very neatly sidestep all of these issues simply by using the bcmath library.
通过使用bcmath库,您可以非常巧妙地回避所有这些问题。
Just remember to read the documentation and be careful whether you are passing arguments as strings or as numeric datatypes.
请记住阅读文档,并注意是否将参数传递为字符串或数字数据类型。
#6
3
I also ran into this issue recently when doing calculations with floats. For example, I had 2 floats that when subtracted and formatted, the value was -0.00.
我最近在使用浮点数进行计算时也遇到了这个问题。例如,我有2个浮点数当被减去并格式化时,它的值是-0.00。
$floatOne = 267.58;
$floatTwo = 267.58;
$result = number_format($floatOne - floatTwo, 2);
print $result; //printed a string -0.00
What I did was:
我所做的是:
$result = abs($floatOne - $floatTwo);// Made the number positive
print money_format('%i', $result); // printed the desired precision 0.00
In my solution I know that floatOne will never be less than floatTwo. The money_format function is only defined if the system has strfmon capabilities, Windows does not.
在我的解决方案中,我知道floatOne永远不会小于floatTwo。只有当系统具有strfmon功能时,才定义money_format函数,而Windows没有。
#7
1
If still somebody reach this page with similar problems where floating number subtraction causes error or strange values. I want to explain this problem with a bit more details. The culprit is the floating point numbers. And different operating systems and different versions of programming languages can behave differently.
如果还有人碰到类似的问题,比如浮点数减法会导致错误或奇怪的值。我想用更多的细节来解释这个问题。罪魁祸首是浮点数。不同的操作系统和不同版本的编程语言可以有不同的表现。
To illustrate the problem, I will explain why with a simple example below.
为了说明这个问题,我将用下面的一个简单示例解释为什么。
It is not directly related to PHP and it is not a bug. However, every programmer should be aware of this issue.
它与PHP没有直接关系,也不是bug。然而,每个程序员都应该意识到这个问题。
This problem even took many lives two decades ago.
20年前,这个问题甚至夺去了许多人的生命。
On 25 February 1991 this problem in floating number calculation in a MIM-104 Patriot missile battery prevented it intercepting an incoming Scud missile in Dhahran, Saudi Arabia, contributing to the death of 28 soldiers from the U.S. Army's 14th Quartermaster Detachment.
1991年2月25日,一枚mi -104“爱国者”导弹电池的浮点数计算问题阻止了它在沙特阿拉伯达赫兰拦截一枚飞来的飞毛腿导弹,导致美国陆军第14分遣队28名士兵死亡。
But why it happens?
但是为什么会发生呢?
The reason is that floating point values represent a limited precision. So, a value might not have the same string representation after any processing. It also includes writing a floating point value in your script and directly printing it without any mathematical operations.
原因是浮点值表示有限的精度。因此,在任何处理之后,一个值可能都没有相同的字符串表示。它还包括在您的脚本中编写一个浮点值,并在不需要任何数学操作的情况下直接打印它。
Just a simple example:
只是一个简单的例子:
$a = '36';
$b = '-35.99';
echo ($a + $b);
You would expect it to print 0.01, right? But it will print a very strange answer like 0.009999999999998
你会期望它输出0。01,对吧?但是它会打印一个非常奇怪的答案,比如0.009999999999998。
Like other numbers, floating point numbers double or float is stored in memory as a string of 0's and 1's. How floating point differs from integer is in how we interpret the 0's and 1's when we want to look at them. There are many standards how they are stored.
与其他数字一样,浮点数double或float都以0和1的字符串形式存储在内存中。浮点数与整型数的不同之处在于当我们想要看0和1时,我们如何解释它们。它们的存储方式有许多标准。
Floating-point numbers are typically packed into a computer datum as the sign bit, the exponent field, and the significand or mantissa, from left to right....
浮点数通常装进电脑基准符号位,指数,和有效数字或尾数,从左到右....
Decimal numbers are not well represented in binary due to lack of enough space. So, uou can't express 1/3 exactly as it's 0.3333333..., right? Why we can't represent 0.01 as a binary float number is for the same reason. 1/100 is 0.00000010100011110101110000..... with a repeating 10100011110101110000.
由于缺乏足够的空间,十进制数在二进制中不能很好地表示。uou不能准确地表示1/3等于0。33333。,对吧?我们不能把0。01表示成二进制浮点数的原因是一样的。1/100是0.00000010100011110101110000 .....重复10100011110101110000。
If 0.01 is kept in simplified and system-truncated form of 01000111101011100001010 in binary,when it is translated back to decimal, it would be read like 0.0099999.... depending on system (64bit computers will give you much better precision than 32-bits). Operating system decides in this case whether to print it as it sees or how to make it in more human-readable way. So, it is machine-dependent how they want to represent it. But it can be protected in language level with different methods.
如果0.01保存在简化和system-truncated 01000111101011100001010以二进制形式,翻译时小数,读起来就像是0.0099999 ....取决于系统(64位计算机将给您比32位更精确的精度)。在这种情况下,操作系统决定是按它看到的方式打印,还是以更人性化的方式打印。因此,它是由机器决定的。但在语言层面上可以通过不同的方法进行保护。
If you format the result, echo number_format(0.009999999999998, 2); it will print 0.01.
如果您格式化结果,echo number_format(0.0099999999999999998, 2);它将打印0.01。
It is because in this case you instruct how it should be read and how precision you require.
这是因为在这种情况下,您会指导如何读取它,以及需要多大的精度。
#1
37
Try sprintf
("%.2f", $c);
尝试sprintf(“%。2 f ",$ c);
Floating point numbers are represented in IEEE notation based on the powers of 2, so terminating decimal numbers may not be a terminating binary number, that's why you get the trailing digits.
浮点数在IEEE表示法中是基于2的幂来表示的,所以终止十进制数可能不是终止二进制数,这就是为什么你会得到尾随数字。
As suggested by Variable Length Coder, if you know the precision you want and it doesn't change (e.g. when you're dealing with money) it might be better to just use fixed point numbers i.e. express the numbers as cents rather than dollars
正如可变长度编码器所建议的,如果你知道你想要的精度并且它不会改变(例如,当你在处理金钱时),最好只使用定点数字,例如把数字表示成美分而不是美元
$a = 3456;
$b = 3455;
$c = $b - $a;
sprintf ("%.2f", $c/100.0);
This way, you won't have any rounding errors if you do a lot of calculations before printing.
这样,如果您在打印之前进行大量计算,就不会有任何舍入错误。
#2
12
Use round()
:
使用圆():
$c = round($b - $a, 2);
Note: you can also choose the rounding mode as appropriate.
注意:您还可以根据需要选择舍入模式。
Edit: Ok I don't get what sprintf()
is doing that number_format()
isn't:
编辑:好的,我不知道sprintf()在做什么,number_format()不是:
$c = number_format($b - $a, 2);
vs
vs
$c = sprintf("%.2f", $b - $a);
?
吗?
#3
4
Native Calculation:
本地计算:
$a = 34.56;
$b = 34.55;
$c = $b - $a; // -0.010000000000005
Works as expected (! use always BC functions for real number calculations, the issue is for all C based platforms):
像预期的那样工作(!始终使用BC函数进行实数计算,问题是针对所有基于C的平台):
$a = '34.56';
$b = '34.55';
$c = bcsub($b, $a, 4); // -0.0100
#4
3
You've run into one of the traps of floating point numbers; that they cannot always represent exact decimal fractions. If you want exact decimal values, you're better off using integers and then dividing by the precision you want at the end.
你遇到了浮点数的一个陷阱;它们不能总是表示精确的小数部分。如果你想要精确的十进制值,你最好使用整数然后除以最后你想要的精度。
For example, if you're doing calculations in floats represeting Dollars (or your favorite currency), you may want to actually do your calculations in integer cents.
例如,如果您在浮动中进行计算,重新预置美元(或您最喜欢的货币),您可能希望实际以整数美分进行计算。
#5
3
You can very neatly sidestep all of these issues simply by using the bcmath library.
通过使用bcmath库,您可以非常巧妙地回避所有这些问题。
Just remember to read the documentation and be careful whether you are passing arguments as strings or as numeric datatypes.
请记住阅读文档,并注意是否将参数传递为字符串或数字数据类型。
#6
3
I also ran into this issue recently when doing calculations with floats. For example, I had 2 floats that when subtracted and formatted, the value was -0.00.
我最近在使用浮点数进行计算时也遇到了这个问题。例如,我有2个浮点数当被减去并格式化时,它的值是-0.00。
$floatOne = 267.58;
$floatTwo = 267.58;
$result = number_format($floatOne - floatTwo, 2);
print $result; //printed a string -0.00
What I did was:
我所做的是:
$result = abs($floatOne - $floatTwo);// Made the number positive
print money_format('%i', $result); // printed the desired precision 0.00
In my solution I know that floatOne will never be less than floatTwo. The money_format function is only defined if the system has strfmon capabilities, Windows does not.
在我的解决方案中,我知道floatOne永远不会小于floatTwo。只有当系统具有strfmon功能时,才定义money_format函数,而Windows没有。
#7
1
If still somebody reach this page with similar problems where floating number subtraction causes error or strange values. I want to explain this problem with a bit more details. The culprit is the floating point numbers. And different operating systems and different versions of programming languages can behave differently.
如果还有人碰到类似的问题,比如浮点数减法会导致错误或奇怪的值。我想用更多的细节来解释这个问题。罪魁祸首是浮点数。不同的操作系统和不同版本的编程语言可以有不同的表现。
To illustrate the problem, I will explain why with a simple example below.
为了说明这个问题,我将用下面的一个简单示例解释为什么。
It is not directly related to PHP and it is not a bug. However, every programmer should be aware of this issue.
它与PHP没有直接关系,也不是bug。然而,每个程序员都应该意识到这个问题。
This problem even took many lives two decades ago.
20年前,这个问题甚至夺去了许多人的生命。
On 25 February 1991 this problem in floating number calculation in a MIM-104 Patriot missile battery prevented it intercepting an incoming Scud missile in Dhahran, Saudi Arabia, contributing to the death of 28 soldiers from the U.S. Army's 14th Quartermaster Detachment.
1991年2月25日,一枚mi -104“爱国者”导弹电池的浮点数计算问题阻止了它在沙特阿拉伯达赫兰拦截一枚飞来的飞毛腿导弹,导致美国陆军第14分遣队28名士兵死亡。
But why it happens?
但是为什么会发生呢?
The reason is that floating point values represent a limited precision. So, a value might not have the same string representation after any processing. It also includes writing a floating point value in your script and directly printing it without any mathematical operations.
原因是浮点值表示有限的精度。因此,在任何处理之后,一个值可能都没有相同的字符串表示。它还包括在您的脚本中编写一个浮点值,并在不需要任何数学操作的情况下直接打印它。
Just a simple example:
只是一个简单的例子:
$a = '36';
$b = '-35.99';
echo ($a + $b);
You would expect it to print 0.01, right? But it will print a very strange answer like 0.009999999999998
你会期望它输出0。01,对吧?但是它会打印一个非常奇怪的答案,比如0.009999999999998。
Like other numbers, floating point numbers double or float is stored in memory as a string of 0's and 1's. How floating point differs from integer is in how we interpret the 0's and 1's when we want to look at them. There are many standards how they are stored.
与其他数字一样,浮点数double或float都以0和1的字符串形式存储在内存中。浮点数与整型数的不同之处在于当我们想要看0和1时,我们如何解释它们。它们的存储方式有许多标准。
Floating-point numbers are typically packed into a computer datum as the sign bit, the exponent field, and the significand or mantissa, from left to right....
浮点数通常装进电脑基准符号位,指数,和有效数字或尾数,从左到右....
Decimal numbers are not well represented in binary due to lack of enough space. So, uou can't express 1/3 exactly as it's 0.3333333..., right? Why we can't represent 0.01 as a binary float number is for the same reason. 1/100 is 0.00000010100011110101110000..... with a repeating 10100011110101110000.
由于缺乏足够的空间,十进制数在二进制中不能很好地表示。uou不能准确地表示1/3等于0。33333。,对吧?我们不能把0。01表示成二进制浮点数的原因是一样的。1/100是0.00000010100011110101110000 .....重复10100011110101110000。
If 0.01 is kept in simplified and system-truncated form of 01000111101011100001010 in binary,when it is translated back to decimal, it would be read like 0.0099999.... depending on system (64bit computers will give you much better precision than 32-bits). Operating system decides in this case whether to print it as it sees or how to make it in more human-readable way. So, it is machine-dependent how they want to represent it. But it can be protected in language level with different methods.
如果0.01保存在简化和system-truncated 01000111101011100001010以二进制形式,翻译时小数,读起来就像是0.0099999 ....取决于系统(64位计算机将给您比32位更精确的精度)。在这种情况下,操作系统决定是按它看到的方式打印,还是以更人性化的方式打印。因此,它是由机器决定的。但在语言层面上可以通过不同的方法进行保护。
If you format the result, echo number_format(0.009999999999998, 2); it will print 0.01.
如果您格式化结果,echo number_format(0.0099999999999999998, 2);它将打印0.01。
It is because in this case you instruct how it should be read and how precision you require.
这是因为在这种情况下,您会指导如何读取它,以及需要多大的精度。