简体   繁体   中英

Wrapping floats counter in C++

This question refers to this one , but applying to floating point numbers too.

In short, if count reaches min or max bound it should wrap to opposite bound taking into account remainder of step value overflow (for instance if bounds are 0... 10.5 , value is 3 and step is -7.5). Also we should handle case when step value is greater than range value.

Ranges may be arbitrary (negative, positive or mixed) but taking into account given asserts.

So far I have this method which works for integers, but gives wrong result for doubles whenever step is fractional.

Also not sure we should use epsilon approach here while comparing doubles.

Question is how to implement this method properly for fractional values.

Runnable code

#include <iostream>
#include <cmath>
#include <assert.h>

double
countInRange(const double step, const double value, const double minValue, const double maxValue)
{
    assert(minValue < maxValue);
    assert(value >= minValue && value <= maxValue);

    auto range = maxValue - minValue + 1;
    assert(fabs(step) <= range);

    auto result = value + step;

    if (result < minValue) {
        result += range;
    } else if (result > maxValue) {
        result -= range;
    }

    return result;
}

int main()
{
    double v = 358;

    printf("increment\n");

    for (int i = 0; i < 10; i++) {
        v = countInRange(0.5, v, 0, 359.5);
        printf("%f\n", v);
    }

    printf("decrement\n");

    v = 2;

    for (int i = 0; i < 10; i++) {
        v = countInRange(-0.5, v, 0, 359.5);
        printf("%f\n", v);
    }
}

Edit

So I ended up with two methods for different interval logics, the first one taken from answer of Superlokkus, they may not cover all cases, but for now it's enough, thanks to all the commenters

    /**
     * Wraps to minValue or maxValue plus step using half-open interval logic
     */
    double
    countInRange(const double step, const double value, const double minValue, const double maxValue)
    {
        assert(minValue < maxValue);
        assert(value >= minValue && value <= maxValue);

        auto range = maxValue - minValue + 1;

        assert(fabs(step) <= range);

        auto result = value + step;

        if (result < minValue) {
            result = maxValue - abs(minValue - result);
        } else if (result > maxValue) {
            result = minValue + (result - maxValue);
        }

        return result;
    }
    /**
     * Wraps to minValue or maxValue using closed interval logic
     */
    double
    countInRange(const int step, const double value, const double minValue, const double maxValue)
    {
        assert(minValue < maxValue);
        assert(value >= minValue && value <= maxValue);

        auto range = maxValue - minValue + 1;

        assert(abs(step) <= range);

        auto result = value + step;

        if (result < minValue) {
            result += range;
        } else if (result > maxValue) {
            result -= range;
        }

        return result;
    }

Is this what you had in mind?

#include <iostream>
#include <iomanip>
#include <stdexcept>
#include <cmath>

constexpr double
countInRange(const double step, const double value, const double minValue, const double maxValue)
{
    if (minValue > maxValue) {
        throw std::invalid_argument("minValue > maxValue");
    }

    auto result{value};
    result += step;

    if (result > maxValue) {
        result = minValue + (result - maxValue);
    } else if (result < minValue) {
        result = maxValue - std::abs(minValue - result);
    }

    return result;
}

int main()
{
    double v = 358;

   std::cout << std::showpoint <<"increment\n";

    for (int i = 0; i < 10; i++) {
        v = countInRange(0.5, v, 0, 359.5);
        std::cout << v << "\n";
    }

    std::cout << std::showpoint << "decrement\n";

    v = 2;

    for (int i = 0; i < 10; i++) {
        v = countInRange(-0.5, v, 0, 359.5);
        std::cout << v << "\n";
    }
}

Output:

increment 358.500 359.000 359.500 0.500000 1.00000 1.50000 2.00000 2.50000 3.00000 3.50000 decrement 1.50000 1.00000 0.500000 0.00000 359.000 358.500 358.000 357.500 357.000 356.500

I removed the assert, since they are not suitable for argument checking, since they are non portable deterministic left out, and can't be handled. Also since you seem to intend to write C++, I corrected the output usage to best C++ pratice.

You could use pointers to float variables in function arguments. This will allow to avoid correction of values while transferring them via stack.

This way looks a little bit dirty but if you want to save precision of arguments it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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