(C#) 我应该在计算中防范 Infinity/-Infinity 吗? IE。抛出溢出异常?
Posted
技术标签:
【中文标题】(C#) 我应该在计算中防范 Infinity/-Infinity 吗? IE。抛出溢出异常?【英文标题】:(C#) Should I Guard Against Infinity/-Infinity in my calculations? I.e. Throw an Overflow Exception? 【发布时间】:2020-05-17 19:39:35 【问题描述】:在 C# 中寻找有关 Infinity/-Infinity 的建议。
我目前正在构建几何类,用于矩形/圆形等形状。 用户将分别提供宽度/深度和直径的输入。
这些属性将是double
,我理解不是显式地溢出,如果你将double.MaxValue
乘以2,那么你会得到+infinity 等。
每个形状类都有额外的属性,例如面积、周长等。 因此,即使提供的维度小于 MaxValue,如果用户愿意,计算值也有可能是一个巨大的数字:
例如Math.PI x Math.Pow(diameter, 2) / 4
=> Math.PI x Math.Pow(double.MaxValue, 2) / 4
(即,即使用户提供 MaxValue 作为输入,此方法也会导致 +infinity。)
我的问题是我是否应该一直防范无穷大? 如果用户值或计算值输入无穷大,我应该抛出异常(OverflowException)吗?
看起来它可能是一种代码气味来检查这些几何类中每个属性/方法的无穷大... 有没有更好的办法?
public double Area
get
double value = Math.PI * Math.Pow(this.diameter, 2) / 4;
if (double.IsInfinity(value)
throw new OverflowException("Area has overflowed.");
public double Perimeter
get
double value = Math.PI * this.diameter;
if (double.IsInfinity(value)
throw new OverflowException("Perimeter has overflowed.");
感谢您的时间和想法! 干杯。
【问题讨论】:
@PavelAnikhouski 这与浮点运算无关,除了转换为整数类型。 嗨@PavelAnikhouski,感谢您的评论!我的理解是选中和未选中仅适用于 int 类型?而 double/float overflow 到无穷大?我误会了吗? 【参考方案1】:每个形状类都将具有额外的属性,例如面积、周长等。因此,即使提供的尺寸小于 MaxValue,如果用户如此倾向于计算的值也有可能是一个巨大的数字
double max 是1.7976931348623157E+308
,担心用户输入一些达到该限制的几何数据是没有意义的。简单地归还无穷大,它可能永远不会发生。换个角度来看,以微米为单位的 Proxima Centauri 的距离大约是 4,014 × 10^22
,所以您会注意到担心这是多么浪费时间。
为了至少给用户一个合理的选择,你可以让他定义你的几何环境中的一个单位代表什么;一微米、毫米、米、千米、光年、秒差距等,因此他将始终将事物保持在合理的范围内。
【讨论】:
感谢@InBetween,您的论点很有道理。我可以看到我可能什么都不担心,但我担心如果有人真的想破坏应用程序,那么他们可以(即使普通用户对此不感兴趣)。我从防御性编码的角度来看待这个问题,但风险是如此之低,我想它甚至不值得强调。干杯【参考方案2】:从概念上讲,您遇到了设计问题。导致无限区域的值会导致您的形状处于无效状态。
例如,假设所讨论的形状是圆形。传入的任何导致Area > double.MaxValue
的值都是无效状态。换句话说,diameter
的任何值 Math.PI * Math.Pow(diamater, 2) > double.MaxValue
都是无效的。
求解double.MaxValue = Math.PI * Math.Pow(diamater, 2)
为diameter
(它是1.5129091144565236E+154
,欢迎您验证我的数学),并在您的构造函数中检查它,
private const double MaxDiameter = 1.5129091144565236E+154;
Circle(double diameter)
if (diameter > MaxDiameter)
throw ArgumentOutOfRangeException();
--
以上可能都不重要
在实践中,请考虑以下事项。已知宇宙的直径约为 30 gigaparsecs,转换为普朗克长度(我能想到的最小距离单位)约为 5.7e61,即远,远低于double.MaxValue
。
我不担心您的属性中的双重溢出。设计你的形状是明智的,所以它使用你定义的单位类型,所以你总是知道你正在使用什么单位。
public Circle(Meters diamater)
public Rectangle(Meters width, Meters height)
【讨论】:
谢谢@jdphenix,我完全同意你的逻辑。我确实想知道我是否是在用鼹鼠山造山!哈哈,我想我只是担心有人会仅仅为了它而尝试破坏应用程序。但我猜他们只会打破自己的流程,所以不值得强调。感谢您的贡献,非常感谢!我唯一的后续问题是您会将哪种字段/属性与专用 Meters 类型相关联?我认为它只包含一个长度属性?干杯MetersSquared
和 MetersCubed
对于适当的属性,我想。我认为硬核这样的维度数量可能很好。我怀疑你没有做 n 维几何 :)【参考方案3】:
对无限值抛出异常是一种设计选择
IMO,对此没有“好的答案”,这取决于您希望如何使用您的课程。例如,如果目标是绘图,那么防止大值可能是有意义的。
对于计算和比较,我不会确定。为 IEEE 标准浮点值定义的 Inf
和 -Inf
(和 NaN
)值是一致的。该课程的用户知道您正在使用double
,因此,可以期待与他们一起使用的所有功能。
哪个形状的面积最大,一个边长为 10 个单位的正方形,还是一个你认为它是无限大的圆形?答案是一致的,因为Inf > 100
是true
。
如果你打算失败,早点失败
如果你决定走(合理的)异常路径,我建议尽早失败。
这意味着,不要在吸气剂中计算面积/周长,而是尽快计算面积和周长(如果您的类是不可变的,则在构造函数中,或者在相关其他属性(如直径/长度等)的设置器中。 .) 并检查当时的任何无限值。
此外,还要检查 NaN
s 的值。
class Circle
public Circle(double diameter)
if (double.IsInfinity(diameter) || double.IsNaN(diameter))
throw new ArgumentOutOfRangeException(nameof(diameter));
Diameter = diameter;
Perimeter = Math.PI * diameter;
if (double.IsInfinity(Perimeter))
throw new OverflowException("Perimeter has overflowed.");
double Diameter get;
double Perimeter get;
【讨论】:
以上是关于(C#) 我应该在计算中防范 Infinity/-Infinity 吗? IE。抛出溢出异常?的主要内容,如果未能解决你的问题,请参考以下文章