简体   繁体   English

使用 static_cast 从 int 转换为 float 时输出不正确<float> C++

[英]Incorrect output when converting from int to float with static_cast<float> c++

I do not understand why my output is coming out as wrong.我不明白为什么我的输出是错误的。 I have attached my code and my result(highlighted the issue)我附上了我的代码和我的结果(突出显示了问题)

I am adding a coin to the insertCoin function in VendingMachine class.我正在向 VendingMachine 类中的 insertCoin 函数添加一个硬币。 As soon as I add 10 cents to this function, it prints out error-> Does not accept 10 cents.一旦我向这个函数添加 10 美分,它就会打印出错误-> 不接受 10 美分。 I am converting the input to float using static_cast.我正在使用 static_cast 将输入转换为浮点数。 I spent some good time on this and at this point, I feel I just cannot see the issue probably because I dont understand some concept.我花了一些时间在这上面,在这一点上,我觉得我看不到这个问题,可能是因为我不明白一些概念。

Also as a quick background, new to c++ and trying to get my object oriented programming up to date.也作为快速背景,C++ 新手并试图让我的面向对象编程保持最新状态。 Trying to make a Vending machine product hehe.尝试制作自动售货机产品呵呵。 Thank you again !再次感谢你 !

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <utility>
#include <stdlib.h> 
using namespace std;

class Item
{
    private:
        string m_name;
        float m_cost;
    public:
        Item(string t_name,float t_cost):m_name(t_name),m_cost(t_cost)
        {
            
        }
        string getName()
        {
            return m_name;
        }
        float getCost()
        {
            return m_cost;
        }
};
class VendingMachine
{
    private:
        vector<Item> m_totalItems;
        unordered_map<string,Item>m_products;
        float m_remainingCharges{0};
        float m_moneyInserted{0};
        size_t itemCount{0};
    public:
        VendingMachine()
        {
            
        }
        void addItemToVendingMachine(string t_name,size_t t_cost)
        {
            float temp=static_cast<float>(t_cost)/static_cast<float>(100);  
            Item item(t_name,temp);
            m_totalItems.push_back(item);
            m_products.insert(make_pair(t_name,item));
        }
        bool chooseProduct(string t_name)
        {
            for(auto item:m_totalItems)
            {
                if(item.getName()==t_name)
                {
                    m_remainingCharges=item.getCost();
                    return true;
                }
                itemCount++;
            }
            cout<<"Item not currently available: "+t_name<<endl;
            return false;
        }
        void insertCoin(size_t t_coin)
        {   
            float temp=static_cast<float>(t_coin);
            if(t_coin<=50)
            {
                temp/=100;
                cout<<temp<<endl;
            }
            if(temp==0.01 or temp==0.05 or temp==0.1 or temp==1.00 or temp==2.00 or temp==0.25 or temp==0.50)
            {
                m_moneyInserted+=temp;
                m_remainingCharges-=m_moneyInserted;
            }
            else
            {
                cout<<"Does not accept: "<< t_coin<<" ,please insert correct coin."<<endl;
                return;
            }
        }
        pair<Item,float> getProduct()
        {
            auto item=m_totalItems[itemCount];
            auto itemBack=m_totalItems.back();
            m_totalItems[itemCount]=itemBack;
            m_totalItems.pop_back();
            return make_pair(item,abs(m_remainingCharges));
        }
        
        float refund()
        {
            if(m_remainingCharges<0)
                return abs(m_remainingCharges);
            else
                return 0;
        }
        void resetOperator()
        {
             m_remainingCharges=0;
             m_moneyInserted=0;
             itemCount=0;
        }
};
int main()
{
    Item item("Candy",0.50);
    cout<<item.getName()<<" ";
    cout<<item.getCost()<<endl;
    VendingMachine machine;
    machine.addItemToVendingMachine("CANDY",10);
    machine.addItemToVendingMachine("SNACK",50);
    machine.addItemToVendingMachine("Coke",25);
    machine.insertCoin(10);
    machine.insertCoin(25);
    machine.insertCoin(50);
    machine.chooseProduct("CANDY");
    auto temp=machine.getProduct();
    cout<<temp.first.getName()<<endl;
    cout<<temp.second<<endl;
    machine.resetOperator();
    return 0;  
};

在此处输入图片说明

Why doesn't it work as expected ?为什么它没有按预期工作?

