我正在尝试计算double的小数部分中的位数,但是出现问题并且我得到无限循环:
double pi = 3.141592;
int counter = 0;
for (; pi != int(pi); ++counter)
pi *= 10;
cout << counter << endl;
我刚读过这个问题,但是我找不到一个好的解决方案。除了将数字转换为字符串和计数字符之外,真的没有更好的方法吗?我想有更正确的方法。
答案 0 :(得分:5)
计算机中的浮点数使用二进制(基数2,零和1,与计算机中的几乎所有数字一样)。因此,其中包含确切数量的二进制数字, BOOST_CLASS_IMPLEMENTATION(my_primitive_type, boost::serialization::primitive_type)
更多,double
更少。但是,如果将像3.141592这样的十进制常量转换为float
,则以完全准确的方式打印它,您将无法获得相同的数字。这是因为转换基数之间的小数一般不精确(它与1/3具有无限十进制扩展0.33的效果相似)。例如:
double
为我输出:
double pi = 3.141592;
std::cout << std::setprecision(100) << pi << std::endl;
因此,当您在问题代码中开始将此乘以10时,您可以看到它在很长一段时间内不会成为精确整数(到那时它远远超出3.14159200000000016217427400988526642322540283203125
的范围),所以你的条件永远不会是真的(要么是小数部分,要么是双倍太大而不适合整数)。
换句话说,你问的是,直接从int
计算数字是没有意义的。首先需要将其打印为字符串,否则您实际上 要计算多个十进制数字。当然你可以对它进行微观优化并跳过实际转换为ASCII或UTF-16字符串,但是我没有看到那种值得麻烦的情况(除非作为学习练习)。
如果在计算中需要精确的十进制数字,则需要一个特殊的数字类型,它不会以二进制形式存储分数。此类数字类型的示例为Java BigDecimal。
答案 1 :(得分:2)
num = pi - int(pi);
while abs(num) >= 0.0000001{
num = num * 10;
count = count + 1;
num = num - int(num);
}
cout<< count<< endl;
有关详细信息,请参阅此C/C++ counting the number of decimals?。
答案 2 :(得分:2)
粗略的是无限循环
当您将浮点变量与小数部分相乘时,某些数字在 FPU 算术中往往表现为无理性(由于舍入误差)。所以无论你乘以10
多少,仍有一些小部分...
如何改为
您需要查看二进制中的尾数,并查看最低有效位集的位置。这样:
union
{
double lf;
BYTE db[8];
} pi;
pi.lf = 3.141592;
// here just output the BYTES to memo,screen or whatever
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[0])); // LSB
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[1]));
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[2]));
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[3]));
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[4]));
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[5]));
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[6]));
mm_log->Lines->Add(AnsiString().sprintf("%x",pi.db[7])); // MSB
结果是pi存储为:
0x400921FAFC8B007A hex
0100 0000 0000 1001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
根据Wiki IEEE_754,双重划分如下:
sign = 0 bin -> +
exponent = 100 0000 0000 bin - 1023 dec = 1
mantissa = 1.1001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
请注意,尾数添加了1.
,这在数字表示中不存在!!!指数也有偏差,这就是-1023
存在的原因。现在当我们得到了指数左移时我们得到了
pi = 11.001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
现在我们寻找最少设置有效位位置
pi = 11.001 0010 0001 1111 1010 1111 1100 1000 1011 0000 0000 0111 1010 bin
^
它的重量为2^-50=1/2^50=8.8817841970012523233890533447266e-16=~10e-16
(小数点后第50个位置),这意味着你得到了大约{16}个四舍五入的pi=3.141592
表示。正确打印后,您将看到:
3.14159200000000016
您还可以使用n10=~log10(n2)
来估算由n10
二进制小数安全表示的十进制小数n2
的数量。但是对于低n2
有点不规则。有关更多信息,请参阅
n2=50
n10=15
的{{1}}非常接近~16
小数。这意味着如果只有安全的十进制数字,3.14159200000000016
应该被截断为3.141592000000000
。
答案 3 :(得分:0)
Atharva MR Fire在这里。
我做了一个简单的函数,可以计算双精度位数(浮点数为8位,所以只需将16更改为8,然后进行一些其他必要的更改即可)。
int get_float_digits(double num)
{
int digits=0;
double ori=num;//storing original number
long num2=num;
while(num2>0)//count no of digits before floating point
{
digits++;
num2=num2/10;
}
if(ori==0)
digits=1;
num=ori;
double no_float;
no_float=ori*(pow(10, (16-digits)));
long long int total=(long long int)no_float;
int no_of_digits, extrazeroes=0;
for(int i=0; i<16; i++)
{
int dig;
dig=total%10;
total=total/10;
if(dig!=0)
break;
else
extrazeroes++;
}
no_of_digits=16-extrazeroes;
return no_of_digits;
}
如果只想获取小数位数,请在返回函数之前的末尾添加代码no_of_digits=no_of_digits-digits;
。
我希望这会有所帮助。