c++ - 在 float 和 double 之间选择

标签 c++ algorithm floating-point precision

背景:

我一直在研究以下问题,S. Skiena 的“编程挑战:编程竞赛培训手册”中的“旅行”:

A group of students are members of a club that travels annually to different locations. Their destinations in the past have included Indianapolis, Phoenix, Nashville, Philadelphia, San Jose, and Atlanta. This spring they are planning a trip to Eindhoven.

The group agrees in advance to share expenses equally, but it is not practical to share every expense as it occurs. Thus individuals in the group pay for particular things, such as meals, hotels, taxi rides, and plane tickets. After the trip, each student's expenses are tallied and money is exchanged so that the net cost to each is the same, to within one cent. In the past, this money exchange has been tedious and time consuming. Your job is to compute, from a list of expenses, the minimum amount of money that must change hands in order to equalize (within one cent) all the students' costs.

Input

Standard input will contain the information for several trips. Each trip consists of a line containing a positive integer n denoting the number of students on the trip. This is followed by n lines of input, each containing the amount spent by a student in dollars and cents. There are no more than 1000 students and no student spent more than $10,000.00. A single line containing 0 follows the information for the last trip.

Output

For each trip, output a line stating the total amount of money, in dollars and cents, that must be exchanged to equalize the students' costs.

(粗体是我的,书 here,网站 here)

我用下面的代码解决了这个问题:

/*
 * 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 精度,显然编程挑战测试中的一些大输入使我的 float 算法失败。

问题:

鉴于输入可以有 1000 个学生,每个学生最多可以花费 10,000 美元,我的 total 变量必须存储最大大小的数字 10,000,000。

我应该如何决定需要哪种精度? 有什么东西应该给我一个提示,即 float 不足以完成这项任务吗?

我后来意识到,在这种情况下,我完全可以避免 float ,因为我的数字适合整数类型,但我仍然有兴趣了解是否有办法预见 float在这种情况下不够精确。

最佳答案

Is there something that should have given me an hint that float wasn't enough for this task?

事实上 0.10 根本不能用二进制 float 表示(如果您使用普通计算机,floatdouble 都是)应该是提示.二进制 float 非常适合一开始就不准确的物理量,或者无论具有可判定相等性的合理数值系统如何都会不准确的计算。货币金额的精确计算不是二进制 float 的良好应用。

How should I decide which precision is needed? … my total variable has to store a number of the maximum size of 10,000,000.

使用整数类型表示美分数。根据你自己的推理,你不应该处理超过 1,000,000,000 美分的金额,所以 long 应该足够了,但只需使用 long long 并节省你自己的风险角落案例的麻烦。

关于c++ - 在 float 和 double 之间选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33983827/

相关文章:

c++ - 无法将 char* 转换为 int*

algorithm - 使用并行处理器运行时哪种算法最好?

algorithm - 访问最高值附近项目的最佳数据结构?

C++在 vector 中找到相同的记录

构造函数中的 C++ 类型特征导致错误

c++ - 如何从c++中的文本文件填充数组

c++ - 如何正确迭代 double

c# - 使用 C# 在 PostgreSQL 上 float

c++ - 可以从指向成员函数模板参数的指针推导出类类型吗

ruby - 在 ruby 中 float 圆形错误?