【问题标题】:For-loop with decimal increment !=.5 yields strange results带小数增量的 For 循环 !=.5 产生奇怪的结果
【发布时间】:2018-08-16 11:33:04
【问题描述】:

最初的想法

我刚刚找到了我的旧 Commodore 64 计算机,将其连接起来,并决定再次尝试学习 Basic。我刚刚完成了第 3 章,其中演示了一个简单的 FOR 循环:

10 FOR NB = 1 TO 10 STEP 1
20 PRINT NB,
30 NEXT NB

正如预期的那样,这会产生以下结果:

1       2       3       4
5       6       7       8
9       10

浮点数介绍

当步长设置为1.0时,上述结果是相同的。但是,除 0.5 之外的其他数字也会导致问题:

如果我将步长增量更改为 anything 但 0.5(或 1),我会得到奇怪的浮点数,显然越早出现的浮点数越低。第一次测试,我把NB改成1 TO 40

测试结果

  • FOR NB = 1 TO 40 STEP .6:1-31 的正常结果,然后是 31.6000001。为了看看我是否会进一步得到奇怪的结果,我将 NB 增加到 100,然后又看到了从 42 开始的奇怪数字:41.2、41,8、42.4、42.9999999、43.5999999 等。
  • FOR NB = 1 TO 40 STEP .4:正常结果为 1–7.4,然后是 7,8000001,然后是正常结果 8.2–22.6,然后是 22.9999999、23.3999999 等。
  • FOR NB = 1 TO 40 STEP .2:1–6.2 的正常结果,然后 6.3999999 以 .2 为增量直到 8.5999999,然后从 8.7999998 变为 9.9999998,然后从 10.2 开始正常结果。
  • FOR NB = 1 TO 40 STEP .1:1–3.6 的正常结果,然后是 3.6999999 等。
  • FOR NB = 1 TO 40 STEP .05:1–2.3 的正常结果,然后是 2.34999999(注意额外的数字)直到 2.59999999,然后是 2.65–2.7,然后是 2.74999999 等等。

失败迭代次数

这些步骤在以下迭代中失败:

  • 0.6 增量在迭代中失败
    • 52 (31.6000001),
    • 51-70 没问题,
    • 那么 71–87 是 0.0000001 到很少(例如:42.9999999),
    • 然后 88–103 再减一(例如:53.1999998),
    • 然后从 104 开始进一步减少(例如:62.7999997)。
  • 0.4 增量在迭代中失败
    • 18,
    • 19-55 没问题,
    • 56–64 位于 -.9999999,
    • 65就好了,
    • 66–84 位于 -.9999999,
    • 85-100 没问题,
    • 101–116 为 +.0000001,
    • 117 在 0.000002 处继续,以此类推。
  • 0.2 增量在迭代中失败
    • 28 在 -.9999999,
    • 47–107 很好,
    • 108–140 在 +0.0000001 时失败,
    • 141 以后在 +0.0000002 处失败,依此类推
    • 0.1 增量在迭代中失败
    • 28 在 -.9999999,
    • 79-88 没问题,
    • 89–90 在 +0.00000001(原文如此)处失败,
    • 91-116 没问题,
    • 117–187 在 +0.0000001 处失败,
    • 188 以后在 +0.0000002 处失败,依此类推。
  • 0.05 增量在迭代中失败
    • 28–33 在 -.00000001,
    • 34-35 没问题,
    • 36–68 在 -0.00000001 处失败,
    • 69-78 没问题,
    • 79–92 在 +0.00000001 处失败,
    • 93–106 在 +0.00000002 处失败,
    • 107 以后在 +0.00000003 处失败,依此类推。

以上注意事项

为了记录,我添加了一个计数器以简化报告;因此程序看起来像这样:

05 NC = 1
10 FOR NB = 1 TO 100 STEP 0.05: REM 0.6, 0.4, 0.2, 0.1, 0.05
20 PRINT NC;":";NB,
25 NC = NC + 1
30 NEXT NB

主要问题

我怀疑问题在于如何将十进制转换为二进制,但奇怪的是它在 0.5 步内就可以正常工作。是什么导致了这个错误,如何修复它,或者应该如何解释它?我的 Commodore 运行 Basic v2。

【问题讨论】:

  • 欢迎来到浮点数的狂野、古怪、美妙的世界。这不仅限于 BASIC。正如 aframestor 的回答中简要解释的那样,这是因为处理将十进制数存储为二进制的精度的性质。
  • (a) 考虑一下如果您尝试增加 1/3 但您的计算机只能处理十进制数字,并且只能处理小数点后两位数,会发生什么情况。它不能数 1/3, 2/3, 1, 4/3, 5/3, 2,... 它只能数 .33, .66, .99, 1.32, 1.65, 1.98,... 同样的事情发生在您使用二进制浮点数尝试使用小数进行计数。数字略有偏差,并且随着事情的发展,错误会增加。 (b) 您并不总是能立即看到错误,因为浮点输出仅使用几位数字进行格式化,而不是显示整个值。
  • @EricPostpischil 我想这就是为什么我得到明显随机出现的偏移量,然后纠正一段时间,然后在经过足够多的迭代后最终似乎不断出错。

标签: for-loop floating-point basic c64


【解决方案1】:

我猜想,因为 0.5 的倍数可以很容易地转换为以 2 为底,所以它不会产生任何问题。我敢打赌,如果您尝试使用 .25 增量,它也可以正常工作。

【讨论】:

  • 但 C64 清楚地将整数与浮点数区分开来。在我的手册页上。 34ff,在变量下,我们被解释为变量由两个字符来区分,第一个是字母,或者仅此而已(浮点数)、百分位数(整数)或美元符号(字符串);此外,在 p. 35 它将整数限制为 -32768–+32767,所以 2¹⁶ 字节(不过我可能在这里错了)。
  • 对于整数 A 和 B. 0.5、0.25、0.75、0.125、0.325、0.824 可以精确表示为 'A/(2^B)' 的合理分数,算法将是精确的。 .
  • 次要的挑剔,就整数值而言,这将是 2^16 位(不是字节)。与 C64 的(浮点)单精度(4 字节)相比,后来的 BASIC 实现了双精度(8 字节),但是这两个数字在内部存储方式方面甚至有不同的格式。例如,我记得我编写了一个 CVDMBF 函数,用于将“Microsoft 二进制格式”转换为双精度的 IEEE 格式。然后是不同类型的舍入。为了好玩,请查看“银行家的四舍五入”。这是 VB6 决定默认使用的。
  • 庄家四舍五入是指将半美分四舍五入的方法。通常,值 0.5 将始终舍入为 1,而 -0.5 将始终舍入为零(最高整数值),但在银行家的舍入中,它舍入的方向取决于小数点左侧的数字是奇数还是偶数。我不记得我脑海中的确切公式,但它可能,例如,根据触发器是什么(奇数或偶数),将 3.5 和 4.5 舍入到 3,或相应地舍入到 3 和 5。它被认为是一种“更公平”的银行业舍入系统。
  • @CannedMan C64 BASIC 区分整数(整数)和浮点仅用于存储。所有数学运算都使用浮点数。任何数学之前的整数首先转换为浮点数;仅当分配给整数变量时,结果才会转换为整数值;所以整数变量很慢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-22
  • 1970-01-01
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 2013-06-27
  • 2016-02-16
相关资源
最近更新 更多