为什么这个C#函数使用System.Drawing overflows绘制正弦波?

为什么这个C#函数使用System.Drawing overflows绘制正弦波?,c#,winforms,graph,drawing,trigonometry,C#,Winforms,Graph,Drawing,Trigonometry,所以我制作了一个windows窗体的小程序,它可以画一个正弦波,但是当我放置一些函数,比如sin(x)^x时,它会溢出。 请检查此代码并帮助我解决此问题 private void button1_Click(object sender, EventArgs e) { if (Completed) return; Completed = true; Graphics g = this.CreateGraphics();

所以我制作了一个windows窗体的小程序,它可以画一个正弦波,但是当我放置一些函数,比如
sin(x)^x
时,它会溢出。 请检查此代码并帮助我解决此问题

    private void button1_Click(object sender, EventArgs e)
    {
        if (Completed) return;
        Completed = true;
        Graphics g = this.CreateGraphics();
        Pen pen = new Pen(Brushes.Black, 2.0F);

        float x1 = 0;
        float y1 = 0;

        float y2 = 0;

        float yEx = 300;
        float eF = 50;


        for (float x = 0; x < Width; x += 0.005F)
        {
            y2 = (float)Math.Pow(Math.Sin(x) , x);

            g.DrawLine(pen, x1 * eF, y1 * eF + yEx, x * eF, y2 * eF + yEx);
            x1 = x;
            y1 = y2;
        }

    }
private void按钮1\u单击(对象发送者,事件参数e)
{
如果(完成)返回;
完成=正确;
Graphics g=this.CreateGraphics();
画笔=新画笔(画笔.黑色,2.0F);
浮点x1=0;
浮动y1=0;
浮动y2=0;
浮动yEx=300;
浮动eF=50;
用于(浮动x=0;x
当我执行时,它运行一段时间,然后显示:


感谢您的任何贡献

验证
绘制线的值

在循环中检查
宽度
,但在调用
绘图线
之前不验证计算。下面是一个没有绘图的示例:

float x1 = 0;
float y1 = 0;

float y2 = 0;

float yEx = 300;
float eF = 50;

const int width = 1024;
const int height = 1024;

for (float x = 0; x < width; x += 0.005F)
{
    y2 = (float)Math.Pow(Math.Sin(x) , x);

    var a = x1 * eF;
    var b = y1 * eF + yEx; 
    var c = x * eF;
    var d = y2 * eF + yEx;

    if(a < 0 || a >= width || b < 0 || b >= height || c < 0 || c >= width || d < 0 || d > height)
        Console.WriteLine("Snap! {0} | {1} | {2} | {3}", a, b, c, d);
    x1 = x;
    y1 = y2;
}
问题是,有时返回的浮点值是。具体而言:

x<0,但不具有负不确定性;y不是整数、负整数或正整数楠

由于Sin(x)将绘制一条既高于0又低于0的曲线,它的一些值将为负值,因此
Math.Pow
调用将生成NaN,这当然不能在笛卡尔空间中绘制

我建议您临时修改代码如下:使用try块包装draw调用

try
{
    g.DrawLine(pen, x1 * eF, y1 * eF + yEx, x * eF, y2 * eF + yEx);
}
catch {}  //Not usually a great idea to eat all exceptions, but for troubleshooting purposes this'll do

然后运行代码并查看行。行中的分隔符将告诉您域/范围的哪些部分有问题。一旦你对计算问题有了一个整体的看法,你就可以将笛卡尔空间转换/映射到一个不会发生溢出的域,例如,通过在SIN函数的结果中添加一个常数。

好的,所以解决方案是TyCobb的答案和John Wu的答案的混合。当您对递增的值(如
x
)使用
Math.Sin
时,
Math.Sin(x)
的输出大约有一半为负值
Math.Pow
不喜欢在第一个参数中给它一个负数,在第二个参数中给它一个非整数(在这种情况下,它将返回
NaN
)。这是因为当你将一个负数乘以一个非整数时,结果是一个复数

现在
Graphics.DrawLine
不关心是否将
NaN
作为第二对
float
参数(
x2,y2
)中的任何一个传递,因为它只会返回而不做任何操作。但是如果将
NaN
作为第一对(
x1,y1
)中的一个传递,它将抛出
溢出异常。我不确定它为什么会这样做,但我愿意猜测这是.NET团队当年的疏忽

现在,从第一个发现中,您可以看到问题主要出现在
x
为负值时。解决这个问题的简单方法是将
x
替换为
Math.Pow(x)
,但这可能会以不反映原始函数意图的方式改变输出。第二种解决方案是跳过这些迭代,但这会在图形中留下漏洞

您可能已经猜到了第三种解决方案,但那就是将
Math.Pow
替换为
Complex.Pow
。这将为您提供所需的支持,使您能够通过返回复数获得幂函数的潜在输入。完成后,您可以选择要对结果执行的操作。(我只是将结果的
实部
作为我的
y
并丢弃
虚部。)

for(浮动x=0;x
结果是:


浮点值的最大值与
Int32
相同。如果结果大于2147483647,您将得到溢出错误。您是否尝试过使用
double
(与
Int64
-9223372036854775807的最大值相同)?或者检查值以处理溢出。您没有验证笔中的输入。你应该确保它们在画布的范围内,因为它不喜欢什么都不画。那么
Width
的值是多少呢?将其放入Visual Studio并使用类似1920的值,然后打印
y2
,可以得到很多结果(其中许多是
NaN
),但我从未得到溢出。这是有道理的,真的,
sin(x)^x
的范围将被限制为(-1,1),这样就永远不会溢出。失败的第一个数字是
sin(3.15002465)^3.15002465
,这将导致@我想数学没那么容易;)@kazemAkhgary先生从来都不是(使用
NaN
作为
图形的输入。DrawX
方法不起作用,但也不会引发异常。它什么都不做。说到负数,当你传入负数时,GDI也会抛出内存不足异常。当你认为你合法地耗尽了我的内存时,这是一段有趣的调试时间mory.;用
g.抽绳(Pens.Black,-1,-1,1200,800)替换for回路的内部(在大小为1024x768的
位图上)运行正常。我不认为画画布的边缘是这种情况下的问题。非常好。我不知道.net有这么复杂的类。太棒了,我的朋友。真的很有帮助。我想不到C#有办法用Complex.Pow处理这个问题。谢谢
try
{
    g.DrawLine(pen, x1 * eF, y1 * eF + yEx, x * eF, y2 * eF + yEx);
}
catch {}  //Not usually a great idea to eat all exceptions, but for troubleshooting purposes this'll do
for (float x = 0; x < Width; x += 0.005F)
{
    y2 = (float)Complex.Pow(Math.Sin(x), x).Real;

    g.DrawLine(pen, x1 * eF, y1 * eF + yEx, x * eF, y2 * eF + yEx);
    x1 = x;
    y1 = y2;
}