数字类型介于0和1之间

Number type between 0 and 1

本文关键字:之间 类型 数字      更新时间:2023-10-16

有没有一种自然的方法可以定义0和1之间的数字类型?本质上,一个人可以用double做任何事情,问题是我应该在一些函数中定义一些绑定检查,我有一个(可能很愚蠢)想法,把绑定检查外包给一个类,比如

class Probability {
    // the value of the Probability
    double val;
    Probability(double val):value(val){
    // freak out if val > 1 or val < 0
    //...
    //
    };
    // operators such as
    Probability operator + (Probability const & a, Probability const & b){
        double result a.val + b.val;
        if ((result > 1) || (result < 0)){
            // freak out
            result = 0
        }
        return result;
    }
    // ...
    //
}

这种方法的问题可能是它减慢了每次操作的速度。有没有更快的绑定检查方法?我也想知道如何处理上面代码中的"反常"部分。

您可以使用类Probability来强制执行边界,在内部存储一个double。如果希望P(0.75) + P(0.5) - P(0.6)之类的操作起作用,可以让操作符返回一个不进行边界检查的代理对象。这个代理对象将有一个到Probability的转换运算符,Probability构造函数将检查边界。如果您只直接使用Probability类型,并允许形式为ProbabilityResultProxy的临时性,那么您将获得所需的行为。

下面的示例概述了这种方法。很明显,在实际实现中缺少了很多您想要的东西,但我想专注于一个特定的解决方案,而不是提供一个完整的类。

实例:

#include <iostream>
class Probability {
  public:
    Probability(double value) {
        if (value < 0 || value > 1) throw std::runtime_error("Invalid probability");
        value_ = value;
    }
    double value() const { return value_; }
  private:
    double value_;
};
class ProbabilityResultProxy {
  public:
    explicit ProbabilityResultProxy(double p) : value_(p) {}
    double value() const { return value_; }
    operator Probability() {
        return Probability(value_);
    }
  private:
    double value_;
};
ProbabilityResultProxy operator+(const Probability& lhs, const Probability& rhs) {
    return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator+(const ProbabilityResultProxy& lhs, const Probability& rhs) {
    return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator+(const Probability& lhs, const ProbabilityResultProxy& rhs) {
    return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator+(const ProbabilityResultProxy& lhs, const ProbabilityResultProxy& rhs) {
    return ProbabilityResultProxy(lhs.value() + rhs.value());
}
ProbabilityResultProxy operator-(const Probability& lhs, const Probability& rhs) {
    return ProbabilityResultProxy(lhs.value() - rhs.value());
}
ProbabilityResultProxy operator-(const ProbabilityResultProxy& lhs, const Probability& rhs) {
    return ProbabilityResultProxy(lhs.value() - rhs.value());
}
ProbabilityResultProxy operator-(const Probability& lhs, const ProbabilityResultProxy& rhs) {
    return ProbabilityResultProxy(lhs.value() - rhs.value());
}
ProbabilityResultProxy operator-(const ProbabilityResultProxy& lhs, const ProbabilityResultProxy& rhs) {
    return ProbabilityResultProxy(lhs.value() - rhs.value());
}
int main() {
    Probability p1(0.75);
    Probability p2(0.5);
    Probability p3(0.6);
    Probability result = p1 + p2 - p3;
    std::cout << result.value() << "n";
    try {
        Probability result2 = p1 + p2;
        std::cout << result2.value();
    } catch (const std::runtime_error& e) {
        std::cout << e.what() << "n";
    }
    return 0;
}

这里,为ProbabilityProbabilityResultProxy的每个组合定义数学运算符。每个操作都返回一个代理对象,最后的赋值会导致执行边界检查。


如果你愿意,你可以让ProbabilityResultProxy成为Probability的私有成员类,并让操作员成为Probability的朋友。这样可以防止任何人直接实例化代理类。

"反常"选项很简单:throw std::invalid_argument。投球有点慢并不重要;无论如何你都得不到答案。好处是不抛出很快,因为优化器可以假设非异常路径的可能性要大得多。

就性能而言,拥有一个IntermediateResult类可能是有益的,这样只会对返回到Probability的最终分配进行范围检查。这避开了0.75+0.5-0.6的例子。