【问题标题】:double, float, {0.0}, {0.0f}, what's the big deal?double、float、{0.0}、{0.0f},有什么大不了的?
【发布时间】:2020-07-10 01:39:06
【问题描述】:

所以我写了这个小练习程序,告诉你在给定的数量下你需要多少五分钱和美分。我不明白为什么即使数学正确,程序也会输出 0 美分。我无法理解问题所在。

// Example program
#include <iostream>

using namespace std;

int main()
{
    const double nickels{0.05};
    const double cents{0.01};
    int amt_needed_nickels{0}, amt_needed_cents{0};
    double amt_val{0.06f}; // for 0.06 cents
    double amt_val_copy{amt_val};
    
    amt_needed_nickels = amt_val / nickels;
    if(amt_needed_nickels != 0)
        amt_val -= (nickels * amt_needed_nickels);
    amt_needed_cents =  amt_val / cents;
    
    cout << "If it costs $" << amt_val_copy << ", you'll need:\nNickels: " << amt_needed_nickels << "\nCents: " << amt_needed_cents << "\n";
    cout << "Even though amt_val is " << amt_val << ", and cents is also " << cents << ", and 0.01/0.01 does equal 1, why is amt_needed_cents not 1?\n";
}

公平地说,我知道如果我将 const double cents{0.01} 更改为 const float cents{0.01},并从 double amt_val{0.06f} 中删除 f;加倍 amt_val{0.06};它会起作用,但我不明白表面下到底发生了什么。为什么程序给上面的amt_needed_cents 0,而在其他情况下给1?

// Example program
#include <iostream>

using namespace std;

int main()
{
    const double nickels{0.05};
    const float cents{0.01};
    int amt_needed_nickels{0}, amt_needed_cents{0};
    double amt_val{0.06}; // for 0.06 cents
    double amt_val_copy{amt_val};
    
    amt_needed_nickels = amt_val / nickels;
    if(amt_needed_nickels != 0)
        amt_val -= (nickels * amt_needed_nickels);
    amt_needed_cents =  amt_val / cents;
    
    cout << "If it costs $" << amt_val_copy << ", you'll need:\nNickels: " << amt_needed_nickels << "\nCents: " << amt_needed_cents << "\n";
    cout << "I know this is correct, but I don't know what the compiler is thinking with this as compared to the other\n\n";
}


【问题讨论】:

  • 我认为你希望这是一个浮点数而不是双精度数:double amt_val{0.06f}; // for 0.06 cents。 0.0 默认为双倍。而 0.0f 是一个浮点数。
  • 不要使用浮点数进行货币计算,因为浮点数并不精确。 0.010.050.06 都没有精确的二进制表示。使用整数,如果不是,请使用支持货币计算的其他(第三方)数据类型。
  • 是的,我认为这就是我刚刚想到的,例如 double var{0.0f} 与 float var{0.0} 相同吗?但这仍然不能让我理解为什么 float 和 double 会导致不同的值,但我想这只是一个需要注意的类型问题..?
  • 0.0f 明确是 float0.0 被假定为double。没有理由使用 f,除非您需要实际的 float
  • 这能回答你的问题吗? Is floating point math broken?

标签: c++ floating-point double


【解决方案1】:

浮点计算并不精确,在进行数学运算时,您会得到非常小的差异,如果您尝试检查数字是否完全相等,这将破坏您的程序。例如,1.2 * 3 不完全是 3.6(请注意,这并不意味着 所有 计算都是不精确的:例如,乘以 2 的任何幂总是精确的:像 1.5 * 2 这样的东西和1.1 / 4 总是给出最好的答案)。这是因为像1/101/100 这样的小数不能完全用二进制表示,当我们的硬币基于1/100(美分)时,这是一个问题。

在您的程序中,您应该通过将所有值转换为美分来将硬币表示为整数,然后一切都会正常工作。也就是说,您知道您将处理的最大分母是100,因此您不妨将所有内容都乘以100。所以,nickels 将是 5cents 将是 1,所有计算都将是精确的。

为避免混淆,以下是整数代码:

int dollars = 100, nickels = 5, cents = 1;

int amt_needed_dollars = amt_val / dollars;
amt_val -= amt_needed_dollars * dollars;
int amt_needed_nickels = amt_val / nickels;
amt_val -= amt_needed_nickels * nickels;
// skipped dividing by cents, because it's '1' anyway
int amt_needed_cents = amt_val;

【讨论】:

  • 更多关于浮点不精确的信息:0.30000000000000004
  • 我刚刚通过调试器运行代码,第 16 行的amt_val0.0099999986588954898,除以cents 得到0.99999986588954892。当您将最后一个数量变为int 时,您将得到一个零。
  • 我做了两个版本,一个用整数作为美分,第二个更精确地使用实数。我想我只是想如果你声明了 double var{0.0f}
  • @unxnut 查看floor()ceil() - 这些可能对您的情况有所帮助。
  • Re:“浮点计算不精确”——这是一种严重误导(尽管很常见)的过度简化。浮点计算精确;如果您多次进行相同的计算,您每次都会得到相同的结果。人们对浮点数学的问题是浮点数不是实数,所以你多年来学到的关于实数如何工作的大部分知识都不适用于浮点数。
【解决方案2】:
  • Float:浮点数。它是一个单精度 32 位数字。更常见。
  • Double:就像它的名字一样,它拥有“双”浮点值。它是一个双精度 64 位数字。它更精确,可以容纳方式更多的小数点。

我在 c++ 方面不是最擅长的,但看起来问题出在这里:

int amt_needed_nickels{0}, amt_needed_cents{0};
amt_needed_nickels = amt_val / nickels;

尝试将 amt_needed_cents 更改为浮点数或稍后将解析/舍入加倍。与其他语言(如 JS)不同,c++ 在尝试转换这样的类型时很烦人。您将两个浮点数划分为一个可能会截断小数的 int。

【讨论】:

    猜你喜欢
    • 2016-03-11
    • 1970-01-01
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多