在浮点和双精度之间进行选择
Choosing between float and double
背景:
我一直在处理以下问题,"旅行"从"编程挑战:编程竞赛培训手册"由S.Skiena:
一群学生是一个俱乐部的成员,该俱乐部每年都会前往不同的位置。他们过去的目的地包括印第安纳波利斯、菲尼克斯、纳什维尔、费城、圣何塞和亚特兰大。今年春天,他们计划去埃因霍温旅行。
集团事先同意平均分摊费用,但事实并非如此在发生每一笔费用时都要分摊。因此团体支付特定的费用,如餐费、酒店费、出租车费,和机票。旅行结束后,统计每个学生的费用货币交换使得每个人的净成本相同一美分以内。在过去,这种货币兑换一直很乏味耗时。你的工作是根据开支清单计算为了平衡必须转手的最低金额(一分钱以内)所有学生的费用。
输入
标准输入将包含多个行程的信息。每个跳闸由一条包含正整数n的线组成,n表示旅行中的学生人数。这之后是n行输入,每个都包含学生花费的美元和美分。学生人数不超过1000人,且没有学生花费超过10000.00美元最后一次旅行。
输出
对于每次旅行,输出一行说明总金额,以美元和美分,必须交换才能使学生的成本。
(粗体是我的,在这里预订,在这里网站)
我用以下代码解决了这个问题:
/*
* the-trip.cpp
*/
#include <iostream>
#include <iomanip>
#include <cmath>
int main( int argc, char * argv[] )
{
int students_number, transaction_cents;
double expenses[1000], total, average, given_change, taken_change, minimum_change;
while (std::cin >> students_number) {
if (students_number == 0) {
return 0;
}
total = 0;
for (int i=0; i<students_number; i++) {
std::cin >> expenses[i];
total += expenses[i];
}
average = total / students_number;
given_change = 0;
taken_change = 0;
for (int i=0; i<students_number; i++) {
if (average > expenses[i]) {
given_change += std::floor((average - expenses[i]) * 100) / 100;
}
if (average < expenses[i]) {
taken_change += std::floor((expenses[i] - average) * 100) / 100;
}
}
minimum_change = given_change > taken_change ? given_change : taken_change;
std::cout << "$" << std::setprecision(2) << std::fixed << minimum_change << std::endl;
}
return 0;
}
我最初的实现有float
而不是double
。它处理的是描述中提供的小问题实例,我花了很多时间试图找出问题所在。
最后,我发现我必须使用double
精度,显然编程挑战测试中的一些大输入使我的浮点算法失败了。
问题:
假设输入可以有1000个学生,每个学生最多可以花费10000$,我的total
变量必须存储一个最大大小为10000000.
我应该如何决定需要哪种精度?有没有什么东西应该给我一个提示,float对于这个任务来说是不够的?
后来我意识到,在这种情况下,我本可以避免浮点运算,因为我的数字适合整数类型,但我仍然有兴趣了解是否有办法预见float
在这种情况中不够精确。
有没有什么东西应该给我一个提示,float对于这个任务来说是不够的?
0.10在二进制浮点中根本不可表示(如果使用普通计算机,则float
和double
都是)这一事实应该是提示。二进制浮点非常适合一开始就不准确的物理量,或者无论是具有可判定等式的合理数值系统,都不准确的计算。精确计算货币金额不是二进制浮点的好应用。
我应该如何决定需要哪种精度…我的总变量必须存储一个最大大小为10000000的数字。
使用整数类型来表示美分数。根据你自己的推理,你不应该处理超过1000000000美分的金额,所以long
应该足够了,但只需使用long long
,就可以避免遇到麻烦的风险。
正如您所说:永远不要使用浮点变量来表示货币。使用整数表示法-可以是一个以美分或当地货币的小数形式表示的大数字,也可以是两个数字[这让数学运算有点尴尬,但更容易看到/读取/写入两个单位的值]。
不使用浮点的动机是它"经常不准确"。就像1/3不能用十进制表示法写为一个精确的值一样,无论你写了多少个三,实际的答案都会有更多的三,二进制浮点值不能精确地描述一些十进制值,你会得到"你的0.20值与客户欠的0.20不匹配"-这没有意义,但这是因为根据计算机的说法,"0.200000000001"answers"0.19999999999"并不完全相同。最终,这些微小的舍入误差将以某种方式引发一些大问题——无论是float
、double
还是extra_super_long_double
。
然而,如果你有这样的问题:如果我必须用单位的1/100的精度表示一个1000万的值,我需要多大的浮点变量,你的计算就会变成:
float bigNumber = 10000000;
float smallNumber = 0.01;
float bits = log2(bigNumber/smallNumber);
cout << "Bits in mantissa needed: " << ceil(bits) << endl;
所以,在这种情况下,我们得到的比特是29.897,所以你需要30个比特(换句话说,float
还不够好
当然,如果你不需要一美元的零头(或其他什么),你可以少用几个数字。即log2(10000000)
=23.2,因此24位尾数->对于float
来说仍然太大。
10000000>2^23,因此您至少需要24位尾数,这就是单精度所提供的。由于中间取整,最后一位可能出错。
1位~3.321928位。
- QTreeView幻灯片多选后无法使用单击选择
- 在PostgreSQL中根据它们的ID选择大量行的最快方法是什么?
- 组合框第一行不可选择
- 计算所选行的总和
- 选择特定版本的 Visual Studio 命令行工具包,并根据特定版本的C++运行时环境编译文件
- extensor:选择具有特定列值的行
- 从 QTreeView 中删除项目时取消选择所有行
- 复制矩阵选择不复制的行
- 为什么QTableView扩展选择忽略了我的默认选择行
- 如何在 TStringGrid 中启用多行选择
- 当列中的属性与eigen,c 中的给定条件匹配时,仅选择这些行froom a矩阵
- Delphi / C 构建器 - 在TDBGrid中设置活动 /选择的行颜色
- 如何从 gtkmm 树视图中获取所选行
- 如何在多行文本C++的左上角显示复选框
- CListCtrl GetSelectionMark() 未返回正确选择的行
- 如何将所选行的第一列值绑定到变量并在 QT 的 SQL 命令中使用它?
- 在win API中带有图像和整行选择的组合框
- QTable查看如何确定是否选择了“行”
- 如何让我的QTableView与QSqlTableModel具有复选框和多行?
- 从 QT 复选框到 Postgresql 选择星期几