Reputation: 1501
What is a modern way to map constant value doubles to integers?
I want this as a header only include.
I have avoided #define
and started with a nest of if
statements.
I believe the if
statements were faster ( I had them prioritized and a few goto
s in there) but the switch below seems more readable.
I looked at dis-assembly of code below and it seemed reasonable.
I do not like the giant switch. I thought there may be a better modern approach (without loosing performance or requiring linking).
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;
}
}
Upvotes: 0
Views: 263
Reputation:
Regardless of how you actually want to use the data, I imagine it's best to first store it in some simple, exact form:
static constexpr int GaugeToInchScale = 10000;
static constexpr std::array<int, 34> GaugeToInchScaledData = { 2391, 2242, ..., 67 };
static constexpr int GaugeToInchFirstGauge = 3;
Then, any functions to use this data would be implemented in terms of the constants in whichever sense was most natural.
And if needed, other tables could be synthesized from this data — even as compile time constants if desired.
Here is a simplistic example of a compile time algorithm that just generates a linear chain of if
statements that test for equality. The point of this example isn't to make an efficient algorithm, but to demonstrate how to write algorithms of this type.
// 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); }
Upvotes: 2
Reputation: 394
I have compared switch method, unorder_map and binary_search (I ignored the *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;
}
I have executed them for several times.
So just use the switch/case...
Edited
I try to split the cpp file (to prevent some optimaztions) and make the objects static.
The gap is still huge but becomes smaller.
The running time:
Upvotes: 1
Reputation: 145249
Doing a header-only implementation of something, what you think is a problem, is not problematic.
Comparing fractional floating point values for equality, which the code shows you think is unproblematic, is the main problem here.
The function name InchtoGauge
suggests that the ill-fated comparisons are meant to just be an implementation of a simple function. You should just compute that function. It looks like this (you can just interpolate on a few values):
In passing, evidently the large number of values in the code have just been computed from 5 straight line segments. At least it looks that way to me. This means you can get by with far fewer values than you have, to define the function.
Upvotes: 2