仿射温度
摄氏度和华氏度不是普通比例单位。它们的换算带有 offset,因此不能和米、秒、千克这样的线性单位放在同一个乘法模型里。
数学视角
普通 Quantity 属于线性空间:差值可以相加、缩放、取反,也可以参与乘除组合。绝对温度点更接近仿射空间中的点:点本身没有自然的零点,两个点相减才得到线性差值。
text
point - point -> interval
point + interval -> point
interval + interval -> interval
scale(interval) -> interval普通线性单位换算只需要比例:
text
x_base = a * x而摄氏度、华氏度这类绝对温度标度需要仿射变换:
text
x_base = a * x + b这个 offset 让绝对点不能安全地进入 Un 的线性比例模型。温度差仍然可以用开尔文这样的普通 Quantity 表示,但温度点需要单独的 affine 层。
问题
例如,20 degC 是一个绝对温度点,而 20 K 可以是一个温度差。二者在计算中不是同一种东西。
如果把摄氏度当成普通 scaled unit,会出现这类错误:
20 degC + 20 degC看起来像合法数量相加,但物理意义不成立。20 degC * 2得到40 degC,但绝对温度点不应该这样缩放。20 degC和20 K被当作只差比例的同类单位,忽略了 offset。
选择
LunarUnits 把绝对温度建模为 affine point,把温度差保留为普通线性 Quantity。点和差值是不同概念:
- 点减点得到差值。
- 点加差值得到新的点。
- 差值可以参与普通线性单位运算。
避免的错误
这种分层可以阻止“绝对点当作向量”导致的错误。它尤其适合公式计算:显热公式需要的是温度差,而不是两个绝对温度点的普通数值相乘。
代价
代价是 API 多了一层 Point,用户不能把所有温度都当作普通 Quantity。但这个区分换来的是更清晰的物理边界:绝对位置和间隔不再混用。
边界
LunarUnits 的线性单位核心不带 offset。任何需要 offset 的尺度都应进入 affine 层,而不是塞进 Un。这保持了普通单位乘除法的简单性,也让非线性规则集中在专门模型里。
这个边界不只适用于温度。绝对时间点也可以看作带参考原点的仿射点,而时长仍然是线性数量。也就是说,“2026-07-03 10:00”这类点和“2 小时”这类间隔应该分层处理;两点之差得到间隔,点加间隔得到新的点。