简体   繁体   English

如何处理 C++ 数学表达式中的浮点比较,因此可以处理舍入错误?

[英]How to handle floating point comparisons in C++ mathematical expressions therefore rounding errors can be handled?

Let me explain little further.让我进一步解释一下。 I am writing a code to calculate the amount of water in swimming pool after filling it for some time at the filling rate.我正在编写一个代码来计算游泳池在以填充率填充一段时间后的水量。 Input taken is length , width , depth in foot , time to fill the pool as timeToFillPool in seconds , water filling rate in pool as fillingRate in in US Gallons/minute , amount of water already in the pool as poolCurrWaterAmount in US Gallons .采用的输入是lengthwidth英尺depth 、以为单位的timeToFillPool填充池的时间、以美国加仑/分钟为单位的池中的水填充速率为以美制加仑/分钟为单位的fillingRate 、以美国加仑为单位的poolCurrWaterAmount池中已经存在的水量。 From this information I calculated total pool water capacity, totalPoolCapacity , by multiplying length, width and depth and converting it to US Gallons.根据这些信息,我通过将长度、宽度和深度相乘并将其转换为美制加仑来计算总池水容量totalPoolCapacity

I asked the user that how much water is already in the pool and then calculated and shown user appropriate messages like water can be filled or not if the pool is already full or water filled in given time will exceed pool capacity?我问用户池中已有多少水,然后计算并向用户显示适当的消息,例如如果池已满或在给定时间内注满的水将超过池容量,是否可以注满水?

I am assuming all values input are positive or equal to zero if allowed to be zero.我假设输入的所有值都是正数或等于零(如果允许为零)。

#include <iostream>
#include <iomanip>
#include <cmath>  //for fabs() functions

using namespace std ;

int main()
{
    double timeToFillPool ;
    double length, width, depth, fillingRate ;
    double poolCurrWaterAmount, totalPoolCapacity ;
    const double CUBIC_FOOT_TO_USGALLON = 7.48052 ; //i.e. 1 cubic foot = 7.48052 US Gallon
    //setting tolerance value for comparing floating point numbers to 1/10000
    //any thing less will be considered zero
    const double EPSILON = 0.0001 ;

    //preparing the output stream to print floating numbers in decimal
    //form with precision to print two digits after decimal point
    cout << fixed << setprecision(2);

    cout << "Please enter swimming pool's dimensions,capacity,fill rate & drain rate information.\n";
    cout << "Enter Length in foot                         : " ;
    cin >> length ;

    cout << "Enter width in foot                          : " ;
    cin >> width ;

    cout << "Enter depth in foot                          : " ;
    cin >> depth ;

    cout << "Enter filling rate of water in US Gallon/min : " ;
    cin >> fillingRate ;

    //calculating totalPoolCapacity in US Gallon
    totalPoolCapacity = length * width * depth * CUBIC_FOOT_TO_USGALLON ;
    cout << "\n\nTotal pool capacity                          = " << totalPoolCapacity << " US Gallon." ;

    cout << "\n\nPlease enter current amount of water in pool in US Gallon to " ;
    cout << "\nfill the pool according to filling rate for the specific amount of time in minutes : " ;
    cin >> poolCurrWaterAmount ;
    //to check minimum and maximum range of current amount of water.
    while( !(poolCurrWaterAmount >= 0.0   &&   poolCurrWaterAmount <= totalPoolCapacity ) )
    {
        cout << "\nYou have entered in-valid value for current amount of water!"
             << "\nEnter current amount of water value from 0 to maximum capacity of pool "
             << setw(10) << totalPoolCapacity << " in US Gallon : " ;
        cin >> poolCurrWaterAmount ;
    }

    cout << "\nPlease enter time in minute to fill water in pool : " ;
    cin >> timeToFillPool ;


    //Calculations and message displayed are on the basis of whether the filling water
    //will cause overflow of water after filling the pool or not.
    //managing floating point eqaulity  poolCurrWaterAmount == totalPoolCapacity
    //setting the tolerance value EPSILON to 1/10000 = 0.0001 of a US Gallon
    if ( fabs(poolCurrWaterAmount - totalPoolCapacity) < EPSILON)
    {
        cout << "\n\nPool is Full. Water cannot be added." ;
        cout << "\nTotal water in pool is " << setw(10) << totalPoolCapacity << " US Gallon." ;
    }
    else if (fillingRate * timeToFillPool > (totalPoolCapacity - poolCurrWaterAmount) )
    {
        //case to check that time entered for filling water will cause overflow of water or not
        cout << "\n\nWarning! Pool will be overflowed with water! No water added!" ;
        cout << "\nCurrent amount of water in pool = "
             << setw(10) << poolCurrWaterAmount << " US Gallon." ;
        cout << "\nMaximum time required to completely fill the pool at\nfilling rate of "
             << setw(10) << fillingRate << " US Gallon/min is "
             << setw(10) << ( (totalPoolCapacity - poolCurrWaterAmount) / fillingRate ) << " minute." ;
    }
    else   //case where time entered for filling water will not cause overflow of water in pool
    {
        cout << "\n\nCurrent amount of water in pool = "
             << setw(10) << poolCurrWaterAmount << " US Gallon." ;
        cout << "\nAfter filling "
             << setw(10) << (fillingRate * timeToFillPool) << " US Gallon at filling rate of "
             << setw(10) << fillingRate << " US Gallons/min for "
             << setw(10) << timeToFillPool << " minute\nthe new amount of water in pool is "
             << setw(10) << ( poolCurrWaterAmount + fillingRate * timeToFillPool ) << " US Gallon." ;
    }

}
//end of main function
this is the ouput of the program: -
***********************************

