2013-06-07 5 views
8

मैंने अभी पाया है कि एक सॉफ़्टवेयर जिसे मैं पुन: कार्यान्वित करना चाहता हूं, extensivelly System.Round() का उपयोग करता है। समस्या यह है कि यह फ़ंक्शन "बैंकर राउंडिंग" का उपयोग करता है और व्यवहार को Math.RoundTo() (rmDown, rmUp, rmNearest, rmTruncate) में बदला नहीं जा सकता है।डेल्फी फ़ंक्शन सिस्टम ओवरराइड करें। राउंड

मुझे व्यवहार को "सामान्य राउंडिंग" (12.5 -> 13 नहीं 12.5 -> 12) में बदलना है ... तो मैं वैश्विक स्तर पर System.Round() को ओवरराइड करना चाहता हूं। मैं ऐसा करना चाहता हूं, क्योंकि गोल() का उपयोग कई बार किया जाता है और मैं उन्हें मैन्युअल रूप से बदलना नहीं चाहता हूं।

यह कैसे संभव है?

+3

System.round के व्यवहार वास्तव में सेट किया जा सकता http://docwiki.embarcadero.com/Libraries/XE2/en/System.Round के अनुसार। – RobS

+9

आप प्रकाशित 'फ़ंक्शन राउंड (विस्तारित): विस्तारित' के साथ केवल नई इकाई बना सकते हैं, इस इकाई को अपनी प्रत्येक स्रोत फ़ाइलों के 'उपयोग' अनुभाग में शामिल करें और पुन: संकलित करें। यदि आपने पूरी तरह से योग्य नाम का उपयोग नहीं किया है (और लगभग कोई भी इसे सर्वव्यापी आरटीएल फ़ंक्शन के लिए नहीं करता है) तो ई-संकलन पर आपका नया फ़ंक्शन 'System.Round' से अधिक दिखाई देगा। वैश्विक व्यवहार को बदलने से आपके प्रोग्राम में कुछ तृतीय-पक्ष कोड के लिए अजीब परिणाम हो सकते हैं, –

+0

सहमत - यह सबसे आसान तरीका है। +1 –

उत्तर

14

चेतावनी: हालांकि नीचे दिया गया उत्तर उस प्रश्न को संबोधित करता है जो पूछा गया था, मैं अनुशंसा करता हूं कि कोई भी इसका उपयोग कभी नहीं करेगा। यदि आप Round से अलग-अलग गोल करना चाहते हैं तो एक समर्पित फ़ंक्शन लिखें और कॉल करें।


आप Round के कार्यान्वयन को बदलने के लिए रनटाइम कोड हुक का उपयोग कर सकते हैं।

शिकन यह है कि Round फ़ंक्शन के पते को पकड़ना थोड़ा मुश्किल है, हालांकि यह एक आंतरिक है। इस्तेमाल किए गए कॉलिंग सम्मेलन का पालन करने के लिए आपको सावधान रहना होगा। इनपुट मान x87 स्टैक रजिस्टर ST(0) में पारित किया गया है और वापसी मान EDX:EAX में 64 बिट पूर्णांक है।

यहां यह कैसे करें।

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer); 
var 
    OldProtect: DWORD; 
begin 
    if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then 
    begin 
    Move(NewCode, Address^, Size); 
    FlushInstructionCache(GetCurrentProcess, Address, Size); 
    VirtualProtect(Address, Size, OldProtect, @OldProtect); 
    end; 
end; 

type 
    PInstruction = ^TInstruction; 
    TInstruction = packed record 
    Opcode: Byte; 
    Offset: Integer; 
    end; 

procedure RedirectProcedure(OldAddress, NewAddress: Pointer); 
var 
    NewCode: TInstruction; 
begin 
    NewCode.Opcode := $E9;//jump relative 
    NewCode.Offset := 
    NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode); 
    PatchCode(OldAddress, NewCode, SizeOf(NewCode)); 
end; 

function System_Round: Pointer; 
asm 
    MOV  EAX, offset [email protected] 
end; 

procedure _ROUND; 
asm 
     { -> FST(0) Extended argument  } 
     { <- EDX:EAX Result     } 

     // your implementation goes here 
end; 

initialization 
    RedirectProcedure(System_Round, @_ROUND); 

आप बल्कि एएसएम से पास्कल में अपने संस्करण को लागू होता है तो आप मानक डेल्फी बुला सम्मेलन को _ROUND की अमानक बुला सम्मेलन अनुकूल करने के लिए की जरूरत है। इस तरह:

function MyRound(x: Extended): Int64; 
begin 
    // your implementation goes here 
end; 

procedure _ROUND; 
var 
    x: Extended; 
asm 
     { -> FST(0) Extended argument  } 
     { <- EDX:EAX Result     } 

     FSTP TBYTE PTR [x] 
     CALL MyRound 
end; 

ध्यान दें कि मैंने यहां माना है कि आपका प्रोग्राम 32 बिट को लक्षित कर रहा है। यदि आपको 64 बिट को लक्षित करने की आवश्यकता है तो सिद्धांत बहुत समान हैं, लेकिन विवरण स्पष्ट रूप से भिन्न हैं।

