C语言 计算浮点数中`.`后的位数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17837654/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:00:14  来源:igfitidea点击:

Count number of digits after `.` in floating point numbers?

cfloating-point

提问by someone

This is one interview question. How do you compute the number of digit after .in floating point number.

这是一个面试问题。你如何计算.浮点数后的位数。

e.g. if given 3.554 output=3

例如,如果给定 3.554 输出 = 3

for 43.000 output=0. My code snippet is here

对于 43.000 输出 = 0。我的代码片段在这里

double no =3.44;
int count =0;
while(no!=((int)no))
{
    count++;
    no=no*10;
}
printf("%d",count);

There are some numbers that can not be indicated by floattype. for example, there is no 73.487in floattype, the number indicated by floatin c is 73.486999999999995to approximate it.

有些数字不能用float类型表示。例如,73.487floattype中没有float,c中的数字是73.486999999999995近似的。

Now how to solve it as it is going in some infinite loop.

现在如何解决它,因为它进入了一些无限循环。

Note : In the IEEE 754 Specifications, a 32 bit float is divided as 24+7+1 bits. The 7 bits indicate the mantissa.

注意:在 IEEE 754 规范中,一个 32 位浮点数被划分为 24+7+1 位。7 位表示尾数。

采纳答案by Keith Thompson

The problem isn't really solvable as stated, since floating-point is typically represented in binary, not in decimal. As you say, many (in fact most) decimal numbers are not exactly representable in floating-point.

如上所述,该问题并不能真正解决,因为浮点数通常以二进制表示,而不是以十进制表示。正如您所说,许多(实际上是大多数)十进制数不能完全用浮点数表示。

On the other hand, allnumbers that are exactly representable in binary floating-point are decimals with a finite number of digits -- but that's not particularly useful if you want a result of 2 for 3.44.

另一方面,所有可以用二进制浮点精确表示的数字都是具有有限位数的小数——但如果你想要 2 的结果,这不是特别有用3.44

