2017-01-13 16 views
0

그래서이 작은 창을 사인파를 그리는 프로그램으로 만들었지 만 sin(x)^x과 같은 특정 기능을 오버플로했습니다. 이 코드를 검토하고이 사실을 파악하는 데 도움이됩니다.왜이 C# 함수는 System.Drawing 오버플로를 사용하여 사인파를 그리는가?

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; 
     } 

    } 

내가 실행, 그것은 조금 동안 실행 한 다음이 표시됩니다
what is exactly displayed

모든 기여는 감사합니다!

+0

앞에'float'의 최대 값 인 Int32와 동일합니다. 결과가 2,147,483,647보다 큰 경우 오버플로 오류가 발생합니다. 'double '을 사용해 보셨습니까? (이것은'Int64'와 동일한 최대 값 - 9,223,372,036854775807입니까?)? 또는 오버플로를 처리 할 값을 확인하십시오. – Tim

+1

입력 내용을 펜으로 확인하지 않습니다. 캔버스의 테두리 안에 있는지 확인해야합니다. 실제로 캔버스를 그리는 것이 좋지 않기 때문입니다. – TyCobb

+0

그럼'Width'의 값은 무엇입니까? 이것을 Visual Studio로 떨어 뜨리고 1920과 같은 값을 사용하면'y2 '를 출력하면 많은 결과를 얻을 수 있습니다 (많은 경우 NaN입니다). 그러나 오버플로가 발생하지 않습니다. 그리고 그것은 의미가 있습니다. 정말로 'sin (x)^x'의 범위는 (-1, 1)로 제한 될 것이므로 결코 넘치지 않아야합니다. – Abion47

답변

1

좋아, 솔루션은 TyCobb의 답변과 John Wu의 답변이 섞여있는 것으로 나타났습니다. x과 같이 증가하는 값에 Math.Sin을 사용하면 Math.Sin(x)의 출력의 대략 절반이 음수가됩니다. Math.Pow은 첫 번째 매개 변수에 음수를 지정하고 두 번째 매개 변수로 정수가 아닌 값을 지정하면이 값을 싫어합니다.이 경우 NaN을 반환합니다. 그 이유는 비 - 정수로 음수를 올리면 그 결과가 복소수가되기 때문입니다. 당신은 그냥 아무것도하지 않고 돌아갑니다으로 float 매개 변수 (x2, y2)의 두 번째 쌍 중 하나로서 NaN을 전달하는 경우

지금 Graphics.DrawLine 상관하지 않습니다.그러나 NaN을 첫 번째 쌍 ( x1, y1) 중 하나로 전달하면 OverflowException이 표시됩니다. 왜 그렇게하는지, 나는 잘 모르겠다. 그러나 나는 그날 닷넷 팀의 감독이었던 것 같다.

이제 첫 발견에서부터 주로 x이 부정적 일 때마다 문제가 있음을 알 수 있습니다. 쉬운 솔루션은 xMath.Pow(x)으로 바꾸는 것이지만 원래의 기능의 의도를 반영하지 않는 방식으로 출력을 변경할 수 있습니다. 두 번째 솔루션은 단순히 반복을 건너 뛰는 것이지만 그래프에 구멍이 생깁니다.

당신이 이미 짐작할 수있는 세 번째 해결책은 Math.PowComplex.Pow으로 바꾸는 것입니다. 이렇게하면 복소수를 반환하여 힘의 함수에 도달 할 수있는 잠재적 입력에 필요한 지원을 얻을 수 있습니다. 일단 그렇게하면 결과를 통해 원하는 것을 선택할 수 있습니다. (I 방금 y으로서 결과의 Real 참가하고 Imaginary 부분을 무시).

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; 
} 

결과이있다 :

enter image description here

+0

아주 좋은. 모르겠다. NET에 대한 복잡한 클래스가 있습니다. –

+0

그냥 브라보, 내 친구. 정말 도움이되었습니다. C#에서 Complex.Pow를 사용하여이 문제를 처리 할 수있는 방법이 있다고 추측하지는 못했습니다. 감사! –

0

DrawLine에 대한 값을 확인하십시오.

루프에서 Width에 대한 확인을 수행하지만 DrawLine으로 전화하기 전에 계산의 유효성을 검사하지 않습니다. 여기에 무승부 예는 다음과 같습니다 캔버스를 그릴 때

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; 
} 

Here's an online example

GDI는 실제로는 일반적으로 붐을 갈 것입니다, 당신을 도움이되지 않습니다. 높이와 너비 내에 항상 값을 제한해야합니다.

다음은 예를 몇 가지 하이라이트입니다 :

Snap! 1101,215 | NaN | 1101,465 | NaN 
Snap! 1101,465 | NaN | 1101,715 | NaN 
Snap! 1101,715 | NaN | 1101,965 | NaN 
Snap! 1101,965 | NaN | 1102,215 | NaN 
Snap! 1102,215 | NaN | 1102,465 | NaN 
Snap! 1102,465 | NaN | 1102,715 | NaN 
Snap! 1102,715 | NaN | 1102,965 | NaN 
Snap! 1102,965 | NaN | 1103,215 | NaN 
Snap! 1103,215 | NaN | 1103,465 | NaN 
Snap! 1103,465 | NaN | 1103,715 | NaN 
Snap! 1103,715 | NaN | 1103,965 | NaN 
Snap! 1103,965 | NaN | 1104,215 | NaN 
Snap! 1104,215 | NaN | 1104,465 | NaN 
Snap! 1104,465 | NaN | 1104,715 | NaN 
Snap! 1104,715 | NaN | 1104,965 | NaN 
Snap! 1104,965 | NaN | 1105,215 | NaN 
+0

'g.DrawLine (Pens.Black, -1, -1, 1200, 800);이있는 for 루프의 내부 구조 (크기가 1024x768 인'Bitmap'에서)는 정상적으로 실행됩니다. 이 경우 캔버스 가장자리가 문제가됩니다. – Abion47

+0

@ Abion47 예제 및 코드 데모를 업데이트했습니다. – TyCobb

0

문제는 Math.Pow 때로는 not a number or NaN입니다 float를 반환합니다. 구체적으로 :

x < 0 음수인데, y는 정수, NegativeInfinity 또는 PositiveInfinity가 아닙니다. = NaN이

이후 씬 (X)의 값의 일부를 따라서 Math.Pow 호출 수득한다 물론 직교 좌표 공간 상에 그려 질 수 NaN이 부정적인 것 이상 0 이하 모두 인 곡선을 그리는 것 .

다음과 같이 코드를 임시 수정하는 것이 좋습니다. try 블록으로 그리기 호출을 래핑하십시오.

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 함수의 결과에 상수를 추가합니다.

+0

'Graphics.DrawX' 메서드의 입력으로'NaN'을 사용하는 것은 효과가 없을 것입니다. – Abion47

+0

네거티브라고 말하면 GDI는 음수를 전달할 때 Out of Memory 예외를 던지기 쉽습니다. 합법적으로 실행되지 않았다고 생각할 때 재미있는 디버그 시간입니다. 기억.;) – TyCobb