(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 类型相关联?我认为它只包含一个长度属性?干杯 MetersSquaredMetersCubed 对于适当的属性,我想。我认为硬核这样的维度数量可能很好。我怀疑你没有做 n 维几何 :)【参考方案3】:

对无限值抛出异常是一种设计选择

IMO,对此没有“好的答案”,这取决于您希望如何使用您的课程。例如,如果目标是绘图,那么防止大值可能是有意义的。

对于计算和比较,我不会确定。为 IEEE 标准浮点值定义的 Inf-Inf(和 NaN)值是一致的。该课程的用户知道您正在使用double,因此,可以期待与他们一起使用的所有功能。

哪个形状的面积最大,一个边长为 10 个单位的正方形,还是一个你认为它是无限大的圆形?答案是一致的,因为Inf > 100true

如果你打算失败,早点失败

如果你决定走(合理的)异常路径,我建议尽早失败。

这意味着,不要在吸气剂中计算面积/周长,而是尽快计算面积和周长(如果您的类是不可变的,则在构造函数中,或者在相关其他属性(如直径/长度等)的设置器中。 .) 并检查当时的任何无限值。

此外,还要检查 NaNs 的值。

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。抛出溢出异常?的主要内容,如果未能解决你的问题,请参考以下文章

IEEE 754 中 Infinity 的用例是啥

在 JavaScript 中,为啥零除以零返回 NaN,而任何其他除以零返回 Infinity?

使用 Infinity 和 NaN 禁用异常

C# 后台工作人员 - 我应该同时使用多少?

网络操作系统安全与防范方面的论文应该写些啥?

如何解决某些图像上的颤振“Infinity or NaN toInt”