Please enter swimming pool's dimensions,capacity,fill rate & drain rate information.
Enter Length in foot                         : 3
Enter width in foot                          : 2
Enter depth in foot                          : 2
Enter filling rate of water in US Gallon/min : 4


Total pool capacity                          = 89.77 US Gallon.

Please enter current amount of water in pool in US Gallon to
fill the pool according to filling rate for the specific amount of time in minutes : 89.77

You have entered in-valid value for current amount of water!
Enter current amount of water value from 0 to maximum capacity of pool      89.77 in US Gallon :

************************************************************************************************

The problem is that the internal value stored in totalPoolCapacity is 89.76624 and due to setprecision(2) it rounds of the value to 89.77 so when I enter 89.77 it doesn't accept it as a right value although it should be right value according to display message.问题是存储在totalPoolCapacity中的内部值是 89.76624 并且由于 setprecision setprecision(2)它将值四舍五入到 89.77 所以当我输入 89.77 它不接受它作为正确的值虽然它应该是正确的值根据显示信息。 I don't want to show whole value to user.我不想向用户展示全部价值。

Also please explain how to handle this calculation with setprecision(2) (totalPoolCapacity - poolCurrWaterAmount) / fillingRate另请说明如何使用 setprecision setprecision(2) (totalPoolCapacity - poolCurrWaterAmount) / fillingRate处理此计算

and what will be good EPSILON value to compare floating point numbers.以及比较浮点数的良好EPSILON值。

therefore time calculated and shown to user will not effect the overall calculation with rounding effects.因此,计算并显示给用户的时间不会影响具有舍入效果的整体计算。 That is what user sees, the program behaves according to that by manipulating internal representation of floating point numbers and their rounding off effects.这就是用户所看到的,程序通过操纵浮点数的内部表示及其四舍五入效果来根据该行为行事。

You should never do a floating point comparison like poolCurrWaterAmount <= totalPoolCapacity .您永远不应该进行像poolCurrWaterAmount <= totalPoolCapacity这样的浮点比较。

Instead you should do (poolCurrWaterAmount - totalPoolCapacity) < epsilon .相反,您应该这样做(poolCurrWaterAmount - totalPoolCapacity) < epsilon

