2016-06-28 17 views
2

그래서 QBasic 4.5 코드를 C로 변환하려고합니다. 정확한 결과를 얻으려고하고 있는데 QB가 어떻게 계산되는지 이해할 수 없습니다.왜 이러한 무한 QB 계산은 약간 다른 값을 생성합니까?

, 이들 두 줄

DIM a AS SINGLE 
DIM d2 AS SINGLE 
DIM e2 AS SINGLE 

a = 32.174 
d2 = 1!/(2! * 32.174 * 144!) 
e2 = 1!/(2! * a! * 144!) 

D2는 1.07920125E -4- (부동 소수점 0x38e2532d)

E2는 1.0792013E-4 적이다 (부동 소수점 0x38e2532e)

된다된다 너무 약간 다릅니다. 아무도 왜 그 이유를 이해하도록 도와 줄 수 있습니까? 매우 감사합니다.

답변

2

어떤 QB 버전을 사용하고 있습니까? 변수를 인쇄하거나 출력하는 방법은 d2e2입니까?

DOSBox 0.74 QuickBASIC 4.5에서 프로그램을 시도 할 때 다른 값을 가져 오지 않습니다. d2와 e2는 PRINT 일 때 동일합니다. 이 AS SINGLE 동일 그래서

a = 32.174 
d2 = 1.079201E-04 
e2 = 1.079201E-04 

exlamation 마크 연산자, SINGLE (단 정밀도 4 바이트)에 캐스트한다. 어쩌면 d2 = 1! /.. 라인의 값 32.174이 어떻게 든 DOUBLE로 타입 변환되고 있습니까?

+0

인쇄 할 때 반올림이 진행되는 것처럼 보입니다. 차이점은 "201"입니다. 나는 그들을 실제로 "PUT #"을 사용하여 파일에 쓰고 (나는 어쨌든 그것을하고 있었다), 그리고 그 헥스를보고있다. 또한 접미사 '!'를 붙이면 그것 32.174에, IDE는 그것을 삭제합니다. 여기서 나는 SINGLE이 암시 적이기 때문에 '!' 불필요합니다. – Chris

+0

죄송합니다 ... 32 비트 시스템에서 QB4.5 사용. 나는 DOSBOX를 64 비트에 가지고 있지만 그것을 시도하지는 않았다. – Chris

+0

나는 SINGLE 변수를 출력하고 검사하기 위해'PUT # '에 익숙하지 않다. 하지만 어쨌든 내 QuickBasic IDE는 또한 줄을 편집 한 후 자동으로'!'를 제거하므로 변수는 이미 암묵적으로 단일 것처럼 말합니다. Btw 뒤에'# '을 추가하면 제거되지 않은 DOUBLE임을 나타냅니다. – BdR

5

값의 원시 바이트 표현과 관련하여도 d2e2에 대해 동일한 출력을 얻고 있습니다. 여기에 몇 가지 주석 출력입니다 :

# Calculation results 
d2: 38 E2 53 2E 
e2: 38 E2 53 2E 
1.079201E-04 = 1.079201E-04 

# Result of changing the last byte (some mantissa bits) to alter the value, 
# proving they're not equal 
d2: 38 E2 53 2F 
e2: 38 E2 53 2E 
1.079201E-04 <> 1.079201E-04 

# Result above may just be luck. This result alters the first byte 
# (some exponent bits) to prove that the intended bits were altered. 
d2: 39 E2 53 2E 
e2: 38 E2 53 2E 
4.316805E-04 <> 1.079201E-04 

코드 :

@BlackJack이 주석에서 설명하고있는 바와 같이, 당신은 몰래하고있는 효과가 파일을 컴파일 할 때 발생하는 것으로 나타

DIM a AS SINGLE 
DIM SHARED d2 AS SINGLE 
DIM SHARED e2 AS SINGLE 

a = 32.174 
d2 = 1!/(2! * 32.174 * 144!) 
e2 = 1!/(2! * a! * 144!) 

' Print the hex representation of the bytes 
' and show they're initially equal. 
CALL printHex 
PRINT 

' Change the last byte of the mantissa by 1 bit. 
' Show that doing this makes the two values unequal. 
DEF SEG = VARSEG(d2) 
    POKE VARPTR(d2), PEEK(VARPTR(d2)) + 1 
DEF SEG 
CALL printHex 
PRINT 

' Show that the correct byte was poked by reverting mantissa change and 
' altering exponent. 
DEF SEG = VARSEG(d2) 
    POKE VARPTR(d2), PEEK(VARPTR(d2)) - 1 
    POKE VARPTR(d2) + 3, PEEK(VARPTR(d2) + 3) + 1 
DEF SEG 
CALL printHex 

SUB printHex 
    'SHARED variables used: 
    ' d2, e2 

    DIM d2h AS STRING * 8, e2h AS STRING * 8 

    ' Get bytes of d2 and e2, storing them as hexadecimal values 
    ' in d2h and e2h. 
    DEF SEG = VARSEG(d2) 
     MID$(d2h, 1) = hexByte$(PEEK(VARPTR(d2) + 3)) 
     MID$(d2h, 3) = hexByte$(PEEK(VARPTR(d2) + 2)) 
     MID$(d2h, 5) = hexByte$(PEEK(VARPTR(d2) + 1)) 
     MID$(d2h, 7) = hexByte$(PEEK(VARPTR(d2))) 
    DEF SEG = VARSEG(e2) 
     MID$(e2h, 1) = hexByte$(PEEK(VARPTR(e2) + 3)) 
     MID$(e2h, 3) = hexByte$(PEEK(VARPTR(e2) + 2)) 
     MID$(e2h, 5) = hexByte$(PEEK(VARPTR(e2) + 1)) 
     MID$(e2h, 7) = hexByte$(PEEK(VARPTR(e2))) 
    DEF SEG 

    ' Print the bytes, separating them using spaces. 
    PRINT "d2: "; MID$(d2h, 1, 2); " "; MID$(d2h, 3, 2); " "; 
    PRINT MID$(d2h, 5, 2); " "; MID$(d2h, 7, 2) 
    PRINT "e2: "; MID$(e2h, 1, 2); " "; MID$(e2h, 3, 2); " "; 
    PRINT MID$(e2h, 5, 2); " "; MID$(e2h, 7, 2) 

    ' Print whether d2 is equal to e2. 
    IF d2 = e2 THEN 
     PRINT d2; "= "; e2 
    ELSE 
     PRINT d2; "<>"; e2 
    END IF 