This is not specific to C++ but to floating point numbers.这不是特定于 C++,而是特定于浮点数。

The C++ standard doesn't tell how floating points are encoded. C++ 标准没有说明浮点是如何编码的。 But in general, floating point numbers are encoded using power of two fractions.但一般来说,浮点数是使用两个小数的幂进行编码的。 And with these schemes, some decimal numbers have no exact match and instead, the closest match is used, with some approximation.在这些方案中,一些十进制数没有精确匹配,而是使用最接近的匹配,并带有一些近似值。

For example, the most popular encoding is probably IEEE-754 .例如,最流行的编码可能是IEEE-754 This nice online converter , shows that there is no exact match for 0.1 .这个不错的在线转换器显示0.1没有完全匹配。 The closest approximation is 0.100000001490116119384765625 :最接近的近似值是0.100000001490116119384765625

  • If you print this value out, with all the rounding, everything will seem fine.如果你打印出这个值,加上所有的四舍五入,一切都会看起来很好。
  • But if you compare for equality with == , the value must be exactly the same.但是,如果您使用==比较相等性,则该值必须完全相同。 Unfortunately different calculations may make different roundings.不幸的是,不同的计算可能会进行不同的舍入。 So you will get two different numbers, very close to 0.1 but different from each other.所以你会得到两个不同的数字,非常接近 0.1 但彼此不同。
  • In your case, the literal value 0.1 is not a float: it's a double, which has a higher precision than a float and hence makes different approximations.在您的情况下,文字值 0.1 不是浮点数:它是一个双精度值,它比浮点数具有更高的精度,因此会产生不同的近似值。

Practical evidence实际证据

If you're not convinced, try the following changes:如果您不相信,请尝试以下更改:

    if(t_coin<=50)
    {
        temp/=100;
        cout.precision(10);  // print more digits
        cout<<scientific<<"temp:"<<temp<<" vs.double "<<0.1
             <<" or float "<<0.1f<<endl;
        }

and then try to compare floats with floats:然后尝试将浮点数与浮点数进行比较:

    if(temp==0.01f or temp==0.05f or temp==0.1f 
        or temp==1.00f or temp==2.00f 
        or temp==0.25f or temp==0.50f)

Online demo在线演示

How to solve it?如何解决?

The best option here, is to work with integers and count the cents, as someone suggested in the comments.正如有人在评论中建议的那样,这里最好的选择是使用整数并计算美分。 There is no floating point induced approximation in such calculation, so for money it's ideal.在这种计算中没有浮点引起的近似值,所以对于金钱来说它是理想的。

A workaround it to align all the floating point numbers to either double or float (by adding a trailing f to the numeric literals).一种解决方法是将所有浮点数对齐为doublefloat (通过向数字文字添加尾随f )。 This would work when comparing constant values, if there could be no rounding issue inf some calculations.如果在某些计算中没有舍入问题,这将在比较常量值时起作用。

Another solution is to replace the strict equality comparison, with an ineguality checking that the difference between both numbers is very small.另一种解决方案是替换严格相等比较,用不等式检查两个数字之间的差异是否非常小。 This is the epsilon approach proposed by Jason in the other answer , comparing the values with the help of a function almost_equal() defined as explained here这是 Jason在另一个答案中提出的 epsilon 方法, 此处解释的almost_equal()函数的帮助下比较这些值

Not related: don't use size_t for reperesenting money.不相关:不要使用size_t来表示钱。 THhis is misleading for the readers and maintainers ;-).这对读者和维护者来说是一种误导;-)。 If you want an unsigned int, or an unsigned long, or an unsigned long long say so.如果你想要一个 unsigned int、一个 unsigned long 或一个 unsigned long long,就这么说。

You can use the epsilon to compare floating point numbers, overcoming the fact that it's very difficult to precisely equate floating point numbers, you have to use "very nearly equals".您可以使用epsilon来比较浮点数,克服了很难精确地相等浮点数的事实,您必须使用“非常接近等于”。 See this post for more detail: What is the most effective way for float and double comparison?有关更多详细信息,请参阅此帖子: 浮动和双重比较的最有效方法是什么?

Personally I would recommend changing your code to work in integer units of cents in order to avoid having to implement the "very nearly equals" pattern throughout your code.我个人建议将代码更改为以美分整数单位工作,以避免在整个代码中实现“非常接近”模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM