如何计算double/float C++中的位数
How to count the number of digits in double/float C++
我正在尝试计算 double 的小数部分的位数,但出了点问题,我遇到了无限循环:
double pi = 3.141592;
int counter = 0;
for (; pi != int(pi); ++counter)
pi *= 10;
cout << counter << endl;
我刚读到这个问题,但是找不到好的解决方案。真的没有比将数字转换为字符串并计算字符更好的方法了吗?我想还有更正确的方法。
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,0 和 1,就像计算机中的几乎所有数字一样)。所以它们中有一个确切数量的二进制数字,在 double
中更多,在 float
中更少。但是,如果您将像 3.141592 这样的十进制常量转换为 double
,然后以完全准确的方式打印它,您将不会得到相同的数字。这是因为转换基数之间的小数通常不是精确的(这与 1/3 具有无限小数扩展 0.33... 时得到的效果类似)。示例:
double pi = 3.141592;
std::cout << std::setprecision(100) << pi << std::endl;
我的输出:
3.14159200000000016217427400988526642322540283203125
所以当你开始在你的问题代码中将它乘以 10 时,你会发现它在很长一段时间内都没有变成一个精确的整数(到那时它已经远远超出了 int
的范围) ,所以你的条件永远不会为真(要么有小数部分,要么双精度数太大而不适合整数)。
换句话说,你问的,直接从 double
计算数字,是没有意义的。您首先需要将其打印为字符串,否则您实际上 没有 多个小数位来计算。当然,您可以对其进行微优化,并跳过实际转换为 ASCII 或 UTF-16 字符串的过程,但我看不到值得为此付出努力的场景(除了作为学习练习)。
如果您在计算中需要精确的十进制数字,则需要一种特殊的数字类型,它不以二进制形式存储分数。这种数字类型的例子是 Java BigDecimal.
粗糙的是无限循环
当您将浮点变量与小数部分相乘时,某些数字在 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,double是这样划分的:
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
有点不规则。有关详细信息,请参阅
- How do you print the EXACT value of a floating point number?
n2=50
的 n10=15
非常接近 ~16
小数。这意味着如果只存在安全的十进制数字,3.14159200000000016
应该被截断为 3.141592000000000
。
Atharva 先生在这里开火。
我做了一个简单的函数,可以计算双精度数字的位数。(浮点数有 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;
,在 return 函数之前。
希望对您有所帮助。
我正在尝试计算 double 的小数部分的位数,但出了点问题,我遇到了无限循环:
double pi = 3.141592;
int counter = 0;
for (; pi != int(pi); ++counter)
pi *= 10;
cout << counter << endl;
我刚读到这个问题,但是找不到好的解决方案。真的没有比将数字转换为字符串并计算字符更好的方法了吗?我想还有更正确的方法。
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,0 和 1,就像计算机中的几乎所有数字一样)。所以它们中有一个确切数量的二进制数字,在 double
中更多,在 float
中更少。但是,如果您将像 3.141592 这样的十进制常量转换为 double
,然后以完全准确的方式打印它,您将不会得到相同的数字。这是因为转换基数之间的小数通常不是精确的(这与 1/3 具有无限小数扩展 0.33... 时得到的效果类似)。示例:
double pi = 3.141592;
std::cout << std::setprecision(100) << pi << std::endl;
我的输出:
3.14159200000000016217427400988526642322540283203125
所以当你开始在你的问题代码中将它乘以 10 时,你会发现它在很长一段时间内都没有变成一个精确的整数(到那时它已经远远超出了 int
的范围) ,所以你的条件永远不会为真(要么有小数部分,要么双精度数太大而不适合整数)。
换句话说,你问的,直接从 double
计算数字,是没有意义的。您首先需要将其打印为字符串,否则您实际上 没有 多个小数位来计算。当然,您可以对其进行微优化,并跳过实际转换为 ASCII 或 UTF-16 字符串的过程,但我看不到值得为此付出努力的场景(除了作为学习练习)。
如果您在计算中需要精确的十进制数字,则需要一种特殊的数字类型,它不以二进制形式存储分数。这种数字类型的例子是 Java BigDecimal.
粗糙的是无限循环
当您将浮点变量与小数部分相乘时,某些数字在 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,double是这样划分的:
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
有点不规则。有关详细信息,请参阅- How do you print the EXACT value of a floating point number?
n2=50
的n10=15
非常接近~16
小数。这意味着如果只存在安全的十进制数字,3.14159200000000016
应该被截断为3.141592000000000
。
Atharva 先生在这里开火。
我做了一个简单的函数,可以计算双精度数字的位数。(浮点数有 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;
,在 return 函数之前。
希望对您有所帮助。