Dart 中的數字
Dart 應用通常面向多個平臺。例如,Flutter 應用可能面向 iOS、Android 和 Web。只要應用不依賴於特定於平臺的庫或以依賴於平臺的方式使用數字,程式碼就可以相同。
此頁面詳細介紹了本機和 Web 數字實現之間的差異,以及如何編寫程式碼以使這些差異無關緊要。
Dart 數字表示
#在 Dart 中,所有數字都是通用 Object 型別層次結構的一部分,並且有兩個具體的、使用者可見的數值型別:int,表示整數值,double,表示分數值。
根據平臺的不同,這些數值型別具有不同的隱藏實現。特別是,Dart 有兩種截然不同的目標編譯型別
- **原生:**最常見的是 64 位移動或桌面處理器。
- **Web:**JavaScript 作為主要執行引擎。
下表顯示了 Dart 數字通常如何實現
| 表示 | 原生 int | 原生 double | Web int | Web double |
|---|---|---|---|---|
| 64 位有符號二進位制補碼 | ✅ | |||
| 64 位浮點數 | ✅ | ✅ | ✅ |
對於原生目標,您可以假設 int 對映到有符號 64 位整數表示,double 對映到與底層處理器匹配的 64 位 IEEE 浮點表示。
但在 Web 上,Dart 編譯到並與 JavaScript 互操作的地方,只有一個數值表示:64 位雙精度浮點值。為了提高效率,Dart 將 int 和 double 都對映到此單個表示。可見的型別層次結構保持不變,但底層的隱藏實現型別不同且相互交織。
下圖說明了原生和 Web 目標的特定於平臺的型別(藍色)。如圖所示,原生上 int 的具體型別僅實現 int 介面。但是,Web 上 int 的具體型別同時實現了 int 和 double。
Web 上的 int 表示為沒有小數部分的雙精度浮點值。在實踐中,這執行良好:雙精度浮點提供了 53 位的整數精度。但是,int 值始終也是 double 值,這可能會導致一些意外情況。
行為差異
#大多數整數和雙精度算術運算基本上具有相同的行為。但是,也存在重要的差異——尤其是在您的程式碼對精度、字串格式或底層執行時型別有嚴格期望時。
當算術結果不同時,如本節所述,行為是**特定於平臺的**且**可能發生變化**。
精度
#下表演示了由於精度而導致的一些數值表示式如何不同。這裡,math 表示 dart:math 庫,math.pow(2, 53) 是 253。
在 Web 上,整數在超過 53 位後會丟失精度。特別是,由於截斷,253 和 253+1 對映到相同的值。在原生上,這些值仍然可以區分,因為原生數字有 64 位——63 位用於值,1 位用於符號。
溢位的影響在比較 263-1 與 263 時可見。在原生上,後者溢位到 -263,這對於二進位制補碼算術來說是預期的。在 Web 上,這些值不會溢位,因為它們的表示方式不同;由於精度損失,它們是近似值。
| 表示式 | 原生 | Web |
|---|---|---|
math.pow(2, 53) - 1 | 9007199254740991 | 9007199254740991 |
math.pow(2, 53) | 9007199254740992 | 9007199254740992 |
math.pow(2, 53) + 1 | 9007199254740993 | 9007199254740992 |
math.pow(2, 62) | 4611686018427387904 | 4611686018427388000 |
math.pow(2, 63) - 1 | 9223372036854775807 | 9223372036854776000 |
math.pow(2, 63) | -9223372036854775808 | 9223372036854776000 |
math.pow(2, 64) | 0 | 18446744073709552000 |
標識
#在原生平臺上,double 和 int 是不同的型別:沒有值可以同時是 double 和 int。在 Web 上,情況並非如此。由於這種差異,標識可能因平臺而異,儘管相等性 (==) 不會。
下表顯示了一些使用相等性和標識性的表示式。相等性表示式在原生和 Web 上相同;標識性表示式通常不同。
| 表示式 | 原生 | Web |
|---|---|---|
1.0 == 1 | true | true |
identical(1.0, 1) | false | true |
0.0 == -0.0 | true | true |
identical(0.0, -0.0) | false | true |
double.nan == double.nan | false | false |
identical(double.nan, double.nan) | true | false |
double.infinity == double.infinity | true | true |
identical(double.infinity, double.infinity) | true | true |
型別和型別檢查
#在 Web 上,底層的 int 型別類似於 double 的子型別:它是沒有小數部分的雙精度值。實際上,Web 上表單 x is int 的型別檢查會在 x 是一個具有零值小數部分的數字 (double) 時返回 true。
因此,以下在 Web 上為真
- 所有 Dart 數字(型別為
num的值)都是double。 - Dart 數字可以同時是
double和int。
這些事實會影響 is 檢查和 runtimeType 屬性。一個副作用是 double.infinity 被解釋為 int。由於這是特定於平臺的行為,因此將來可能會更改。
| 表示式 | 原生 | Web |
|---|---|---|
1 is int | true | true |
1 is double | false | true |
1.0 is int | false | true |
1.0 is double | true | true |
(0.5 + 0.5) is int | false | true |
(0.5 + 0.5) is double | true | true |
3.14 is int | false | false |
3.14 is double | true | true |
double.infinity is int | false | true |
double.nan is int | false | false |
1.0.runtimeType | double | int |
1.runtimeType | int | int |
1.5.runtimeType
| double | double |
按位運算
#出於 Web 效能方面的考慮,int 上的按位運算子(&、|、^、~)和移位運算子(<<、>>、>>>)使用原生 JavaScript 等價物。在 JavaScript 中,運算元會被截斷為 32 位整數,並被視為無符號數。這種處理方式可能會導致對較大數字產生意外的結果。特別是,如果運算元為負數或不適合 32 位,則它們很可能在原生和 Web 之間產生不同的結果。
下表顯示了原生平臺和 Web 平臺在運算元為負數或接近 32 位時如何處理按位運算子和移位運算子。
| 表示式 | 原生 | Web |
|---|---|---|
-1 >> 0 | -1 | 4294967295 |
-1 ^ 2 | -3 | 4294967293 |
math.pow(2, 32).toInt() | 4294967296 | 4294967296 |
math.pow(2, 32).toInt() >> 1 | 2147483648 | 0 |
(math.pow(2, 32).toInt()-1) >> 1 | 2147483647 | 2147483647 |
字串表示
#在 Web 上,Dart 通常依賴 JavaScript 將數字轉換為字串(例如,用於 print)。下表演示瞭如何轉換第一列中的表示式會導致不同的結果。
| 表示式 | 原生 toString() | Web toString() |
|---|---|---|
1 | "1" | "1" |
1.0 | "1.0" | "1" |
(0.5 + 0.5) | "1.0" | "1" |
1.5 | "1.5" | "1.5" |
-0 | "0" | "-0.0" |
math.pow(2, 0) | "1" | "1" |
math.pow(2, 80) | "0" | "1.2089258196146292e+24" |
您應該怎麼做?
#通常,您不需要更改數字程式碼。Dart 程式碼多年來一直在原生和 Web 平臺上執行,數字實現差異很少成為問題。常見的典型程式碼(例如遍歷一系列小整數並索引列表)的行為相同。
如果您有比較字串結果的測試或斷言,請以平臺彈性的方式編寫它們。例如,假設您正在測試包含嵌入數字的字串表示式的值。
void main() {
var count = 10.0 * 2;
var message = "$count cows";
if (message != "20.0 cows") throw Exception("Unexpected: $message");
}上述程式碼在原生平臺上成功,但在 Web 上丟擲異常,因為 message 在 Web 上為 "20 cows"(沒有小數)。作為替代方案,您可以按如下方式編寫條件,以便它在原生和 Web 平臺上都透過。
if (message != "${20.0} cows") throw ...對於位操作,請考慮顯式地操作 32 位塊,這些塊在所有平臺上都一致。要強制對 32 位塊進行有符號解釋,請使用 int.toSigned(32)。
對於其他需要精度的用例,請考慮其他數值型別。BigInt 型別在原生和 Web 上都提供了任意精度的整數。fixnum 包提供了嚴格的 64 位有符號數,即使在 Web 上也是如此。但是,請謹慎使用這些型別:它們通常會導致程式碼變得更大、更慢。
除非另有說明,否則本網站上的文件反映了 Dart 3.5.3。頁面最後更新於 2024-02-07。 檢視原始碼 或 報告問題.