When I run your code snippet, it says that 3.44has 2 digits after the decimal point -- because 3.44 * 10.0 * 10.0just happens to yield exactly 344.0. That might not happen for another number like, say, 3.43(I haven't tried it).

当我运行你的代码片段时,它说3.44小数点后有 2 位数字——因为3.44 * 10.0 * 10.0恰好产生了344.0. 对于另一个数字,例如3.43(我还没有尝试过),这可能不会发生。

When I try it with 1.0/3.0, it goes into an infinite loop. Adding some printfs shows that nobecomes exactly 33333333333333324.0after 17 iterations -- but that number is too big to be represented as an int(at least on my system), and converting it to inthas undefined behavior.

当我尝试使用 时1.0/3.0,它会进入无限循环。添加一些printfs 表明它no恰好33333333333333324.0在 17 次迭代之后变为- 但该数字太大而无法表示为int(至少在我的系统上),并将其转换为int具有未定义的行为。

And for large numbers, repeatedly multiplying by 10 will inevitably give you a floating-point overflow. There are ways to avoid that, but they don't solve the other problems.

而对于大数,反复乘以 10 难免会出现浮点溢出。有一些方法可以避免这种情况,但它们并不能解决其他问题。

If you store the value 3.44in a doubleobject, the actual value stored (at least on my system) is exactly 3.439999999999999946709294817992486059665679931640625, which has 51 decimal digits in its fractional part. Suppose you really wantto compute the number of decimal digits after the point in 3.439999999999999946709294817992486059665679931640625. Since 3.44and 3.439999999999999946709294817992486059665679931640625are effectively the same number, there's no way for any C function to distinguish between them and know whether it should return 2 or 51 (or 50 if you meant 3.43999999999999994670929481799248605966567993164062, or ...).

如果将值存储3.44double对象中,则存储的实际值(至少在我的系统上)正好是3.439999999999999946709294817992486059665679931640625,它的小数部分有 51 个十进制数字。假设你真的计算3.439999999999999946709294817992486059665679931640625. 由于3.443.439999999999999946709294817992486059665679931640625实际上是相同的 number,因此任何 C 函数都无法区分它们并知道它是否应该返回 2 或 51 (或者 50 如果您的意思是3.43999999999999994670929481799248605966567993164062,或者...)。

You could probably detect that the stored value is "close enough" to 3.44, but that makes it a much more complex problem -- and it loses the ability to determine the number of decimal digits in the fractional part of 3.439999999999999946709294817992486059665679931640625.

您可能会检测到存储的值“足够接近” 3.44,但这使它成为一个更加复杂的问题 - 它失去了确定 的小数部分中小数位数的能力3.439999999999999946709294817992486059665679931640625

The question is meaningful only if the number you're given is stored in some format that can actually represent decimal fractions (such as a string), or if you add some complex requirement for determining which decimal fraction a given binary approximation is meant to represent.

仅当您给出的数字以某种可以实际表示十进制分数的格式(例如字符串)存储时,或者如果您添加一些复杂的要求来确定给定的二进制近似值要表示的十进制分数时,该问题才有意义.

There's probably a reasonable way to do the latter by looking for the unique decimal fraction whose nearest approximation in the given floating-point type is the given binary floating-point number.

通过查找在给定浮点类型中最接近的近似值是给定二进制浮点数的唯一十进制小数,可能有一种合理的方法来执行后者。

回答by R.. GitHub STOP HELPING ICE

I doubt this is what you want since the question is asking for something that's not usually meaningful with floating point numbers, but here is the answer:

我怀疑这是你想要的,因为问题是要求一些通常对浮点数没有意义的东西,但这里是答案:

int digits_after_decimal_point(double x)
{
    int i;
    for (i=0; x!=rint(x); x+=x, i++);
    return i;
}

回答by aka.nice

The question could be interpreted as such:

这个问题可以解释为:

Given a floating point number, find the shortest decimal representation that would be re-interpreted as the same floating point value with correct rounding.

给定一个浮点数,找到最短的十进制表示,该表示将被重新解释为具有正确舍入的相同浮点值。

Once formulated like this, the answer is Yes we can - see this algorithm:

一旦这样制定,答案是肯定的,我们可以 - 看这个算法:

Printing floating point numbers quickly and accurately. Robert G. Burger and R. Kent Dybvig. ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, June 1996

快速准确地打印浮点数。Robert G. Burger 和 R. Kent Dybvig。ACM SIGPLAN 1996 编程语言设计与实现会议,1996 年 6 月

http://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf

http://www.cs.indiana.edu/~dyb/pubs/FP-Printing-PLDI96.pdf

See also references from Compute the double value nearest preferred decimal resultfor a Smalltalk implementation.

另请参阅计算最接近Smalltalk 实现的首选十进制结果的双精度值的参考资料。

回答by Drew McGowen

Sounds like you need to either use sprintfto get an actual rounded version, or have the input be a string (and not parsed to a float).

听起来您需要使用sprintf来获取实际的舍入版本,或者将输入设为字符串(而不是解析为 a float)。

Either way, once you have a string version of the number, counting characters after the decimal should be trivial.

无论哪种方式,一旦您有了数字的字符串版本,计算小数点后的字符应该是微不足道的。

回答by phuclv

There are no general exact solutions. But you can convert the value to string and don't count the part exceeding the type's precisionand exclude the trailing 0s or 9s. This will work for more cases but it still won't return the correct answer for all.

没有通用的精确解。但是您可以将值转换为字符串并且不计算超出类型精度的部分并排除尾随 0s 或 9s。这将适用于更多情况,但仍然不会为所有人返回正确答案。

For example double's accuracy is about 15 digitsif the input is a decimal string from the user (17 digits for binary-decimal-binary round trip), so for 73.486999999999995 there are 15 - 2 = 13 digits after the radix point (minus the 2 digits in the int part). After that there are still many 9s in the fractional part, subtract them from the count too. Here there are ten 9s which means there are 13 - 10 = 3 decimal digits. If you use 17 digits then the last digit which may be just garbage, exclude it before counting the 9s or 0s.

例如,如果输入是来自用户的十进制字符串(二进制-十进制-二进制往返为 17 位),则double 的精度约为 15 位,因此对于 73.486999999999995,小数点后有 15 - 2 = 13 位(减去 2 int 部分中的数字)。之后小数部分还有很多9,也从计数中减去。这里有十个 9,这意味着有 13 - 10 = 3 个十进制数字。如果您使用 17 位数字,那么最后一位数字可能只是垃圾,请在计算 9 或 0 之前将其排除。

Alternatively just start from the 15 or 16thdigit and iterate until you see the first non-0 and non-9 digit. Count the remaining digits and you'll get 3 in this case. Of course while iterating you must also make sure that the trailing is all 0s or all 9s

或者,只需从第 15 或 16数字开始并迭代,直到您看到第一个非 0 和非 9 位数字。计算剩余的数字,在这种情况下你会得到 3。当然,在迭代时,您还必须确保尾随全为 0 或全为 9

回答by William Roeder

Request:e.g. if given 3.554 output = 3, for 43.000 output = 0

请求:例如,如果给定 3.554 输出 = 3,则 43.000 输出 = 0

Problem:that's already a decimal like 0.33345. When this gets converted to a double, it might be something like 0.333459999...125. The goal is merely to determine that 0.33345 is a shorter decimal that will produce the same double. The solution is to convert it to a string with the right number of digits that results in the same original value.

问题:这已经是一个像 0.33345 这样的小数。当它转换为 a 时double,它可能类似于 0.333459999...125。目标仅仅是确定 0.33345 是一个较短的小数,它将产生相同的双精度数。解决方案是将其转换为具有正确位数的字符串,从而产生相同的原始值。

int digits(double v){
   int d=0; while(d < 50){
      string t=DoubleToString(v,d); double vt = StrToDouble(t);
      if(MathAbs(v-vt) < 1e-15) break;
      ++d;
   }
   return d;
}

double 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*.1*.1*.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=1/3.;       PrintFormat("v=%g, d=%i", v,digits(v));// v=0.333333, d=15

回答by Barmar

What you can do is multiply the number by various powers of 10, round that to the nearest integer, and then divide by the same number of powers of 10. When the final result compares different from the original number, you've gone one digit too far.

您可以做的是将数字乘以 10 的各种幂,将其四舍五入到最接近的整数,然后除以 10 的相同幂数。 当最终结果与原始数字不同时,您就少了一位太远。

I haven't read it in a long time, so I don't know how it relates to this idea, but How to Print Floating-Point Numbers Accurately from PLDI 1990and 2003 Retrospectiveare probably very relevant to the basic problem.

我很久没读了,所以我不知道它与这个想法有什么关系,但是 How to Print Floating-Point Numbers Accurately from PLDI 1990and 2003 Retrospective可能与基本问题非常相关。