假设用户输入的是一个小数,例如5.2155(有4位小数)。它可以自由地存储(int,double等)。
有没有聪明(或非常简单)的方法来找出这个数字有多少位小数呢?(有点像如何通过屏蔽最后一位来确定一个数字是偶数还是奇数的问题)。
有没有聪明(或非常简单)的方法来找出这个数字有多少位小数呢?(有点像如何通过屏蔽最后一位来确定一个数字是偶数还是奇数的问题)。
我知道两种方法,但都不是很聪明,这更多地是环境的限制,而不是我的过错 :-)
第一种是使用 "%.50f"
格式字符串将数字转换为大缓冲区,并删除尾随零,然后计算小数点后的字符数。这将受到 printf
系列本身的限制。或者您可以使用用户输入的字符串作为输入(而不是 sprintf
浮点值),以完全避免浮点问题。
第二种方法是减去整数部分,然后迭代乘以10,并再次减去整数部分,直到得到零。这被计算机表示浮点数的限制所限制-在每个阶段,您可能会出现无法精确表示的数字(因此.2155实际上可能是.215499999998)。像下面这样(未经测试,除了在我的脑海中,这与 COMX-35 差不多):
count = 0
num = abs(num)
num = num - int(num)
while num != 0:
num = num * 10
count = count + 1
num = num - int(num)
如果你知道待排序的数字类型(例如,它们都是小数点后0到4位的数字),你可以使用标准的浮点数“技巧”来正确地排序。例如,不要使用如下方式:
while num != 0:
使用
while abs(num) >= 0.0000001:
就我个人而言:
从小数部分开始:0.2155
反复乘以10并丢弃数字的整数部分,直到得到零。步骤的数量将是小数的位数。例如:
.2155 * 10 = 2.155
.155 * 10 = 1.55
.55 * 10 = 5.5
.5 * 10 = 5.0
4步 = 4位小数
类似这样的东西也可能适用:
float i = 5.2154;
std::string s;
std::string t;
std::stringstream out;
out << i;
s = out.str();
t = s.substr(s.find(".")+1);
cout<<"number of decimal places: " << t.length();
#include <stdio.h>
#include <string.h>
/* Counting the number of decimals
*
* 1. Use Scientific Notation format
* 2. Convert it to a string
* 3. Tokenize it on the exp sign, discard the base part
* 4. convert the second token back to number
*/
int main(){
int counts;
char *sign;
char str[15];
char *base;
char *exp10;
float real = 0.00001;
sprintf (str, "%E", real);
sign= ( strpbrk ( str, "+"))? "+" : "-";
base = strtok (str, sign);
exp10 = strtok (NULL, sign);
counts=atoi(exp10);
printf("[%d]\n", counts);
return 0;
}
[5]
string number = "543.014";
size_t dotFound;
stoi(number, &dotFound));
string(number).substr(dotFound).size()
stof(number) == stoi(number)
)。int
中,您可以计算它的小数位数。这是对Andrei Alexandrescu改进的改进。他的版本已经比朴素的方法快(在每个数字处除以10)。下面的版本是常数时间,至少在x86-64和ARM上对所有大小都更快,但它占用的二进制代码是原来的两倍,因此它不太友好缓存。此版本针对unsigned
而非signed
。请参考我在Facebook Folly
上的PR进行基准测试。inline uint32_t digits10(uint64_t v) {
return 1
+ (std::uint32_t)(v>=10)
+ (std::uint32_t)(v>=100)
+ (std::uint32_t)(v>=1000)
+ (std::uint32_t)(v>=10000)
+ (std::uint32_t)(v>=100000)
+ (std::uint32_t)(v>=1000000)
+ (std::uint32_t)(v>=10000000)
+ (std::uint32_t)(v>=100000000)
+ (std::uint32_t)(v>=1000000000)
+ (std::uint32_t)(v>=10000000000ull)
+ (std::uint32_t)(v>=100000000000ull)
+ (std::uint32_t)(v>=1000000000000ull)
+ (std::uint32_t)(v>=10000000000000ull)
+ (std::uint32_t)(v>=100000000000000ull)
+ (std::uint32_t)(v>=1000000000000000ull)
+ (std::uint32_t)(v>=10000000000000000ull)
+ (std::uint32_t)(v>=100000000000000000ull)
+ (std::uint32_t)(v>=1000000000000000000ull)
+ (std::uint32_t)(v>=10000000000000000000ull);
}
template <typename T>
std::enable_if_t<(std::is_floating_point<T>::value), std::size_t>
decimal_places(T v)
{
std::size_t count = 0;
v = std::abs(v);
auto c = v - std::floor(v);
T factor = 10;
T eps = std::numeric_limits<T>::epsilon() * c;
while ((c > eps && c < (1 - eps)) && count < std::numeric_limits<T>::max_digits10)
{
c = v * factor;
c = c - std::floor(c);
factor *= 10;
eps = std::numeric_limits<T>::epsilon() * v * factor;
count++;
}
return count;
}
每次迭代都会抛弃值,而是跟踪10的幂乘积以避免累积舍入问题。它使用机器ε(epsilon)来正确处理不能在二进制中精确表示的十进制数,例如问题中规定的5.2155的值。
double
类型,此代码返回6。 - Eric Postpischil根据其他人的写作,这对我很有效。这个解决方案确实处理了数字无法在二进制中精确表示的情况。
如其他人建议的那样,while循环的条件指示了精确的行为。我的更新使用机器epsilon值来测试任何循环上的余数是否可以由数字格式表示。测试不应与0或硬编码值(例如0.000001)进行比较。
template<class T, std::enable_if_t<std::is_floating_point_v<T>, T>* = nullptr>
unsigned int NumDecimalPlaces(T val)
{
unsigned int decimalPlaces = 0;
val = std::abs(val);
val = val - std::round(val);
while (
val - std::numeric_limits<T>::epsilon() > std::numeric_limits<T>::epsilon() &&
decimalPlaces <= std::numeric_limits<T>::digits10)
{
std::cout << val << ", ";
val = val * 10;
++decimalPlaces;
val = val - std::round(val);
}
return val;
}