0

As an input I have two parameters:

int64_t value
signed char scale

I need to get a double that's equal to value * 10^scale

would

static_cast<double>(value) * pow(10, scale)

be the best way to do this?

Roman Goyenko
  • 6,965
  • 5
  • 48
  • 81
  • 2
    [Don't use `pow` for integer powers](https://stackoverflow.com/questions/15851636/why-is-my-integer-math-with-stdpow-giving-the-wrong-answer) – NathanOliver Apr 27 '23 at 18:32
  • 1
    convert both value and scale to doubles first. Since scale only can have 255 values consider making a compile time constant table (constexpr std::array) with precalculated powers of 10 (no need to keep calculating them over and over again at runtime) – Pepijn Kramer Apr 27 '23 at 18:33
  • A cpp newbie here, this was an edit :) And as a newbie - what would be the best wat to populate said compile time constant table (besides having 256 values)? – Roman Goyenko Apr 27 '23 at 18:45
  • Made an example for you :) Also a reminder your question is basically asking for a recommendation because "best" can depend on requirements. e.g. speed vs. memory usage. And usually these kind of "opinion" based answers are not the best to ask here. – Pepijn Kramer Apr 27 '23 at 18:50
  • Changed the question to "fastest". Thank you for the example! – Roman Goyenko Apr 27 '23 at 18:58
  • @NathanOliver I wonder if that's appropriate advice. The linked-to problem is due to a poor implementation of `pow`; it's not something inherent in `pow`. To make a general statement, one would have to survey available implementations and assess how well integer powers are handled. As the reported problem is some years old at this point, it seems like it might be out of date at this point. – Robert Dodier Apr 27 '23 at 19:22
  • 1
    OP, my pretty strong advice to work first towards correctness and think about speed afterwards. The time spent doing the stated operation is likely negligible unless it's in a very tight loop. Even if you really do need to optimize this, you need a reference implementation to tell whether or not you have inadvertently changed the results in the optimized version. – Robert Dodier Apr 27 '23 at 19:27

1 Answers1

2

With a compile time precalculated scale table it would look like this :

#include <array>
#include <cstdint>
#include <cmath>
#include <iostream>

// I use int8_t instead of char since char can be 
// signed or unsigned dependent on platform

namespace init
{
    // https://en.cppreference.com/w/cpp/language/constexpr
    constexpr auto create_power_table()
    {
        // fill an array with 1e-128 to 1e128 values
        std::array<double, 256ul> powers{};
        
        double value = 1.0;
        for (std::size_t n = 0; n < 128; ++n)
        {
            powers[128 - n] = value;
            value /= 10.0;
        }

        value = 1.0;
        for (std::size_t n = 0; n < 127; ++n)
        {
            powers[128 + n] = value;
            value *= 10.0;
        }

        return powers;
    }
}

// create a compile time table with double scale values
constexpr std::array<double, 256> power_table = init::create_power_table();

constexpr double convert(std::int64_t value, std::int8_t scale)
{
    // calculate the index in the table
    std::size_t index = static_cast<std::size_t>(scale) + 128ul;
    return static_cast<double>(value) * power_table[index];
};


int main()
{
    std::cout << convert(1, -120) << "\n";
    std::cout << convert(1, -1) << "\n";
    std::cout << convert(1, 1) << "\n";
    std::cout << convert(1, 2) << "\n";
    std::cout << convert(1, 120) << "\n";
    
    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19