java浮點數(shù)運(yùn)算不精確的原因 float和double區(qū)別?
float和double區(qū)別?浮動的解釋:動詞 (verb的縮寫)飄,飄;漂泊,飄動;漂移;安排(貸款)被提出來考慮(一個想法或計劃);發(fā)行(股票)上市;自由浮動(貨幣匯率)零錢(用于酒吧等給顧客找錢
float和double區(qū)別?
浮動的解釋:
動詞 (verb的縮寫)飄,飄;漂泊,飄動;漂移;安排(貸款)被提出來考慮(一個想法或計劃);發(fā)行(股票)上市;自由浮動(貨幣匯率)
零錢(用于酒吧等給顧客找錢);飄,飄;浮板;漂浮物;魚漂;浮動;冰淇淋飲料;浮動期;坐在漂浮的盒子里(為了治療、康復(fù)或放松);救生圈
雙重解釋:
雙倍;配對;(指花)有重瓣的;對于兩個人來說;兩倍
非常相似的對應(yīng)物;身體替身;兩個人的東西;雙倍;(復(fù)數(shù))雙打,尤指網(wǎng)球;雙料冠軍(同一賽季或同一年度兩次奪冠者);(棒球)雙重打擊;雙精度浮點數(shù)(在C語言中)
動詞 (v
Java和C# 最大的不同是什么?
我覺得除了語法,最重要的是對底層的掌控能力不同。
雖然C#一開始借鑒了Java,但其目的根本不是為了構(gòu)建一個更好的Java,而是為了構(gòu)建一個更好的C,游戲引擎更喜歡C#就是這個原因。
例如,在C#中可以做什么:
上面的代碼會輸出10,為什么?因為數(shù)組的長度。NET存儲在數(shù)組第一個元素之前的8字節(jié)內(nèi)存中。如果您隨后輸出*(long *)p-2),您將直接獲得該對象的TypeHandle地址:
然后拿著這個指針,就可以訪問對象的MethodTable了。
此外,您可以在堆棧上手動分配空間:
然后,您希望繞過GC,直接手動分配堆內(nèi)存:
以上調(diào)用相當(dāng)于C語言中你調(diào)用的malloc,此外還有AllocAligned,Realloc,AllocZeroed等等,可以直接控制內(nèi)存對齊。
接下來,您希望創(chuàng)建一個具有顯式內(nèi)存布局的結(jié)構(gòu)Foo:
那么你就成功模擬了C的一個并集,之所以會有上面的輸出,是因為單精度浮點數(shù)1的二進(jìn)制表示是0x 01111111000000000000000000000000,在小終端模式存儲后占用了4個字節(jié),分別是0x000000000000、0x 00000000000、0x 000000000。
此外,您可以直接從內(nèi)存數(shù)據(jù)構(gòu)造對象,而無需任何復(fù)制開銷:
甚至像這樣:
從堆內(nèi)存中創(chuàng)建自然很好:
再比如,此時你有一個用C寫的庫,里面有這樣一段代碼:
然后我們編寫下面的C#代碼:
上面的代碼做了什么?我們把C#的函數(shù)指針傳入C代碼,然后在C端調(diào)用C#函數(shù)。數(shù)字生成一個字符串wwwww,然后把這個字符串返回給C#端。它不 使用委托而不是函數(shù)指針并不重要,因為函數(shù)指針在。網(wǎng)。
即使我們沒有。;我不想要。NET要導(dǎo)入foo.dll,而我們想自己決定動態(tài)庫的生命周期,我們也可以寫:
以上都不是特定于Windows和導(dǎo)入的。所以還有。Linux和macOS上的dylib是完全不可能的。
此外,我們有一些數(shù)據(jù),我們想計算,但我們想使用SIMD進(jìn)行處理,所以我們只需要寫:
您可以看到在X86平臺上生成了什么代碼:
平臺判斷的分支會被JIT自動淘汰。但實際上,除了手動編寫SIMD代碼,前兩個分支完全可以省略,只剩下:
因為在這個階段,當(dāng)循環(huán)邊界條件是向量長度時,。NET會自動為我們做定向量化,擴(kuò)展循環(huán)。
然后繼續(xù),我們還有ref,in和out來傳遞引用。
假設(shè)我們有一個大的struct,為了避免傳遞時的復(fù)制,我們可以直接使用in進(jìn)行只讀引用傳遞:
對于小型結(jié)構(gòu),為。NET有特殊的優(yōu)化來幫助我們完全消除內(nèi)存分配,并將結(jié)構(gòu)完全放在寄存器中,如下面的代碼:
上面的代碼GetDistance被認(rèn)為是一個熱路徑,所以我添加了它來指示JIT有保證地內(nèi)聯(lián)這個函數(shù),最后生成了下面的代碼進(jìn)行測試:
整個過程沒有訪問內(nèi)存的指令,效率非常高。
我們也可以借用ref的引用語義來做就地更新:
它甚至可以用于指針和手動分配內(nèi)存:
與Java不同,C#中的泛型真正專門化了所有的類型參數(shù)(雖然運(yùn)行時分布用于引用類型的共享實現(xiàn)),這意味著性能可以得到最大程度的保證,對應(yīng)的類型根據(jù)類型參數(shù)的大小有專門的內(nèi)存布局。還是上面的點例子,我們將下面的數(shù)據(jù)int替換為泛型參數(shù)t,并對值類型number進(jìn)行泛型約束:
無論是Test1還是Test2,生成的代碼都很優(yōu)秀,不僅沒有打包和解包,而且沒有訪問操作:
然后,我們有時為了高性能想暫時中止GC恢復(fù),就一句簡單的話:
如果你還能分配128mb的內(nèi)存,你可以告訴GC不要回收,然后一段時間后,即使我們在這個預(yù)算中分配內(nèi)存,也不會發(fā)生GC。它甚至可以防止在內(nèi)存分配不足時阻塞完全垃圾收集:
代碼執(zhí)行完畢,最后一次調(diào)用a:
可以恢復(fù)。GC行為。
此外,我們還可以指定GC在運(yùn)行時的模式,以最大限度地提高性能:
此外,我們甚至可以直接在堆內(nèi)存中執(zhí)行代碼,創(chuàng)建一個JIT。NET中,直接從內(nèi)存中創(chuàng)建一個可執(zhí)行區(qū),然后在其中插入一段代碼來添加兩個32位整數(shù):
除此之外,C#還有無數(shù)底層的編寫方法與操作系統(tǒng)交互,甚至使用C#的編譯器解除與自身標(biāo)準(zhǔn)庫的鏈接,直接從0開始構(gòu)建基本類型,然后通過NativeAOT編譯出完全無GC、可以在裸機(jī)硬件上執(zhí)行引導(dǎo)系統(tǒng)的EFI固件也是沒有問題的。
另外還有ILGPU可以讓你直接在GPU上運(yùn)行C#代碼,在嵌入式設(shè)備上運(yùn)行可以直接操作I2C、PWM、GPIO等。,所以不再舉例。
而C#已經(jīng)進(jìn)入了roadmap的后續(xù)更新:允許引用字段的聲明,增加類型表示定長內(nèi)存,允許傳遞數(shù)組時消除數(shù)組分配,允許棧上任何對象的分配等等。,所有這些都在改善這些基礎(chǔ)性能設(shè)施。
那個 這是我認(rèn)為C#和Java最大的區(qū)別。
在C#中,當(dāng)你不 t需要這些東西,它們好像從來不存在,允許動態(tài)類型,不斷吸收各種功能特性,各種語法糖加持。簡單性和靈活性。;甚至不會失去Python,所以你可以愉快而簡單地編寫各種代碼。一旦你需要,你就可以擁有從上到下幾乎完全的控制能力,而這些能力會讓你在必要的時候不用思考各種奇怪的變通方法,直接把機(jī)器榨干,達(dá)到C和C的性能,甚至因為運(yùn)行時PGO而超過C和C的性能。