C 浮动:如何绕过它们以获得 2d 几何(线)
C floats : how to go around them for 2d geometry (lines)
我目前正在用 C 语言做一些 2D 几何图形,主要是直线相交。这些线有各种斜率:从 0.001 到 1000(示例,我什至不知道)。
到现在为止我一直在使用浮点数,不必担心该值是非常小(然后浮点数会将 0,0011 存储为 1e-3 而没有舍入)还是非常大(然后 1001 会存储为 1e3),在这两种情况下相关的精度损失很小。
但现在我想尝试不用浮点数,用整数。如何保持我的计算精度?我可以有一个标志告诉我斜坡是大还是小,然后考虑十分之一的大斜坡和十倍的小斜坡,这样圆角对于小斜坡就没有问题,在大斜坡的情况下没有溢出.但这感觉很头疼。
基本上,我仍然需要能够区分 0.2 和 0.4 的斜率,以及 1000 和 2000 的斜率的溢出端(假设整数在 1000 时溢出——这里问题不大)。
还有其他想法吗?
将 斜率 存储为一对整数
struct slope {
int delta_y;
int delta_x;
};
这允许范围广泛的坡度,例如 0
和 +/- 1/INT_MAX ... +/- INT_MAX
,甚至是垂直坡度。通过仔细编码,可以进行精确计算。
延迟学分:这很像 。
如果您想以一般方式使用 int,请查找 fixed point arithmetic。
您还可以设计您的算法,以便以不需要次整数精度的方式进行每次计算(例如查找 Bresenham's line and circle 绘图算法)。
对于您的特定问题,您可以尝试将商和分数分开,换句话说,使用有理数。或者换句话说,将 delta X 和 delta Y 作为两个数字。
一般来说,对于任意方向的线,不建议使用 slope/intercept 表示 y = mx + p
,而是使用隐式方程 a x + b y + c = 0
。后者更加各向同性,支持垂直线并为您提供额外的灵活性来缩放系数。
见@chux的回答,系数可以是deltas,Dy x - Dx y + c = 0
(假设直线是由两个点定义的,Dx
和Dy
很可能不会溢出) . c
上仍有可能溢出,您可以使用变体 Dy (x - x0) - Dx (y - y0) = 0
.
无论如何,交集等中间计算可能需要更大的范围,即双倍长度整数。
标记 large/low 值的想法有点适得其反:它实际上是一种处理浮点数的原始方法,即将小数位数与尾数分开。以这种方式工作,您将以某种方式重新设计一个浮点系统,它不如内置类型强大,并且会花费您的汗水和泪水。
不幸的是,无法避免高级算术。实际上,两条直线的交点由 Cramer 公式给出
x = (c b' - c' b) / (a b' - a' b),
y = (a c' - a' c) / (a b' - a' b)
其中要评估的产品比初始系数大一个数量级。这是因为准平行线有很远的交点。
我目前正在用 C 语言做一些 2D 几何图形,主要是直线相交。这些线有各种斜率:从 0.001 到 1000(示例,我什至不知道)。
到现在为止我一直在使用浮点数,不必担心该值是非常小(然后浮点数会将 0,0011 存储为 1e-3 而没有舍入)还是非常大(然后 1001 会存储为 1e3),在这两种情况下相关的精度损失很小。
但现在我想尝试不用浮点数,用整数。如何保持我的计算精度?我可以有一个标志告诉我斜坡是大还是小,然后考虑十分之一的大斜坡和十倍的小斜坡,这样圆角对于小斜坡就没有问题,在大斜坡的情况下没有溢出.但这感觉很头疼。
基本上,我仍然需要能够区分 0.2 和 0.4 的斜率,以及 1000 和 2000 的斜率的溢出端(假设整数在 1000 时溢出——这里问题不大)。
还有其他想法吗?
将 斜率 存储为一对整数
struct slope {
int delta_y;
int delta_x;
};
这允许范围广泛的坡度,例如 0
和 +/- 1/INT_MAX ... +/- INT_MAX
,甚至是垂直坡度。通过仔细编码,可以进行精确计算。
延迟学分:这很像
如果您想以一般方式使用 int,请查找 fixed point arithmetic。
您还可以设计您的算法,以便以不需要次整数精度的方式进行每次计算(例如查找 Bresenham's line and circle 绘图算法)。
对于您的特定问题,您可以尝试将商和分数分开,换句话说,使用有理数。或者换句话说,将 delta X 和 delta Y 作为两个数字。
一般来说,对于任意方向的线,不建议使用 slope/intercept 表示 y = mx + p
,而是使用隐式方程 a x + b y + c = 0
。后者更加各向同性,支持垂直线并为您提供额外的灵活性来缩放系数。
见@chux的回答,系数可以是deltas,Dy x - Dx y + c = 0
(假设直线是由两个点定义的,Dx
和Dy
很可能不会溢出) . c
上仍有可能溢出,您可以使用变体 Dy (x - x0) - Dx (y - y0) = 0
.
无论如何,交集等中间计算可能需要更大的范围,即双倍长度整数。
标记 large/low 值的想法有点适得其反:它实际上是一种处理浮点数的原始方法,即将小数位数与尾数分开。以这种方式工作,您将以某种方式重新设计一个浮点系统,它不如内置类型强大,并且会花费您的汗水和泪水。
不幸的是,无法避免高级算术。实际上,两条直线的交点由 Cramer 公式给出
x = (c b' - c' b) / (a b' - a' b),
y = (a c' - a' c) / (a b' - a' b)
其中要评估的产品比初始系数大一个数量级。这是因为准平行线有很远的交点。