计算浮点数中“。”后面的位数?
这是一个面试问题。 你如何计算之后的数字位数.
在浮点数。
例如,如果给出3.554输出= 3
对于43.000输出= 0。 我的代码片段就在这里
double no =3.44; int count =0; while(no!=((int)no)) { count++; no=no*10; } printf("%d",count);
float
类型无法指示一些数字。 例如, float
类型没有73.487
, float
c表示的数字是73.486999999999995
来近似它。
现在如何解决它,因为它正在进行一些无限循环。
注意:在IEEE 754规范中,32位浮点数被分为24 + 7 + 1位。 7位表示尾数。
如上所述,问题并不是真正可解决的,因为浮点通常用二进制表示,而不是十进制。 如你所说,许多(事实上大多数)十进制数在浮点数中并不完全可以表示。
另一方面,在二进制浮点中可以精确表示的所有数字都是具有有限位数的小数 – 但是如果你想要结果为3.44
话,这并不是特别有用。
当我运行你的代码片段时,它表示3.44
在小数点后有2位数 – 因为3.44 * 10.0 * 10.0
恰好恰好产生344.0
。 这可能不会发生在另一个数字上,例如3.43
(我还没试过)。
当我尝试使用1.0/3.0
,它进入无限循环。 添加一些printf
显示17次迭代后no
变成33333333333333324.0
– 但是这个数字太大而不能表示为int
(至少在我的系统上),并且将其转换为int
具有未定义的行为。
对于大数字,重复乘以10将不可避免地给你一个浮点溢出。 有办法避免这种情况,但它们并没有解决其他问题。
如果将值3.44
存储在double
3.439999999999999946709294817992486059665679931640625
对象中,则存储的实际值(至少在我的系统中)正好是3.439999999999999946709294817992486059665679931640625
,其小数部分中包含51位小数。 假设你真的想要计算3.439999999999999946709294817992486059665679931640625
的点后的小数位数。 由于3.44
和3.439999999999999946709294817992486059665679931640625
实际上是相同的数字 ,任何C函数都无法区分它们并且知道它是否应该返回2或51(如果你的意思是3.43999999999999994670929481799248605966567993164062
, 3.43999999999999994670929481799248605966567993164062
50,或者……)。
您可能检测到存储的值“足够接近”到3.44
,但这使得它成为一个更复杂的问题 – 并且它失去了确定3.439999999999999946709294817992486059665679931640625
的小数部分中的小数位数的3.439999999999999946709294817992486059665679931640625
。
只有当您给出的数字以某种实际上可以表示小数部分(例如字符串)的格式存储时,或者如果您添加一些复杂的要求来确定给定的二进制近似值表示哪个小数部分时,该问题才有意义。
通过查找给定浮点类型中最接近的近似值是给定的二进制浮点数的唯一小数部分,可能有一种合理的方法来执行后者。
我怀疑这是你想要的,因为问题是要求浮点数通常没有意义的东西,但这里是答案:
int digits_after_decimal_point(double x) { int i; for (i=0; x!=rint(x); x+=x, i++); return i; }
听起来你需要使用sprintf
来获得一个实际的圆形版本,或者输入是一个字符串(而不是解析为float
)。
无论哪种方式,一旦你有一个字符串版本的数字,计算十进制后的字符应该是微不足道的。
问题可以解释为:
给定浮点数,找到最短的十进制表示,将其重新解释为具有正确舍入的相同浮点值。
一旦这样配制,答案是肯定的我们可以 – 看到这个算法:
快速准确地打印浮点数。 Robert G. Burger和R. Kent Dybvig。 ACM SIGPLAN 1996年会议计划语言设计与实施会议,1996年6月
http://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf
另请参阅计算 Smalltalk实现的double值最接近的首选十进制结果的引用。
没有一般的确切解决方案。 但是你可以在超过当前类型的精度后停止,这样它就不会无限运行。 此外,您可以计算是否有连续的0或9并排除它们。 这将使它在更多情况下运行良好,但它仍然没有为所有人返回正确的答案。
例如,double的精度约为16-17位,因此对于73.486999999999995,您可以运行17 - 2
(减去int部分中的2位数)次。 在那之后,除了最后一个数字之外,还有很多9个,所以从计数中减去它们
您可以做的是将数字乘以10的各种幂,将其舍入为最接近的整数,然后除以相同的10次幂。当最终结果与原始数字不同时,您已经去了一位数太远。
我没有在很长一段时间内阅读它,所以我不知道它与这个想法有什么关系,但如何从PLDI 1990和2003中准确地打印浮点数回顾可能与基本问题非常相关。
请求:例如,如果给出3.554输出= 3,则43.000输出= 0
问题:这已经是0.33345的小数。 当它转换为double时,它可能类似于0.333459999 … 125。 目标仅仅是确定0.33345是一个较短的小数,它将产生相同的双倍。
解决方案是将其转换为具有正确数字位数的字符串,从而产生相同的原始值。
int digits(double v){
int d = 0; 而(d <50){
string t = DoubleToString(v,d); double vt = StrToDouble(t);
如果(MathAbs(v-vt)<1e-15)中断;
++ d;
}
返回d;
}
双v = 0.33345; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 0.33345,d = 5
V = 0.01; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 0.01,d = 2
V = 0.00001; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 1e-05,d = 5
V = 5 * 0.00001; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 5e-05,d = 5
V = 5 * 0.1 * 0.1 * 0.1; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 0.005,d = 3
V = 0.05; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 0.05,d = 2
V = 0.25; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 0.25,d = 2
V =三分之一.; PrintFormat(“v =%g,d =%i”,v,digits(v)); // v = 0.333333,d = 15