afa*_*lek 45 c# c++ floating-point counter loops
在我正在阅读的一本书中,有这样的摘录:
您还可以使用浮点值作为循环计数器.这是一个
for
带有这种计数器的循环示例:Run Code Online (Sandbox Code Playgroud)double a(0.3), b(2.5); for(double x = 0.0; x <= 2.0; x += 0.25) cout << "\n\tx = " << x << "\ta*x + b = " << a*x + b;
此代码片段计算的值
a*x+b
对于值x
从0.0
到2.0
,在步骤0.25
; 但是,在循环中使用浮点计数器时需要注意.许多十进制值无法以二进制浮点形式精确表示,因此差异可能会累积为累积值.这意味着您不应该编写for循环,以便结束循环取决于浮点循环计数器达到精确值.例如,以下设计不良的循环永远不会结束:Run Code Online (Sandbox Code Playgroud)for(double x = 0.0 ; x != 1.0 ; x += 0.2) cout << x;
与此环的目的是要输出的值
x
,因为它从变化0.0
到1.0
; 但是,0.2
没有精确表示为二进制浮点值,因此值x
绝不是完全正确的1
.因此,第二个循环控制表达式始终为false,并且循环无限期地继续.
有人可以解释第一个代码块是如何运行而第二个代码块不运行的?
Jon*_*eet 71
第一个将最终终止,即使x
没有达到准确 2.0 ...因为它最终会成为更大小于2.0,从而打破了.
第二个必须x
使命中正好 1.0以便打破.
不幸的是,第一个示例使用0.25的步长,这在二进制浮点中是完全可表示的 - 如果两个示例都使用0.2作为步长,那将更为明智.(0.2在二进制浮点中不能完全表示.)
这是一个更广泛问题的例子 - 在比较双精度时,您经常需要在一些可接受的容差范围内检查相等性,而不是精确相等.
在某些情况下,通常检查未更改的默认值,相等是正常的:
double x(0.0);
// do some work that may or may not set up x
if (x != 0.0) {
// do more work
}
Run Code Online (Sandbox Code Playgroud)
一般来说,检查与预期值不能这样做 - 你需要这样的东西:
double x(0.0);
double target(10000.0);
double tolerance(0.000001);
// do some work that may or may not set up x to an expected value
if (fabs(target - x) < tolerance) {
// do more work
}
Run Code Online (Sandbox Code Playgroud)
浮点数在内部表示为二进制数,几乎总是以IEEE格式表示您可以在此处查看数字的表示方式:
http://babbage.cs.qc.edu/IEEE-754/
例如,0.25的二进制是0.01 b和表示为1.00000000000000000000000*2 -2.
这内部存储有1位用于符号,8位用于指数(表示值介于-127和+128之间,值为23位(前导1.未存储).实际上,这些位是:
[0] [01111101] [00000000000000000000000]
而二进制中的0.2没有精确的表示,就像1/3没有精确的十进制表示.
这里的问题是正如1/2可以精确地以十进制格式表示为0.5,但是1/3只能近似为0.3333333333,0.25可以精确地表示为二进制分数,但0.2不能.在二进制中它是0.0010011001100110011001100 .... b其中最后四位数字重复.
要存储在计算机上,它将被输入0.0010011001100110011001101 b.这真的非常接近,所以如果你计算坐标或其他绝对值很重要的东西,那就没关系了.
不幸的是,如果您将该值添加到自身五次,您将获得1.00000000000000000000001 b.(或者,如果您将0.2舍入到0.0010011001100110011001100 b,则会得到0.11111111111111111111100 b)
无论哪种方式,如果您的循环条件是1.00000000000000000000001 b == 1.00000000000000000000000 b它将不会终止.如果你使用<=代替,如果值刚好在最后一个值之下,它可能会运行一个额外的时间,但它会停止.
可以制作一种能够准确表示小十进制值的格式(就像只有两个小数位的任何值一样).它们用于财务计算等.但是正常的浮点值确实如此:它们交换能够表示一些小的"简单"数字,如0.2,以便能够以一致的方式表示大范围.
出于这个原因,通常避免使用float作为循环计数器,常见的解决方案是:
编译器可以优化浮动"="循环以将其转换为您的意思,但我不知道标准是允许的还是在实践中发生的.
归档时间: |
|
查看次数: |
15473 次 |
最近记录: |