nick
nick

Reputation: 862

How to keep only 1 effective digit in c++

I want to keep 1 digit for my number.

For example:

3414.1 -> 3000
212 -> 200
12.1 -> 10
0.787 -> 0.7

I can come up with an idea of using std::string, that will transfer double into std::string, then transfer it back after handling.

but i think it will be slow, is there any good method i can use?

Upvotes: 0

Views: 267

Answers (2)

bolov
bolov

Reputation: 75688

Here is my version. If you care about performance you should benchmark. You didn't specify how to handle negative values so I assumed you "keep" the digit.

#include <concepts>
#include <cmath>

constexpr auto keep_msd(std::integral auto n)
{
    decltype(n) ten_pow = 1;

    while (n > 9 || n < -9)
    {
        n /= 10;
        ten_pow *= 10;
    }

    return n * ten_pow;
}

template <class F>
    requires std::floating_point<F>
constexpr F keep_msd(F f)
{
    if (!std::isfinite(f))
        return f;

    if (f == 0.0)
        return 0.0;

    if (f >= 1 || f <= -1)
        return static_cast<F>(keep_msd(static_cast<long long>(f)));

    F sig = f < 0 ? -1.0 : 1.0;
    f *= sig;

    F ten_pow = 1.0;
    
    while (f <= 1)
    {
        f *= static_cast<F>(10.0);
        ten_pow /= static_cast<F>(10.0);
    }

    return sig * static_cast<int>(f) * ten_pow;
}

Keep in mind that floating point math is broken so with that out of the way here is the test suite:

template <class F>
    requires std::floating_point<F>
constexpr bool are_almost_eq(F a, F b, F delta)
{
    return std::abs(a - b) <= delta;
}

static_assert(keep_msd(0) == 0);

static_assert(keep_msd(5) == 5);
static_assert(keep_msd(10) == 10);
static_assert(keep_msd(12) == 10);
static_assert(keep_msd(1'234) == 1'000);
static_assert(keep_msd(1'004) == 1'000);
static_assert(keep_msd(1'000) == 1'000);


static_assert(keep_msd(-5) == -5);
static_assert(keep_msd(-10) == -10);
static_assert(keep_msd(-12) == -10);
static_assert(keep_msd(-1'234) == -1'000);
static_assert(keep_msd(-1'004) == -1'000);
static_assert(keep_msd(-1'000) == -1'000);

static_assert(keep_msd(0.0) == 0.0);
static_assert(keep_msd(std::numeric_limits<double>::infinity()) ==
                std::numeric_limits<double>::infinity());
static_assert(keep_msd(-std::numeric_limits<double>::infinity()) ==
                -std::numeric_limits<double>::infinity());


static_assert(keep_msd(1.2) == 1.0);
static_assert(keep_msd(1.2) == 1.0);
static_assert(keep_msd(1.002) == 1.0);
static_assert(keep_msd(1'000.0004) == 1'000.0);
static_assert(keep_msd(1'002.3004) == 1'000.0);

static_assert(keep_msd(-1.2) == -1.0);
static_assert(keep_msd(-1.2) == -1.0);
static_assert(keep_msd(-1.002) == -1.0);
static_assert(keep_msd(-1'000.0004) == -1'000.0);
static_assert(keep_msd(-1'002.3004) == -1'000.0);

static_assert(are_almost_eq(keep_msd(0.1), 0.1, 0.000'000'1));
static_assert(are_almost_eq(keep_msd(0.3), 0.3, 0.000'000'1));
static_assert(are_almost_eq(keep_msd(0.123456), 0.1, 0.000'000'1));
static_assert(are_almost_eq(keep_msd(0.000'001), 0.000'001, 0.000'000'001));

static_assert(are_almost_eq(keep_msd(-0.1), -0.1, 0.000'000'1));
static_assert(are_almost_eq(keep_msd(-0.3), -0.3, 0.000'000'1));
static_assert(are_almost_eq(keep_msd(-0.123456), -0.1, 0.000'000'1));
static_assert(are_almost_eq(keep_msd(-0.000'001), -0.000'001, 0.000'000'001));

Upvotes: 1

S4eed3sm
S4eed3sm

Reputation: 1478

You should find length of the number, for this we can use log10 function: ceil(log10(3414)) = 4, but we want most significant value then we use floor(log10(3414)) = 3, then we calculate like this: (3414 / 1000) * 1000 = 3000. Here complete solution:

#include <iostream>
#include <cmath>
template <typename T>
T f(T x) {
    if(x == 0) return 0;
    if(x < 0) return -f(-x);
    if(x < 1) {
        return f(x*10)/10.0;
    }
    auto t = pow(10, floor(log10(ceil(x))));
    return long(x/t) * t;
}

int main() {
    std::cout << "f(3414.1) -> " << f(3414.1) << '\n';
    std::cout << "f(212) -> " << f(212) << '\n';
    std::cout << "f(12.1) -> " << f(12.1) << '\n';
    std::cout << "f(0.787) -> " << f(0.787) << '\n';
    std::cout << "f(7.87) -> " << f(7.87) << '\n';
    std::cout << "f(70.87) -> " << f(70.87) << '\n';
    std::cout << "f(-0.787) -> " << f(-0.7) << '\n';
    std::cout << "f(-3414.1) -> " << f(-3414.1) << '\n';
    std::cout << "f(-212) -> " << f(-212) << '\n';
}

The output will be:

f(3414.1) -> 3000
f(212) -> 200
f(12.1) -> 10
f(0.787) -> 0.7
f(7.87) -> 7
f(70.87) -> 70
f(-0.787) -> -0.7
f(-3414.1) -> -3000
f(-212) -> -200

Upvotes: 1

Related Questions