값의 원시 바이트 표현과 관련하여도 d2
과 e2
에 대해 동일한 출력을 얻고 있습니다. 여기에 몇 가지 주석 출력입니다 :
# 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
을 사용하여 e2
을 d2
과 동일하게 지정할 수 있지만 해당 값을 변경할 수는 없습니다. 누군가가 궁금해 할 것입니다. 누군가 IDE에 컴파일되지 않았을 때만 나타나는 이유가 궁금 할 것입니다. 솔직히 모르겠습니다. 내 생각에 IDE는 정밀도를 유지할 수있는만큼 FP 스택에 많은 수레를 유지하므로, a
의 32 비트 반올림 값 대신 FP 스택에 저장된 기존 80 비트 값을 사용합니다 , 아직 거기에 저장되어 있다면. 이렇게하면 FP 스택 외부에 80 비트 값을 저장하면 값을 저장하기 위해 지정한 위치에 따라 가장 가까운 32 비트 또는 64 비트 값으로 반올림해야하기 때문에 정밀도 손실이 줄어 듭니다. 물론 FP 스택에 8 개 이상의 값이 필요한 경우 다른 스택을 사용하기 위해 스왑 아웃해야하므로 정밀 손실이 결국 나타납니다.
@BlackJack은 IDE가 최적화로 코드를 컴파일하는 대신 코드를 해석한다는 것을 지적했습니다. IDE가 코드를 IDE에서 실행했지만 컴파일 된 버전이 다른 경우 바이트 표현이 동일하다는 이유가 될 수 있습니다. 즉, d2
과 e2
의 계산은 d2
의 계산을 BC.EXE와 같은 단일 값으로 최적화하는 것과 완전히 똑같은 방법으로 실행됩니다.
현대의 컴파일러가 BC.EXE보다 최적화 된 경우 현대의 컴파일러가 훨씬 더 똑똑하고 현대적인 도움 없이도 작업 할 수있는 메모리가 많기 때문에 C 코드에서이 코드를 인식하지 못할 가능성이 높습니다 SSE2와 같은 부동 소수점 기술.
인쇄 할 때 반올림이 진행되는 것처럼 보입니다. 차이점은 "201"입니다. 나는 그들을 실제로 "PUT #"을 사용하여 파일에 쓰고 (나는 어쨌든 그것을하고 있었다), 그리고 그 헥스를보고있다. 또한 접미사 '!'를 붙이면 그것 32.174에, IDE는 그것을 삭제합니다. 여기서 나는 SINGLE이 암시 적이기 때문에 '!' 불필요합니다. – Chris
죄송합니다 ... 32 비트 시스템에서 QB4.5 사용. 나는 DOSBOX를 64 비트에 가지고 있지만 그것을 시도하지는 않았다. – Chris
나는 SINGLE 변수를 출력하고 검사하기 위해'PUT # '에 익숙하지 않다. 하지만 어쨌든 내 QuickBasic IDE는 또한 줄을 편집 한 후 자동으로'!'를 제거하므로 변수는 이미 암묵적으로 단일 것처럼 말합니다. Btw 뒤에'# '을 추가하면 제거되지 않은 DOUBLE임을 나타냅니다. – BdR