6

I have to keep one double value cached. After it is used, it should be invalidated. Two alternatives

One is to add boolean flag, true when cached value is good, when it is used set it to false, and when flag is false, recalculate and refill.

Second one is more interesting - I could keep it as double value and use NaN as invalid/need to recalc flag.

double get() const {
    if (!isnan(_value)) {
        double t = _value;
        _value = std::numeric_limits<double>::quiet_NaN;
        return t;
    }
}

Any objections against it? Any thoughts on efficiency?

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
  • 4
    Is this a C question or a C++ question? The answers are totally different for the two languages. – David Schwartz Sep 24 '15 at 19:14
  • 5
    Key question: is NaN ever a reasonable value to set the cache to? That's always the key issue with using flag values - if the value can ever be set to the flag during normal operation, you'll have a real pain-in-the-tush bug to deal with. – Erik Johnson Sep 24 '15 at 19:15
  • With a status flag, you could possibly go beyond "valid" and "not valid" into meanings such as "internal default value", "system default value", "user configured default", "operator override value", etc., etc. – wallyk Sep 24 '15 at 19:17
  • 1
    "Efficiency" is probably not as big a concern as algorithm understandability, reliability, and appropriateness for the solution. – wallyk Sep 24 '15 at 19:19
  • if you have 2 states, a flag should be fine – Grady Player Sep 24 '15 at 19:19
  • 3
    @DavidSchwartz As much as I dislike confusing C and C++, I don't see how the answers would be totally different here. The only aspect I can see is that C++ would allow me to test at compile-time whether `std::numeric_limits::has_quiet_NaN` and act upon that information. But I guess most people would take IEEE754 compliance granted these days. Are there any other differences you had in mind? – 5gon12eder Sep 24 '15 at 19:22
  • Boost optional can help. But that's c++. I believe that it migrates to the c++ standard library in the next revision. I'm always keen to avoid ieee754 assumptions. – Bathsheba Sep 24 '15 at 19:26
  • @DavidSchwartz it is more of the C++ question, though, I would say, with some literal NaN value instead of std::numeric_limits, C version would be 99% equivalent to C++ – Severin Pappadeux Sep 24 '15 at 19:29
  • So what is `get()` returning in case that `_value` is `NaN`? – Markus Mayr Sep 24 '15 at 19:38
  • @MarkusMayr Actual calculation computes two values. One is returned immediately, another is stored for the next get() call. So it is even/odd pattern – Severin Pappadeux Sep 24 '15 at 19:41
  • 1
    If you do care about efficiency, be aware that using NaNs massively hurts performance on x86 and x64 architectures. Every time a NaN comes onto or is touched in a register, the FPU stalls for about twenty cycles (I guess because it triggers a microcoded subroutine or something). In math-heavy apps, this can add up to a huge cost. I saw a profile once where fully 10% of the CPU time was just spent on NaN stalls. – Crashworks Sep 24 '15 at 20:34
  • @Crashworks thank you, I was aware about slowdown on i87, though I thought on X64 using only SSE2/AVX register this problem is already cured. Have to check... – Severin Pappadeux Sep 25 '15 at 02:52

3 Answers3

6

use the boolean otherwise you'll end up with some interesting problems/bugs down the road when your calculated double actually turns out to be a NaN (due to calculation). If you rely on NaN as a signal for "I have used that value" then you won't be able to distinguish in case of a "valid" unused NaN.

Not to mention that such semantics overload will cause a future reader of your code (even yourself a few month from now) to scratch his head in attempts to decipher that clever use. ;-)

In general it is a bad practice to overload the meaning of a variable. It may seem cute at first but it will inevitably cause more harm down the road.

As far as efficiency goes - I would really recommend you first measure and only then worry about optimization. I will bet you that once you run the tests you'll find that the difference in speed is well below the performance noise caused by the CPU temperature fluctuations.

YePhIcK
  • 5,816
  • 2
  • 27
  • 52
3

I doubt there would be difference with efficiency, but code with boolean flag would be more readable:

double get() const {
    if (!_cached)
        _value = recalculate();
    _cached = !_cached;
    return _value;
}
Slava
  • 43,454
  • 1
  • 47
  • 90
0

I think it is worth pointing out that the nan can be the more efficient solution, the big advantage being that you are using less memory than with the flag (whose overhead will probably be more than 1 byte per double, because of alignment). This means it will also likely be faster if you need to read a lot of memory.

Another thing that should pointed out is that IEEE nans can have different values. Their exponents are required to be all ones, but the mantissa can be anything except all zeros. This means that you could use a "special" nan to distinguish from the ones produced as a result of computation, or even use different kinds of nans if you need a flag with more than two states.

toth
  • 2,519
  • 1
  • 15
  • 23
  • Problem with that approach is that the only available standard function to check is `isnan()` and friends. If there are really different NaN patterns to use, I have to write `is_that_NaN()`, `is_it_another_NaN()` etc. But thanks for the answer – Severin Pappadeux Sep 24 '15 at 19:38
  • @SeverinPappadeux, that is true but it is a very short function to write. memcmp(&my_double, &my_special_nan, sizeof(double)) !=0 will do the trick. – toth Sep 24 '15 at 19:39