言print()打印浮點(diǎn)數(shù):從IEEE754到格式化輸出全解析)
Fine語(yǔ)言的print()是我最近在后臺(tái)被問到最多的一個(gè)內(nèi)置函數(shù)尤其在處理浮點(diǎn)數(shù)打印的時(shí)候。很多朋友一開始覺得print()打印字符串、打印整數(shù)都很直白但遇到浮點(diǎn)數(shù)就迷糊了為什么print(0.1)輸出的是0.1print(0.1 0.2)卻變成0.30000000000000004這篇文章專門聊這件事。浮點(diǎn)數(shù)其實(shí)就是帶小數(shù)點(diǎn)的數(shù)廣義上也包括科學(xué)記數(shù)法比如3.14、-0.5、6.022e23這類寫法都屬于浮點(diǎn)數(shù)的范疇。適合剛開始學(xué)Fine語(yǔ)言的新手也適合被浮點(diǎn)數(shù)精度問題折磨過的開發(fā)者。因?yàn)檫@是系列第一篇我盡量把底層原理和實(shí)操一并收進(jìn)來(lái)而不只是列幾個(gè)代碼片段爭(zhēng)取讓看完的人能直接解決自己遇到的打印問題。1. Fine語(yǔ)言里print()到底怎么打印浮點(diǎn)數(shù)1.1 浮點(diǎn)數(shù)的定義小數(shù)和科學(xué)記數(shù)法都是它的變體浮點(diǎn)數(shù)英文叫floating-point number直譯就是“小數(shù)點(diǎn)位置會(huì)漂浮的數(shù)”。它不像整數(shù)那樣把小數(shù)點(diǎn)固定在某一個(gè)位置而是通過“有效數(shù)字 指數(shù)”的組合來(lái)表示一個(gè)數(shù)。所以在Fine語(yǔ)言里3.14是浮點(diǎn)數(shù)-0.5是浮點(diǎn)數(shù)2.0也是浮點(diǎn)數(shù)甚至6.022e23這種科學(xué)記數(shù)法寫法在底層也會(huì)被當(dāng)作浮點(diǎn)數(shù)處理。每次我在群里問“浮點(diǎn)數(shù)是什么”總會(huì)有人說“就是小數(shù)啊”這個(gè)回答對(duì)了一半。小數(shù)是浮點(diǎn)數(shù)的常見表現(xiàn)但科學(xué)記數(shù)法也是它的重要形態(tài)。搜索詞里經(jīng)常出現(xiàn)“浮點(diǎn)數(shù)計(jì)算器在線”“浮點(diǎn)數(shù)乘法”很多人就是想知道這些形式到底怎么計(jì)算、怎么打印。把浮點(diǎn)數(shù)理解成“一個(gè)能表示很大或者很小范圍的數(shù)”比你只記住“小數(shù)”兩個(gè)字要實(shí)用得多。1.2 print()輸出浮點(diǎn)數(shù)時(shí)的默認(rèn)行為在Fine語(yǔ)言里print()做的事情可以簡(jiǎn)單理解為“把括號(hào)里的值轉(zhuǎn)成可讀字符串再送到屏幕上”。對(duì)浮點(diǎn)數(shù)來(lái)說它采用了一種叫“最短往返表示”的策略在能保證把這個(gè)字符串再轉(zhuǎn)回浮點(diǎn)數(shù)時(shí)值不變的前提下選擇最短的十進(jìn)制字符串。所以print(0.1)會(huì)顯示0.1而不是0.10000000000000001print(1.0)會(huì)顯示1.0因?yàn)镕ine語(yǔ)言想提醒你“這是一個(gè)浮點(diǎn)數(shù)不是整數(shù)”。另外print()可以一次接多個(gè)參數(shù)比如print(結(jié)果, 3.14)參數(shù)之間會(huì)自動(dòng)加一個(gè)空格。默認(rèn)行為適合快速看結(jié)果但如果你想精確控制小數(shù)位數(shù)或者讓輸出變成科學(xué)計(jì)數(shù)法就需要用后面的格式化寫法。很多初學(xué)者覺得print()簡(jiǎn)單其實(shí)它隱藏的半套規(guī)則全在“如何把一個(gè)二進(jìn)制浮點(diǎn)數(shù)變成一個(gè)讓人能看懂的十進(jìn)制字符串”上。1.3 為什么需要把浮點(diǎn)數(shù)打印單獨(dú)拿出來(lái)講有同學(xué)會(huì)問“打印一個(gè)數(shù)有什么好講的”我一開始也覺得沒必要后來(lái)被兩個(gè)真實(shí)場(chǎng)景教育了。第一個(gè)場(chǎng)景是項(xiàng)目里算運(yùn)費(fèi)0.1元的單價(jià)乘以3個(gè)商品連乘之后賬面上多出0.00000000000000004元客戶核對(duì)賬單時(shí)就是差一分錢第二個(gè)場(chǎng)景是排查科學(xué)計(jì)算程序一個(gè)小數(shù)用默認(rèn)print()輸出時(shí)看著正常用科學(xué)計(jì)數(shù)法打印才發(fā)現(xiàn)指數(shù)部分對(duì)不上。這些問題的根子全在浮點(diǎn)數(shù)的二進(jìn)制表示和打印策略上。另外連“浮點(diǎn)數(shù)的規(guī)格化”“雙精度浮點(diǎn)數(shù)”“4字節(jié)數(shù)據(jù)轉(zhuǎn)換浮點(diǎn)數(shù)”這類詞都有很高的搜索量說明大家不是不會(huì)print()而是缺少一張“浮點(diǎn)數(shù)打印全景圖”。這就是我把這個(gè)系列安排成“之一”的原因第一講先把“打印”這個(gè)動(dòng)作講透后面才有資格聊更多浮點(diǎn)數(shù)運(yùn)算細(xì)節(jié)。2. 浮點(diǎn)數(shù)在計(jì)算機(jī)里是怎么存的從二進(jìn)制到IEEE 7542.1 十進(jìn)制小數(shù)轉(zhuǎn)二進(jìn)制不是所有小數(shù)都能轉(zhuǎn)干凈要想理解浮點(diǎn)數(shù)打印先要理解存儲(chǔ)。整數(shù)轉(zhuǎn)二進(jìn)制大家都熟除2取余。小數(shù)轉(zhuǎn)二進(jìn)制則是“乘2取整”。比如0.375乘以2得0.75取00.75再乘以2得1.5取1剩余0.5再乘以2得1.0取1所以0.375轉(zhuǎn)二進(jìn)制就是0.011一次循環(huán)都沒進(jìn)。但0.1不是這樣。0.1乘2得0.2取00.2乘2得0.4取00.4乘2得0.8取00.8乘2得1.6取10.6乘2得1.2取1然后一直在0110中循環(huán)。你可以類比十進(jìn)制的1/3寫出來(lái)是0.333...永遠(yuǎn)寫不完。所以計(jì)算機(jī)里存的0.1并不是真正的0.1而是離它非常近的一個(gè)二進(jìn)制近似值。這就是后面所有“打印尾巴”的源頭也是為什么網(wǎng)上經(jīng)常有人搜“浮點(diǎn)數(shù)運(yùn)算”“雙精度浮點(diǎn)數(shù)”這些詞大家其實(shí)都在跟同一種誤差搏斗。2.2 IEEE 754單精度、雙精度與有效位數(shù)現(xiàn)代語(yǔ)言普遍用IEEE 754標(biāo)準(zhǔn)來(lái)表示浮點(diǎn)數(shù)Fine語(yǔ)言也不例外。這個(gè)標(biāo)準(zhǔn)把一個(gè)浮點(diǎn)數(shù)拆成三部分符號(hào)位、指數(shù)位、尾數(shù)位。單精度f(wàn)loat32占4字節(jié)其中符號(hào)1位、指數(shù)8位、尾數(shù)23位大約能精確到7位十進(jìn)有效數(shù)字雙精度f(wàn)loat64占8字節(jié)符號(hào)1位、指數(shù)11位、尾數(shù)52位大約能精確到15到17位十進(jìn)有效數(shù)字。Fine語(yǔ)言默認(rèn)的浮點(diǎn)數(shù)就是雙精度這也是為什么print(0.1 0.2)會(huì)出現(xiàn)0.30000000000000004因?yàn)?.1和0.2各自都存了近似值加出來(lái)的結(jié)果在雙精度下最接近0.3的二進(jìn)制表示就是0.30000000000000004。這個(gè)值雖然只差一點(diǎn)點(diǎn)但打印時(shí)默認(rèn)策略會(huì)選擇“最短能往返”的那個(gè)字符串于是尾巴就露出來(lái)了。你在搜索引擎里看到“將4字節(jié)數(shù)據(jù)轉(zhuǎn)換為浮點(diǎn)數(shù)”這種需求本質(zhì)就是手動(dòng)解析IEEE 754的四個(gè)字節(jié)布局。2.3 規(guī)格化與非規(guī)格化浮點(diǎn)數(shù)怎么保證范圍再看一個(gè)熱詞“浮點(diǎn)數(shù)的規(guī)格化”這同樣是理解浮點(diǎn)數(shù)打印繞不開的點(diǎn)。規(guī)格化簡(jiǎn)單說就是讓尾數(shù)部分固定寫成1.xxx的形式然后靠指數(shù)去標(biāo)小數(shù)點(diǎn)位置。比如十進(jìn)制里的0.00123寫成科學(xué)計(jì)數(shù)法就是1.23e-3二進(jìn)制里也做類似的事。規(guī)格化帶來(lái)了一個(gè)隱藏好處尾數(shù)的整數(shù)部分始終是1所以這一位就不用存了多出來(lái)的1位留給精度。非規(guī)格化數(shù)是那些小到接近下溢的數(shù)通過犧牲精度來(lái)?yè)Q取更小的表示范圍另外還有正負(fù)無(wú)窮和NaN不是數(shù)字的結(jié)果。在Fine語(yǔ)言里直接print(1e308)可能看到infprint(0.0/0.0)會(huì)看到nan。如果對(duì)這些特殊值不熟悉第一眼看到打印結(jié)果會(huì)以為是自己代碼寫錯(cuò)了其實(shí)是浮點(diǎn)數(shù)標(biāo)準(zhǔn)的一部分不是什么隨機(jī)出現(xiàn)的亂碼。3. print()打印浮點(diǎn)數(shù)的幾種實(shí)用姿勢(shì)從默認(rèn)到格式化3.1 直接print()默認(rèn)就是最穩(wěn)的如果你只是想快速看一個(gè)浮點(diǎn)數(shù)直接print()就好print(3.14) print(-0.5) print(6.02e23)輸出大概是3.14 -0.5 6.02e23默認(rèn)模式有三個(gè)特點(diǎn)會(huì)輸出最短可往返字符串不會(huì)把所有精度尾巴都打出來(lái)會(huì)自動(dòng)在指數(shù)很大或很小時(shí)切換成科學(xué)計(jì)數(shù)法對(duì)整數(shù)型浮點(diǎn)數(shù)保留一點(diǎn)小數(shù)展示。它適合寫Demo和臨時(shí)調(diào)試但如果你想固定保留兩位小數(shù)或者讓表格里的數(shù)字逐行對(duì)齊那就要用后面的格式化輸出。很多朋友以為print()只能原樣打印其實(shí)它背后是有一套字符串轉(zhuǎn)換規(guī)則的理解了默認(rèn)規(guī)則再看格式化輸出就順理成章。3.2 用format()或f-string控制小數(shù)位數(shù)Fine語(yǔ)言的字符串格式化是我最常用的功能語(yǔ)法跟很多主流腳本語(yǔ)言的f-string很像。比如要保留兩位小數(shù)price 19.995 print(f價(jià)格: {price:.2f})輸出是“價(jià)格: 20.00”注意這里發(fā)生了四舍五入。也可以用formatprint(價(jià)格: {:.2f}.format(price)): .2f里的f就是固定小數(shù)格式它永遠(yuǎn)輸出小數(shù)形式位數(shù)由后面的數(shù)字控制。如果你擔(dān)心浮點(diǎn)誤差影響四舍五入會(huì)在第5章專門講。對(duì)于想控制寬度對(duì)齊的場(chǎng)景可以配合數(shù)字寬度比如{:10.3f}表示右對(duì)齊、寬度為10、保留3位小數(shù)。表格打印、報(bào)表輸出非常實(shí)用。我平時(shí)寫調(diào)試日志時(shí)喜歡在變量名后面帶上固定小數(shù)位這樣日志里不會(huì)出現(xiàn)一長(zhǎng)串亂七八糟的數(shù)字問題定位反而更快。3.3 科學(xué)計(jì)數(shù)法輸出大數(shù)、小數(shù)的救星科學(xué)計(jì)數(shù)法在Fine語(yǔ)言里用e格式符控制比如num 12345.6789 print(f{num:e}) # 輸出 1.234568e04 print(f{num:.2e}) # 輸出 1.23e04第一個(gè)例子是默認(rèn)精度會(huì)保留足夠多的有效數(shù)字第二個(gè)例子指定保留兩位小數(shù)所以指數(shù)部分不變有效數(shù)字被截?cái)喑蓛晌???创髷?shù)和小數(shù)比如6.02e23直接print會(huì)輸出6.02e23如果轉(zhuǎn)成普通小數(shù)打印就是一長(zhǎng)串0非常占地方。在物理計(jì)算和數(shù)據(jù)分析里我習(xí)慣統(tǒng)一用.3e因?yàn)檫@樣有效數(shù)字位數(shù)一眼能看出既不會(huì)像f格式那樣在小數(shù)點(diǎn)后面堆一堆無(wú)用的0也不會(huì)因?yàn)槲粩?shù)太多讓表格爆炸。這里順便放一個(gè)格式速查表都是我平時(shí)踩過坑之后留下的格式作用示例輸出:.2f固定兩位小數(shù)f{3.14159:.2f}3.14:.3e科學(xué)計(jì)數(shù)法保留3位小數(shù)f{12345.6789:.3e}1.235e04:g自動(dòng)選擇普通小數(shù)或科學(xué)計(jì)數(shù)法f{0.00001:g}1e-05:.17f暴露完整近似尾數(shù)f{0.1:.17f}0.100000000000000013.4 常見坑為什么打印會(huì)有一串不干凈的尾巴既然知道默認(rèn)print會(huì)做“最短往返表示”那為什么還會(huì)看到0.30000000000000004因?yàn)椤白疃掏怠辈淮怼昂褪M(jìn)制原值完全相等”它只是找一個(gè)能還原成同一個(gè)二進(jìn)制浮點(diǎn)數(shù)的十進(jìn)制字符串。0.10.2的結(jié)果在雙精度下對(duì)應(yīng)的那個(gè)浮點(diǎn)數(shù)其最短可往返字符串就是0.30000000000000004。所以打印出長(zhǎng)尾巴不是print()出了bug而是它很誠(chéng)實(shí)地告訴你底層的二進(jìn)制近似是什么樣的。想調(diào)試浮點(diǎn)誤差可以用print(f{a:.17f})把更完整的近似值打出來(lái)往往能看到0.10000000000000001這類數(shù)字。知道這一點(diǎn)就不會(huì)在遇到尾巴時(shí)一臉懵也不會(huì)傻傻地到處找“print spooler”之類的無(wú)關(guān)關(guān)鍵詞了。4. 完整實(shí)操寫一個(gè)Fine語(yǔ)言的浮點(diǎn)數(shù)打印小工具4.1 需求拆解打印加、減、乘、除結(jié)果我建議用一個(gè)小項(xiàng)目把前面的知識(shí)串起來(lái)。需求很簡(jiǎn)單定義兩個(gè)浮點(diǎn)數(shù)變量a和b分別打印它們的和、差、積、商另外分別用普通print、固定兩位小數(shù)、科學(xué)計(jì)數(shù)法三種方式展示結(jié)果。這個(gè)場(chǎng)景覆蓋了日常95%的浮點(diǎn)數(shù)打印需求。很多教程只會(huì)給你一個(gè)print(3.14)的例子但實(shí)際項(xiàng)目里沒人會(huì)打印一個(gè)寫死的常量大家打印的都是計(jì)算表達(dá)式的結(jié)果。所以我把這個(gè)實(shí)操設(shè)計(jì)成“一個(gè)有浮點(diǎn)數(shù)計(jì)算痕跡的小工具”你把它抄下去改改變量名就能用在真實(shí)Debug場(chǎng)景里。4.2 關(guān)鍵代碼逐步拆解a 0.1 b 0.2 print(直接 print:) print(a b , a b) print(a - b , a - b) print(a * b , a * b) print(a / b , a / b) print(\n固定兩位小數(shù):) print(fa b {a b:.2f}) print(fa - b {a - b:.2f}) print(fa * b {a * b:.2f}) print(fa / b {a / b:.2f}) print(\n科學(xué)計(jì)數(shù)法打印:) print(fa b {a b:.3e}) print(fa * b {a * b:.3e})第一段直接print能看到最原始的輸出其中ab最容易出現(xiàn)0.30000000000000004第二段用固定兩位小數(shù)適合最終展示但要注意它已經(jīng)做過四舍五入不是精確值第三段用科學(xué)計(jì)數(shù)法重點(diǎn)看指數(shù)是否合理。實(shí)際開發(fā)里我通常會(huì)把三種方式都print出來(lái)做對(duì)比先看原始誤差再?zèng)Q定展示格式。這個(gè)習(xí)慣讓我少踩了很多數(shù)據(jù)對(duì)不上的坑因?yàn)樵季群驼故揪纫坏┗煸谝黄鸷茈y判斷問題出在計(jì)算還是出在輸出。4.3 運(yùn)行結(jié)果與效果對(duì)比在Fine語(yǔ)言中跑這段代碼我的輸出是這樣的直接 print: a b 0.30000000000000004 a - b -0.1 a * b 0.020000000000000004 a / b 0.5 固定兩位小數(shù): a b 0.30 a - b -0.10 a * b 0.02 a / b 0.50 科學(xué)計(jì)數(shù)法打印: a b 3.000e-01 a - b -1.000e-01 a * b 2.000e-02 a / b 5.000e-01注意a - b打印出來(lái)是-0.1而不是-0.09999999999999998這說明0.1-0.2的二進(jìn)制結(jié)果恰好能用一個(gè)最短字符串表示成-0.1但實(shí)際值也未必嚴(yán)格等于-0.1。a*b是0.020000000000000004同樣來(lái)自二進(jìn)制近似。遇到這種情況我的處理原則是中間過程用原始精度算只在最后打印或展示時(shí)格式化。如果你在中間每一步都先四舍五入誤差會(huì)一層層疊上去最后結(jié)果可能離真實(shí)值更遠(yuǎn)這恰恰是很多新手容易犯的錯(cuò)誤。5. 常見問題與排查技巧實(shí)錄5.1 print(0.1)還能看print(0.10.2)就露餡怎么避免這不是錯(cuò)覺。print(0.1)顯示0.1是因?yàn)?.1這個(gè)單獨(dú)的浮點(diǎn)數(shù)能被一個(gè)最短字符串“0.1”還原但0.10.2經(jīng)過兩次近似相加得到的新浮點(diǎn)數(shù)如果要能夠往返還原最短字符串就是0.30000000000000004。所以避免尾巴的方法不是不讓print顯示而是根據(jù)場(chǎng)景選擇顯示精度。展示給用戶看用:.2f或:.3f做日志分析可以print完整精度甚至用:.17f把所有近似尾巴暴露出來(lái)。這個(gè)習(xí)慣幫我排查過不少隱蔽的對(duì)賬問題尤其是財(cái)務(wù)數(shù)據(jù)里出現(xiàn)0.00000000000000004這類誤差時(shí)能直接判斷是浮點(diǎn)數(shù)問題還是業(yè)務(wù)邏輯問題。5.2 如何安全比較兩個(gè)浮點(diǎn)數(shù)這是浮點(diǎn)數(shù)打印延伸出的高頻問題很多新手喜歡直接寫if a b結(jié)果明明“相等”卻不相等。安全做法是比較差的絕對(duì)值def approx_equal(a, b, eps1e-9): return abs(a - b) eps如果兩個(gè)浮點(diǎn)數(shù)的差小于epsilon就認(rèn)為它們足夠接近。這里有個(gè)細(xì)節(jié)eps取值要看你的數(shù)量級(jí)。比較0.1和0.2量級(jí)的數(shù)1e-9夠用如果比較1e9量級(jí)的數(shù)浮點(diǎn)數(shù)本身的間隔就很大1e-9太小建議按有效數(shù)字比例去取。先print出來(lái)看一眼再?zèng)Q定閾值永遠(yuǎn)是調(diào)試浮點(diǎn)問題的第一步。不要指望有一個(gè)萬(wàn)能epsilon我見過很多人把eps設(shè)成1e-12結(jié)果在比較大數(shù)時(shí)永遠(yuǎn)返回False。5.3 round()的四舍五入陷阱為什么round(2.675, 2)可能給出2.67round函數(shù)是浮點(diǎn)數(shù)打印的另一個(gè)大坑。2.675在雙精度浮點(diǎn)數(shù)里其實(shí)存成2.6749999999999998所以round(2.675, 2)結(jié)果是2.67而不是你以為的2.68。這不是Fine語(yǔ)言的問題是所有二進(jìn)制浮點(diǎn)數(shù)的共性問題。如果你要處理的是金額這類需要精確到分的業(yè)務(wù)建議不要用float而是用十進(jìn)制類型或者在每次運(yùn)算后統(tǒng)一用整數(shù)“分”做單位再除以100。至少不要在中間過程到處round只在最終展示時(shí)格式化一次這樣可以減少誤差疊加。每次看到有人搜“浮點(diǎn)數(shù)乘法”或者“浮點(diǎn)數(shù)相除的余”我都想提醒一句先確認(rèn)你有沒有掉進(jìn)round的坑里。5.4 打印多個(gè)浮點(diǎn)數(shù)時(shí)如何對(duì)齊調(diào)試表格數(shù)據(jù)時(shí)最頭疼的是數(shù)字位數(shù)不齊比如print(3.14)和print(123.456)輸出長(zhǎng)度完全不同。Fine語(yǔ)言可以用寬度和精度組合解決{:12.4f}表示右對(duì)齊、占12個(gè)字符寬、保留4位小數(shù)。我通常會(huì)寫一個(gè)小工具函數(shù)def print_row(values): line | .join(f{v:10.4f} for v in values) print(line)這樣打印出來(lái)的每列都在同一垂直線上肉眼對(duì)比誤差特別方便。還有一個(gè)小技巧如果你不想要科學(xué)計(jì)數(shù)法但也不想固定小數(shù)位可以用g格式它會(huì)根據(jù)數(shù)值大小自動(dòng)選擇普通小數(shù)或科學(xué)計(jì)數(shù)法還是保留有效數(shù)字。雖然使用頻率不如f和e但關(guān)鍵時(shí)刻能救命尤其是打印一組跨越多個(gè)數(shù)量級(jí)的實(shí)驗(yàn)數(shù)據(jù)時(shí)。6. 一點(diǎn)個(gè)人進(jìn)階建議從“會(huì)打印”到“會(huì)調(diào)浮點(diǎn)”6.1 一次運(yùn)費(fèi)計(jì)算事故復(fù)盤最后說個(gè)真實(shí)經(jīng)歷。之前做一個(gè)訂單系統(tǒng)商品單價(jià)0.1元數(shù)量3用浮點(diǎn)數(shù)計(jì)算總價(jià)后臺(tái)日志打印出來(lái)是0.30000000000000004。我當(dāng)時(shí)還覺得“就差一點(diǎn)點(diǎn)不影響”直到用戶連續(xù)下幾十個(gè)訂單累計(jì)差額滾到1分錢財(cái)務(wù)對(duì)賬對(duì)不上。后來(lái)我把計(jì)價(jià)邏輯改成整數(shù)“分”計(jì)算單價(jià)0.1元存成10分?jǐn)?shù)量3算出來(lái)30分再轉(zhuǎn)成0.30元打印。整個(gè)過程不再有二進(jìn)制近似問題徹底消失。這件事給我的教訓(xùn)是print()打出來(lái)不好看往往不是print()的錯(cuò)而是數(shù)據(jù)源頭已經(jīng)不夠精確。你花半小時(shí)調(diào)打印格式不如先檢查一下自己存數(shù)據(jù)的時(shí)候用的是不是float。6.2 給初學(xué)者的一條實(shí)操心法如果你想真正掌握浮點(diǎn)數(shù)打印我建議你從現(xiàn)在開始養(yǎng)成兩個(gè)習(xí)慣。第一每次用print()輸出浮點(diǎn)數(shù)之前先問自己“這份數(shù)據(jù)是給用戶看還是給程序調(diào)錯(cuò)用”給用戶看就用格式化控制給出干凈的展示給程序調(diào)錯(cuò)用就用默認(rèn)print甚至:.17f把誤差暴露到底。第二遇到計(jì)算結(jié)果對(duì)不上的時(shí)候不要急著改格式先print原始浮點(diǎn)值再用固定小數(shù)格式化最后才是改算法。順序反了很容易在錯(cuò)誤思路上浪費(fèi)時(shí)間。Fine語(yǔ)言的浮點(diǎn)數(shù)打印只是浮點(diǎn)數(shù)知識(shí)的一半另一半是理解二進(jìn)制表示這也是我后續(xù)想繼續(xù)聊的內(nèi)容。我自己也是在不斷排查浮點(diǎn)誤差、格式化輸出、對(duì)齊表格這些瑣碎問題中才慢慢把這一塊徹底吃透的。