【问题标题】:Modern C++ way to map constant doubles to integers将常量双精度数映射到整数的现代 C++ 方法
【发布时间】:2015-10-12 13:56:07
【问题描述】:

将常量值双精度数映射到整数的现代方法是什么?

我希望这仅作为标题包含。

我避开了#define,并从一组if 语句开始。

我相信if 语句更快(我将它们设置为优先级并在其中添加了一些gotos),但下面的开关似乎更具可读性。

我看了下面代码的反汇编,看起来很合理。

我不喜欢巨型开关。我认为可能有更好的现代方法(不会失去性能或需要链接)。

inline int InchtoGauge(double d)
{
    switch (static_cast<int>(d * 10000))
    {
    case 2391: return 3;
    case 2242: return 4;
    case 2092: return 5;
    case 1943: return 6;
    case 1793: return 7;
    case 1644: return 8;
    case 1495: return 9;
    case 1345: return 10;
    case 1196: return 11;
    case 1046: return 12;
    case 897: return 13;
    case 747: return 14;
    case 673: return 15;
    case 598: return 16;
    case 538: return 17;
    case 478: return 18;
    case 418: return 19;
    case 359: return 20;
    case 329: return 21;
    case 299: return 22;
    case 269: return 23;
    case 239: return 24;
    case 209: return 25;
    case 179: return 26;
    case 164: return 27;
    case 149: return 28;
    case 135: return 29;
    case 120: return 30;
    case 105: return 31;
    case 97: return 32;
    case 90: return 33;
    case 82: return 34;
    case 75: return 35;
    case 67: return 36;
    default:return -1;
    }

}

【问题讨论】:

  • 将映射存储为排序数组并使用二进制搜索执行查找(例如std::lower_bound)?
  • 关联数组,std::unordered_map 或类似的。如果没有太多,我确信映射将是唯一的,没有冲突,它会表现得很好。总比一个巨大的开关好。
  • 对于值数组,我建议使用数组。这里的主要问题是浮点值很少在相等性上匹配
  • 你真的想让InchtoGauge(0.1)返回-1吗?

标签: c++ c++11 dictionary


【解决方案1】:

无论您实际上希望如何使用数据,我认为最好先以某种简单、准确的形式存储它:

static constexpr int GaugeToInchScale = 10000;
static constexpr std::array<int, 34> GaugeToInchScaledData = { 2391, 2242, ..., 67 };
static constexpr int GaugeToInchFirstGauge = 3;

然后,任何使用这些数据的函数都将根据最自然的常量来实现。

如果需要,可以从这些数据中合成其他表 - 如果需要,甚至可以使用 as compile time constants


这是一个编译时算法的简单示例,它只生成一个线性链 if 语句来测试相等性。这个例子的目的不是要制作一个高效的算法,而是演示如何编写这种类型的算法。

// searcher<N>::search(x) assumes `x` doesn't appear among the first `N` entries
template< int N >
struct searcher {
    constexpr static int search(int x) {
        if (x == GaugeToInchScaledData[N]) {
            return N + GaugeToInchFirstGauge;
        }
        return searcher<N+1>::search(x);
    }
};

template<>
struct searcher<GaugeToInchScaledData.size()>
{
    constexpr static int search(int x) {
        return -1;
    }
};

int search(int n) { return searcher<0>::search(n); }

【讨论】:

    【解决方案2】:

    做一个只有标题的实现,你认为是个问题,没有问题。

    比较分数浮点值是否相等,代码显示您认为这没有问题,这是这里的主要问题。

    函数名称InchtoGauge 表明命运多舛的比较只是一个简单函数的实现。你应该只计算那个函数。它看起来像这样(您可以只插入几个值):

    顺便说一句,显然代码中的大量值是从 5 条直线段中计算出来的。至少在我看来是这样的。这意味着您可以使用比您拥有的更少的值来定义函数。

    【讨论】:

    • 不幸的是,英寸测量不是一个简单或定义明确的函数。制造业的大多数部分都使用查找表来查找价值。并不意味着它不能以不同的方式实现。
    • @JoeMcGrath:您尝试实现的功能很简单。我已经添加了它的图表。
    【解决方案3】:

    我比较了switch方法,unorder_map和binary_search (我忽略了 *10000)

    int InchtoGauge2(int d) {
        const int lut[] = {
            2391, 2242, 2092, 1943, 1793,
            1644, 1495, 1345, 1196, 1046,...
        };
        const int N = sizeof(lut)/sizeof(lut[0]);
        const int n = lower_bound(lut, lut+N, d, greater<int>()) - lut;
        return (n==N || lut[n]!=d)? -1: 3+n;
    }
    
    int InchtoGauge3(int d)
    {
        unordered_map<int,int> m{
            {2391, 3},
            {2242, 4},
            {2092, 5},...
        };
        auto p = m.find(d);
        return p == m.end()? -1: p->second;
    }
    

    我已经执行了好几次了。

    • 开关/外壳 228us
    • binary_search 12913us
    • unordered_map 1307857us

    所以只需使用开关/外壳...

    已编辑

    我尝试拆分 cpp 文件(以防止某些优化) 并使对象成为静态的。

    差距仍然很大,但会变小。

    运行时间:

    • 开关/外壳 x
    • binary_search 15x
    • unordered_map 25x

    【讨论】:

    • 感谢您的意见。我发现我的大部分旧习惯都更快。我决心在不慢的情况下找到使用现代 c++ 的方法。
    • 你的编译器标志是什么?您是否正在使用优化进行编译?也值得展示你是如何做计时的;获得准确的时间是一项令人惊讶的非平凡任务,而且您可能正在做一些扭曲结果的事情。
    • 另外,对使用它的真实程序进行计时会有所帮助;有时事物(尤其是展开循环或查找表之类的事物)在单独测试时的性能比在实际使用场景中的性能要好得多。
    • 关于查找表的主题,考虑创建一个char lut[2392],其中包含在d &lt; 2392 时返回的结果。 (如果你也设置了unsigned int参数而不是int,那么你就不用测试是否d &lt; 0了)
    • @Hurkyl:我用g++ -O2 编译它。我通过运行for (i = -10; i &lt; 10010; ++i) 来测试片段,因为原始问题中没有描述实际用法。
    猜你喜欢
    • 1970-01-01
    • 2011-06-18
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多