In your case, epsilon should be 0.005.在您的情况下,epsilon 应该是 0.005。

In general, for an equality operator, epsilon could be as small as DBL_EPSILON .通常,对于相等运算符,epsilon 可以像DBL_EPSILON一样小。

For a deep-dive into this topic, including more rigorous algorithms, see comparing-floating-point-numbers-2012-edition .要深入了解该主题,包括更严格的算法,请参阅compare-floating-point-numbers-2012-edition

One thing you could do is call std::fesetround(FE_DOWNWARD);您可以做的一件事是调用std::fesetround(FE_DOWNWARD); so that your displayed numbers get rounded downwards rather than upwards.以便您显示的数字向下舍入而不是向上舍入。 That would make it so that when the user re-enters the rounded value he saw in your output, the entered value is slightly less than the actual capacity of the pool, rather than slightly more, and would therefore avoid triggering your error message.这样当用户重新输入他在 output 中看到的四舍五入值时,输入的值会略小于池的实际容量,而不是略多一些,因此可以避免触发您的错误消息。

Or, if you don't like that approach, you could alternatively just set poolCurrWaterAmount = std::min(poolCurrWaterAmount, totalPoolCapacity);或者,如果您不喜欢这种方法,您也可以设置poolCurrWaterAmount = std::min(poolCurrWaterAmount, totalPoolCapacity); instead of emitting an error message, so that if the user enters a value greater than the pool's capacity, it is treated as if he entered a value equal to the pool's capacity.而不是发出错误消息,因此如果用户输入的值大于池的容量,则将其视为输入的值等于池的容量。

For the first question:对于第一个问题:

The problem is that the internal value stored in totalPoolCapacity is 89.76624 and due to setprecision(2) it rounds of the value to 89.77 so when I enter 89.77 it doesn't accept it as a right value although it should be right value according to display message.问题是存储在 totalPoolCapacity 中的内部值是 89.76624 并且由于 setprecision(2) 它将值四舍五入到 89.77 所以当我输入 89.77 它不接受它作为正确的值虽然它应该是正确的值根据显示信息。 I don't want to show whole value to user.我不想向用户展示全部价值。

you can try set the poolCurrentWaterAmount to totalPoolCapacity if the user enters a value that is equal to the rounded value of totalPoolCapacity , for example:如果用户输入的值等于totalPoolCapacity的四舍五入值,您可以尝试将poolCurrentWaterAmount设置为totalPoolCapacity ,例如:

#include <iostream>
#include <iomanip>
#include <sstream>

double round_double_value(double val, int prec) {
    std::stringstream strstream;
    strstream << std::fixed << std::setprecision(prec) << val;
    double result;
    strstream >> result;
    return result;
}

int main()
{
    const double CUBIC_FOOT_TO_USGALLON = 7.48052 ;
    const double EPSILON = 0.0001 ;

    double length = 3.0;
    double width = 2.0;
    double depth = 2.0;
    double fillingRate = 4.0;

    double totalPoolCapacity = length * width * depth * CUBIC_FOOT_TO_USGALLON ;
    int out_precision = 2;
    std::cout << std::fixed << std::setprecision(out_precision);
    std::cout << "Total pool capacity = " << totalPoolCapacity << " US Gallon.\n" ;

    double poolCurrWaterAmount = 89.77;
    std::cout << "You entered current pool water amount = " << poolCurrWaterAmount << '\n';

    if ((poolCurrWaterAmount > totalPoolCapacity)
        && (poolCurrWaterAmount == round_double_value(totalPoolCapacity, out_precision)) ) {
        // Assume the user meant to input the maximum..
        poolCurrWaterAmount = totalPoolCapacity;
    }

    if( !(poolCurrWaterAmount >= 0.0
            &&  poolCurrWaterAmount <= totalPoolCapacity ) )
    {
        std::cout << "You have entered in-valid value for current amount of water!\n";
        return(1);
    }

    return 0;
}

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

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