负坐标导致区块计算在两处出错,而非一处
截断除法将方块 -1 放入区块 0 而非 -1,而普通 % 运算符给出区块内偏移量 -1 而非 15。两者都错了,而且在出生点以西之前看起来都是对的。
在正象限中,每个区块计算都微不足道;但在其他三个象限中,它们都错了。它出错的地方正好有两处,它们彼此独立,并且都产生看似合理的结果。
两种运算
chunk = floor(block / 16) not trunc(block / 16)
offset = ((block % 16) + 16) % 16 not block % 16
区域坐标是同一对,高一级,用 32 代替 16:
region = floor(chunk / 32) -> r.<x>.<z>.mca
regionOff = ((chunk % 32) + 32) % 32
因此,一个区域是 32 × 32 个区块,即 512 × 512 个方块。
各自出错之处
除法。 大多数语言都向零截断,而 floor 则向负无穷舍入。它们在正数上一致,但在每个不是精确倍数的负数上不一致:
- 方块
−1→floor(−1/16) = −1✓,但trunc(−1/16) = 0✗
方块 -1 位于原点以西一个方块,属于区块 -1。截断将其放入区块 0,与方块 +1 并列。每个负区块索引都高出一位。
余数。 在 JavaScript、Java、C 和大多数其他语言中,% 保留左操作数的符号:
- 方块
−1→ 正确偏移量15,但−1 % 16 = −1✗
偏移量 -1 根本不是 16 格宽区块内的位置。用它来索引方块数组要么抛出错误,要么悄无声息地读取错误的单元格。
一个示例
一个轴上的方块 -1290:
| 步骤 | 正确 | 朴素 |
|---|---|---|
| 区块 | floor(−1290/16) = -81 | trunc = -80 ✗ |
| 区块内偏移量 | 6 | −1290 % 16 = -10 ✗ |
| 区域 | floor(−81/32) = -3 | trunc = -2 ✗ |
| 区域内偏移量 | 15 | −81 % 32 = -17 ✗ |
四个值,四个错误答案,没有一个超出范围到足以明显看出错误。区域 -3 跨越区块 -96 到 -65,区块 -81 位于其起始处向内 15 格——这正是修正后的余数返回的结果。
下界转换在一个方向上是损失的
主世界到下界(Nether)的转换除以 8,并且也向下取整:
netherX = floor(overworldX / 8)
返回时乘以 8。这不是一个往返过程。floor 丢弃了余数,因此返回时会将你落在 8 的倍数上:
- 主世界
1000→ 下界125→ 返回到1000✓ - 主世界
1007→ 下界125→ 返回到1000✗ — 短了七个方块
每个轴的误差是 0 到 7 个方块,因此 X 轴和 Z 轴可能同时最多有 7 个方块的误差。这正是精确传送门链接需要记下下界坐标而不是重新推导的全部原因:一旦你进行了除法,它所来自的主世界位置就无法恢复了。
对于下界枢纽来说,这很少重要——7 个方块在传送门自身的捕获半径内。但对于链接两个绝不能互相抢夺流量的传送门来说,这非常重要。
负主世界坐标以熟悉的方式使其变得更糟:主世界 −1 给出下界 floor(−1/8) = −1,而截断给出 0 并将你发送到轴的错误一侧。