+2

+1 क्या कोई ऐसी चीज है जिसे आप नहीं जानते? – jpfollenius

+1

@Smasher Ha, धन्यवाद! ऐसी चीजों की भारी मात्रा में हैं। रनटाइम कोड हुकिंग हमेशा वास्तव में तुलना में स्पष्ट दिखता है। 'System_Round' वाली चाल वह है जिसे मैंने माधशी के पागल एक्सेप्ट से सीखा। यहां कुछ भी मूल नहीं है। –

+1

@ डेविड हेफरन - यह अभी भी विश्वकोश ज्ञान है, भले ही आप अपने मूल –

7
UNIT MathRound; 

INTERFACE 

FUNCTION ROUND(X : Extended) : Int64; 

IMPLEMENTATION 

FUNCTION ROUND(X : Extended) : Int64; 
    BEGIN 
    Result:=TRUNC(X+0.5) 
    END; 

END. 

आप मैं अपने प्रोजेक्ट की निर्देशिका MathRound.PAS में ऊपर सहेजते हैं, तो अपनी स्रोत फ़ाइल में इस इकाई में शामिल हैं, तो आप बैंकर का राउंडिंग कि डिफ़ॉल्ट रूप से कार्यान्वित किया जाता है के बजाय एक गणितीय ROUND समारोह होगा।

यह -12.5 से -12 (यानी हमेशा .5 मानों के लिए शून्य की ओर घूमता है) और -12.1 से -11 तक होगा।

IF X<0.0 THEN Result:=-TRUNC(ABS(X)+0.5) ELSE Result:=TRUNC(X+0.5) 
समारोह शरीर के रूप में

: आप एक अधिक "तार्किक" राउंडिंग चाहते हैं, आप के बजाय इस लाइन का उपयोग करना चाहिए।

यह परिणाम

ROUND(12.5) = 13 
ROUND(12.1) = 12 
ROUND(-12.5)= -13 
ROUND(-12.1)= -12 
+0

आईएमएचओ यह सबसे सरल और स्वच्छ समाधान – ComputerSaysNo

1

में आप समय और प्रयास मैन्युअल कुछ और कॉल करने के लिए अपने सभी मौजूदा Round कॉल बदलने के लिए आवश्यक को लेकर चिंतित हैं जाएगा। तो उन्हें मैन्युअल रूप से मत बदलें। इसे स्वचालित करने के लिए एक उपकरण का उपयोग करें। उदाहरण के लिए, आप sed का उपयोग कर सकते हैं।

sed -i -e "s/\bRound\b/BiasedRoundAwayFromZero/g" *.pas 

उस परिवर्तन के साथ, आपका कोड अब इस बारे में स्पष्ट है कि इसका उपयोग किस दौर में किया जाता है।यह आपके कोड को पढ़ने के लिए हर किसी को यह जानने के लिए आवश्यक नहीं है कि मानक कार्यों के वैश्विक व्यवहार को प्रभावित करने के लिए कोड में कहीं और पैच लागू किया गया हो। यह आपके द्वारा अन्य पुस्तकालयों से लिंक किए गए कोड को भी प्रभावित नहीं करता है, जो Round के मानक व्यवहार पर भरोसा कर सकता है और वैश्विक परिवर्तन से टूटा जा सकता है।

+1

है जो बहुत अंधाधुंध होने के लिए उत्तरदायी है। निश्चित रूप से कोड में "राउंड" शब्द के अन्य उपयोग होंगे। –

+0

मुझे आश्चर्य है कि जो अधिक होने की संभावना है, @ डेविड, स्टैंडअलोन शब्द 'राउंड' कोड में दिखाई देता है जब * फ़ंक्शन का जिक्र नहीं होता है, या 'सिस्टम' राउंड' को कहीं भी कॉल किया जाता है। कार्यक्रम (न केवल प्रोजेक्ट का स्रोत कोड) और डिफ़ॉल्ट राउंडिंग व्यवहार का उपयोग करने की आवश्यकता है। और यह पता लगाने में मुश्किल है? मैं यह बताता हूं कि यह दोनों मामलों में उत्तरार्द्ध है। –

+0

मैं अधिक टिप्पणियों के बारे में सोच रहा था। सिस्टम का पता लगाने में बहुत आसान है। बेशक। गोल नाम के अन्य कार्यों होने की संभावना नहीं है। विधियों के लिए भी देखें। –

0

// बैंकरों दौर omzeilen

function RoundN(X: double): double; 
const 
    cFuncName = 'RoundN'; 
begin 
    Result := Trunc(X + Frac(X)); 
end; 
+1

हाय और एसओ में आपका स्वागत है। StackOverflow.com एक अंग्रेजी साइट है। कृपया अपने कोड, प्रश्न और उत्तर में हमेशा अंग्रेजी विवरण प्रदान करें। आपका उत्तर केवल अंग्रेजी पाठ रखने के लिए संपादित किया जाना चाहिए। – Hexfire

+0

यह ओपी से पूछा नहीं है। यह गोल विधि को ओवरराइड नहीं करता है – rvheddeg

संबंधित मुद्दे