假设用户输入的是十进制数,例如。 5. 2155 (有4位小数)。它可以自由存储(int,double)等。
是否有聪明(或非常简单)的方法来查明该数字的小数位数? (有点像问题,如何通过屏蔽最后一位来找到数字是偶数还是奇数)。
答案 0 :(得分:18)
我知道的两种方式,不幸的是不是很聪明,但这更像是对环境的限制而不是我: - )
第一个是将sprintf
数字转换为带有"%.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:
答案 1 :(得分:6)
一旦将数字从用户表示(字符串,OCR-ed gif文件等)转换为浮点数,您就不必处理相同的数字。所以严格的,不是非常有用的答案是“不”。
如果(案例A )您可以避免从字符串表示转换数字,问题就变得容易了,您只需要计算小数点后的数字并减去尾随零的数量
如果你不能这样做(案例B ),那么你需要假设最大小数位数,将数字转换回字符串表示形式并将其四舍五入到这个最大数字。 round-to-even method。例如,如果用户提供的1.1表示为1.09999999999999(假设),将其转换回字符串,则猜测“1.09999999999999”。将这个数字四舍五入到比如四个小数点可以得到“1.1000”。现在回到案例A 。
答案 2 :(得分:4)
脱离我的头顶:
从小数部分开始:.2155
重复乘以10并丢弃数字的整数部分直到你得到零。步数将是小数位数。 e.g:
.2155 * 10 = 2.155
.155 * 10 = 1.55
.55 * 10 = 5.5
.5 * 10 = 5.0
4步= 4位小数
答案 3 :(得分:2)
你是什么意思“自由存储(int”?一旦存储在int中,它剩下零小数,显然。一个double以二进制形式存储,因此与“decimal”没有明显或简单的关系。为什么在将输入发送到最终的数字变量目的地之前,不要将输入保持为字符串,只要足够长以计算小数位数吗?
答案 4 :(得分:2)
这样的事情也可能有效:
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();
答案 5 :(得分:1)
使用科学记数法格式(以避免舍入错误):
#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]
答案 6 :(得分:1)
战斗结束后的几年,但我已经用三条线做出了自己的解决方案:
<button type="button" class="btn btn-default" data-toggle="tooltip"
data-placement="left" title="Tooltip on left">Tooltip on left</button>
当然,你必须先测试它是否真的是浮动的
(例如string number = "543.014";
size_t dotFound;
stoi(number, &dotFound));
string(number).substr(dotFound).size()
)
答案 7 :(得分:1)
由于目标是要快,所以这是对andrei alexandrescu's improvement的改进。他的版本已经比幼稚的方式快(每位数除以10)。下面的版本是恒定时间的,至少在所有尺寸的x86-64和ARM上都更快,但是占用的二进制代码数量是后者的两倍,因此它对缓存的友好程度不高。
我的PR on facebook folly上的该版本与亚历山大版本的基准。
在unsigned
上工作,而不在signed
上工作。
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);
}
答案 8 :(得分:0)
我建议将值作为字符串读取,搜索小数点,并将其前后的文本解析为整数。没有浮点或舍入错误。
答案 9 :(得分:0)
char* fractpart(double f)
{
int intary={1,2,3,4,5,6,7,8,9,0};
char charary={'1','2','3','4','5','6','7','8','9','0'};
int count=0,x,y;
f=f-(int)f;
while(f<=1)
{
f=f*10;
for(y=0;y<10;y++)
{
if((int)f==intary[y])
{
chrstr[count]=charary[y];
break;
}
}
f=f-(int)f;
if(f<=0.01 || count==4)
break;
if(f<0)
f=-f;
count++;
}
return(chrstr);
}
答案 10 :(得分:0)
这是完整的程序
#include <iostream.h>
#include <conio.h>
#include <string.h>
#include <math.h>
char charary[10]={'1','2','3','4','5','6','7','8','9','0'};
int intary[10]={1,2,3,4,5,6,7,8,9,0};
char* intpart(double);
char* fractpart(double);
int main()
{
clrscr();
int count = 0;
double d = 0;
char intstr[10], fractstr[10];
cout<<"Enter a number";
cin>>d;
strcpy(intstr,intpart(d));
strcpy(fractstr,fractpart(d));
cout<<intstr<<'.'<<fractstr;
getche();
return(0);
}
char* intpart(double f)
{
char retstr[10];
int x,y,z,count1=0;
x=(int)f;
while(x>=1)
{
z=x%10;
for(y=0;y<10;y++)
{
if(z==intary[y])
{
chrstr[count1]=charary[y];
break;
}
}
x=x/10;
count1++;
}
for(x=0,y=strlen(chrstr)-1;y>=0;y--,x++)
retstr[x]=chrstr[y];
retstr[x]='\0';
return(retstr);
}
char* fractpart(double f)
{
int count=0,x,y;
f=f-(int)f;
while(f<=1)
{
f=f*10;
for(y=0;y<10;y++)
{
if((int)f==intary[y])
{
chrstr[count]=charary[y];
break;
}
}
f=f-(int)f;
if(f<=0.01 || count==4)
break;
if(f<0)
f=-f;
count++;
}
return(chrstr);
}
答案 11 :(得分:0)
这是适用于float
和double
类型的健壮的C ++ 11实现:
#include <type_traits>
#include <cstdlib>
#include <cmath>
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;
while (c > 0 && count < std::numeric_limits<T>::max_digits10)
{
c = v * factor;
c = c - std::floor(c);
factor *= 10;
count++;
}
return count;
}
它会在每次迭代时丢弃该值,而是跟踪10乘方的幂,以避免出现四舍五入的问题。
答案 12 :(得分:0)
{'access_token': '1234',
'expires_in': 28800,
'refresh_token': '5678',
'scope': ['heartrate',
'profile',
'settings',
'nutrition',
'location',
'weight',
'activity',
'sleep',
'social'],
'token_type': 'Bearer',
'user_id': 'abcd',
'expires_at': 1611455442.4566112}
答案 13 :(得分:0)
根据其他人写的内容,这对我来说效果很好。此解决方案确实处理无法以二进制精确表示数字的情况。
正如其他人所建议的,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;
}
举个例子,如果输入值为 2.1,正确的解是 1。但是,如果使用双精度,这里发布的一些其他答案会输出 16,因为 2.1 不能用双精度精确表示。
答案 14 :(得分:-1)
一种方法是以字符串形式读取数字。找到小数点后子字符串的长度,即该人输入的小数位数。使用
将此字符串转换为float atof(string.c_str());
另一方面;处理浮点运算以将它们存储在具有有限精度的特殊对象中时,总是一个好主意。例如,您可以将浮点存储在名为“Decimal”的特殊类型的对象中,其中整数部分和数字的小数部分都是整数。这样你就具有有限的精度。这样做的缺点是你必须写出算术运算的方法(+, - ,*,/等),但你可以很容易地用C ++覆盖运算符。我知道这偏离了你原来的问题,但最好以有限的形式存储你的小数。通过这种方式,您还可以回答您的问题,即该数字的小数位数。