更改关联性时答案略有不同
Slightly different answers when changing associativity
通过以下简单的 C++ 练习
#include <iostream>
using namespace std;
int main()
{
int euro, cents_v1, cents_v2, do_again;
double price;
do_again = 1;
while(do_again != 0){
cout<<"Insert price in Euro with cents"<<endl;
cin>>price;
euro = price;
cents_v1 = price*100 - euro*100;
cents_v2 = (price - euro) * 100;
cout<<"Total: "<<euro<<" euro and "<<cents_v1<<" cents"<< endl;
cout<<"Total: "<<euro<<" euro and "<<cents_v2<<" cents"<< endl;
cout <<"\nDo it again? 1: yes, 0: no."<<endl;
cin>>do_again;
}
return 0;
}
你可以得到两个不同的答案,如果输入是,例如 31.13:
Insert price in Euro with cents
31.13
Total: 31 euro and 13 cents
Total: 31 euro and 12 cents
Do it again? 1: yes, 0: no.
如何处理这个问题?在更复杂的情况下,编程中是否有避免或控制此问题的规则?
由于四舍五入问题,您永远不应使用 float
或 double
来表示货币单位(它们不准确,而当涉及到金钱时,人们希望准确)。
尝试将以下行添加到您的代码中:
std::cout << std:: setprecision(17) << price << "\n";
根据您的输入结果是:
31.129999999999999
所以你要么需要创建一个 class 来代表你的钱(这不是个坏主意)。或者使用整数(因为整数总是精确的)。因此,将金额存储为 cents 的数量而不是欧元的数量。然后显示时只转换为欧元和美分。
class DecimalCurrencyUnit
{
long cents;
public:
DecimalCurrencyUnit()
: cents(0)
{}
DecimalCurrencyUnit(long euros, long cent)
: cents(euros * 100 + cent)
{}
friend std::ostream& operator<<(std::ostream& s, DecimalCurrencyUnit const& out)
{
return s << '€'
<< (out.cents / 100) << "."
<< std::setw(2) << std::setfill('0') << (out.cents % 100);
}
friend std::istream& operator>>(std::istream& s, DecimalCurrencyUnit& in)
{
long euros;
long cent;
char s;
char x;
if ((s >> s >> euros >> x >> cent) && s == '€' && x == '.' && cent < 100)
{
in.cents = euros * 100 + cent;
}
else
{
s.setsetate(std::ios::bad);
}
return s;
}
// Add standard operators here for *+/- etc.
}
浮点加法和乘法是可交换的,但不是结合的或分配的。因此,正如您所观察到的,cents_v1
和 cents_v2
实际上可能代表不同的值。
避免这些类型的浮点陷阱的一种通用技术是使用任意精度算术库。有 quite a few of these,我对它们不够熟悉,无法推荐一个。当然,使用任意精度的数字会导致性能下降,但是在您知道您的算法是瓶颈之前,进一步优化是不明智的。
如果您绝对必须使用浮点运算,可以使用多种经验法则来提高长浮点计算的准确性;查看一些数值方法文本以获取更多信息。还有大量关于浮点计算精度的研究,产生了一些 rather cool software.
通过以下简单的 C++ 练习
#include <iostream>
using namespace std;
int main()
{
int euro, cents_v1, cents_v2, do_again;
double price;
do_again = 1;
while(do_again != 0){
cout<<"Insert price in Euro with cents"<<endl;
cin>>price;
euro = price;
cents_v1 = price*100 - euro*100;
cents_v2 = (price - euro) * 100;
cout<<"Total: "<<euro<<" euro and "<<cents_v1<<" cents"<< endl;
cout<<"Total: "<<euro<<" euro and "<<cents_v2<<" cents"<< endl;
cout <<"\nDo it again? 1: yes, 0: no."<<endl;
cin>>do_again;
}
return 0;
}
你可以得到两个不同的答案,如果输入是,例如 31.13:
Insert price in Euro with cents
31.13
Total: 31 euro and 13 cents
Total: 31 euro and 12 cents
Do it again? 1: yes, 0: no.
如何处理这个问题?在更复杂的情况下,编程中是否有避免或控制此问题的规则?
由于四舍五入问题,您永远不应使用 float
或 double
来表示货币单位(它们不准确,而当涉及到金钱时,人们希望准确)。
尝试将以下行添加到您的代码中:
std::cout << std:: setprecision(17) << price << "\n";
根据您的输入结果是:
31.129999999999999
所以你要么需要创建一个 class 来代表你的钱(这不是个坏主意)。或者使用整数(因为整数总是精确的)。因此,将金额存储为 cents 的数量而不是欧元的数量。然后显示时只转换为欧元和美分。
class DecimalCurrencyUnit
{
long cents;
public:
DecimalCurrencyUnit()
: cents(0)
{}
DecimalCurrencyUnit(long euros, long cent)
: cents(euros * 100 + cent)
{}
friend std::ostream& operator<<(std::ostream& s, DecimalCurrencyUnit const& out)
{
return s << '€'
<< (out.cents / 100) << "."
<< std::setw(2) << std::setfill('0') << (out.cents % 100);
}
friend std::istream& operator>>(std::istream& s, DecimalCurrencyUnit& in)
{
long euros;
long cent;
char s;
char x;
if ((s >> s >> euros >> x >> cent) && s == '€' && x == '.' && cent < 100)
{
in.cents = euros * 100 + cent;
}
else
{
s.setsetate(std::ios::bad);
}
return s;
}
// Add standard operators here for *+/- etc.
}
浮点加法和乘法是可交换的,但不是结合的或分配的。因此,正如您所观察到的,cents_v1
和 cents_v2
实际上可能代表不同的值。
避免这些类型的浮点陷阱的一种通用技术是使用任意精度算术库。有 quite a few of these,我对它们不够熟悉,无法推荐一个。当然,使用任意精度的数字会导致性能下降,但是在您知道您的算法是瓶颈之前,进一步优化是不明智的。
如果您绝对必须使用浮点运算,可以使用多种经验法则来提高长浮点计算的准确性;查看一些数值方法文本以获取更多信息。还有大量关于浮点计算精度的研究,产生了一些 rather cool software.