END SUB 

FUNCTION hexByte$ (b%) 
    ' Error 5 is "Illegal function call". 
    ' This can only happen if b% is outside the range 0..255. 
    IF b% < 0 OR b% > 255 THEN ERROR 5 

    ' MID$("0" + HEX$(15), 2 + (-1)) => MID$("0F", 1) => "0F" 
    ' MID$("0" + HEX$(16), 2 + (0)) => MID$("010", 2) => "10" 
    hexByte$ = MID$("0" + HEX$(b%), 2 + (b% < 16)) 
END FUNCTION 

편집 . 그는 단서가 주어진 이후, 나는 도스 박스에서의 CodeView 디버거를 사용하고, 여기에 요약 된 결과입니다 : 기본 컴파일러 (BC.EXE)는 부동 소수점의 간단한 과제에 d2에 할당을 감소

5: a = 32.174 057D:0030 C70636002DB2 MOV Word Ptr [0036],B22D 057D:0036 C70638000042 MOV Word Ptr [0038],4200 6: d2 = 1!/(2! * 32.174 * 144!) 057D:003C C7063A002D53 MOV Word Ptr [003A],532D 057D:0042 C7063C00E238 MOV Word Ptr [003C],38E2 7: e2 = 1!/(2! * a! * 144!) 057D:0048 CD35065000 FLD DWord Ptr [0050]; 00 CB 21 CD 057D:004D CD34363600 FDIV DWord Ptr [0036]; 42 00 B2 2D 057D:0052 CD351E3E00 FSTP DWord Ptr [003E]; e2 = result 057D:0057 CD3D FWAIT 

상수 (즉, 표현식 자체를 평가하고 지정된 모든 작업을 수행하는 대신 단일 할당으로 코드를 최적화했습니다. 그러나 e2에 대한 할당은 비상 수식 a!을 포함하므로 간단하지 않습니다.

문제에 대처하고 가능한 한 많은 정밀도를 유지하려고하기 위해서는 (1/288)/a 수학적으로 동등한 1/(2 * a * 144)을 변경하고, 1/288의 대략적인 값은 FLD 오프셋로드를 결국 이유입니다, 0x0050 오프셋에서 보관 하였다. SINGLE 값을로드 한 후 a (오프셋 0x0036) 값으로 나눠서 결과를 e2 (오프셋 0x003E)에 저장했습니다. CONST a = 32.174을 사용하여 e2d2과 동일하게 지정할 수 있지만 해당 값을 변경할 수는 없습니다.

누군가가 궁금해 할 것입니다. 누군가 IDE에 컴파일되지 않았을 때만 나타나는 이유가 궁금 할 것입니다. 솔직히 모르겠습니다. 내 생각에 IDE는 정밀도를 유지할 수있는만큼 FP 스택에 많은 수레를 유지하므로, a의 32 비트 반올림 값 대신 FP 스택에 저장된 기존 80 비트 값을 사용합니다 , 아직 거기에 저장되어 있다면. 이렇게하면 FP 스택 외부에 80 비트 값을 저장하면 값을 저장하기 위해 지정한 위치에 따라 가장 가까운 32 비트 또는 64 비트 값으로 반올림해야하기 때문에 정밀도 손실이 줄어 듭니다. 물론 FP 스택에 8 개 이상의 값이 필요한 경우 다른 스택을 사용하기 위해 스왑 아웃해야하므로 정밀 손실이 결국 나타납니다.

@BlackJack은 IDE가 최적화로 코드를 컴파일하는 대신 코드를 해석한다는 것을 지적했습니다. IDE가 코드를 IDE에서 실행했지만 컴파일 된 버전이 다른 경우 바이트 표현이 동일하다는 이유가 될 수 있습니다. 즉, d2e2의 계산은 d2의 계산을 BC.EXE와 같은 단일 값으로 최적화하는 것과 완전히 똑같은 방법으로 실행됩니다.

현대의 컴파일러가 BC.EXE보다 최적화 된 경우 현대의 컴파일러가 훨씬 더 똑똑하고 현대적인 도움 없이도 작업 할 수있는 메모리가 많기 때문에 C 코드에서이 코드를 인식하지 못할 가능성이 높습니다 SSE2와 같은 부동 소수점 기술.

+2

니스, 그건 철저한 조사와 설명입니다. 나의 유일한 코멘트는'MID $ + PRINT' 부분을'SUB '에 넣을 수 있다는 것입니다. – BdR

+0

와우, 멋진 작품입니다. 당신은 분명히 QB에서 세계의 나머지 전문가 중 하나입니다. :) 테이크 어웨이는 내가 문제를 경험하고 있다는 것을 알 것입니다. – Chris

+0

@Chris 전문가가 아닙니다. 그냥 C와 DOS에 대한 지식을 QB로 변환합니다. –