Reputation: 862
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
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
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