Invalid double崩潰分析
第一次遇到
java.lang.NumberFormatException: Invalid double: "0,3"
這樣包含逗號的浮點數異常,第一感覺就是伺服器給的資料錯誤,但前段時間復現了這個異常,才發現是程式碼不規範導致了這樣的異常: 崩潰的詳細log如下:
E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.frank.lollipopdemo, PID:
10898java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.frank.lollipopdemo/com.frank.lollipopdemo.MainActivity}:
java.lang.NumberFormatException: Invalid double: "0,3" at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325) at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387) at
android.app.ActivityThread.access$800(ActivityThread.java:151) at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) at
android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at
android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke(Native
Method) at java.lang.reflect.Method.invoke(Method.java:372) at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) Caused by:
java.lang.NumberFormatException: Invalid double: "0,3" at
java.lang.StringToReal.invalidReal(StringToReal.java:63) at
java.lang.StringToReal.initialParse(StringToReal.java:164) at
java.lang.StringToReal.parseDouble(StringToReal.java:282) at
java.lang.Double.parseDouble(Double.java:301) at
com.frank.lollipopdemo.MainActivity.onCreate(MainActivity.java:43) at
android.app.Activity.performCreate(Activity.java:5990) at
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106) at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278) at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387) at
android.app.ActivityThread.access$800(ActivityThread.java:151) at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) at
android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at
android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke(Native
Method) at java.lang.reflect.Method.invoke(Method.java:372) at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
而出錯的程式碼是這樣的:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_title = (TextView) findViewById(R.id.tv_title);
tv_content = (TextView) findViewById(R.id.tv_content);
String jsonVal = "0.31415926";
String title = remain1Places(jsonVal);// -> 0.3
tv_title.setText(title);
tv_content.setText(getPercent(Double.parseDouble(title)));// -> ##.##%
}
public static String remain1Places(String string) {
if (TextUtils.isEmpty(string)) {
return "";
}
return String.format("%.1f", Double.parseDouble(string));
}
public static String getPercent(double value) {
NumberFormat numberFormat = NumberFormat.getPercentInstance();
numberFormat.setMinimumFractionDigits(1);
numberFormat.setMaximumFractionDigits(2);
return numberFormat.format(value);
}
想把浮點數”0.31415926”保留一位小數,就使用了String.format()
方法完成,而且完全不顧編譯器對我們的建議:
格式化成百分比。 看似沒有問題,但是當我們把系統語言換成法語(France)之後,程式直接Crash,而且當我們只顯示remain1Places()
後的title時,發現”0.31415926”變成了”0,3”,而不是”0.3”,這就直接導致了Double.parseDouble(“0,3”)丟擲一個資料格式異常。 那為什麼String.format()
會將小數點的”句點(.)“換成了”逗號(,)“呢?
摘自維基百科: A decimal mark is a symbol used to separate the integer part from the fractional part of a number written in decimal form. Different countries officially designate different symbols for the decimal mark. The choice of symbol for the decimal mark also affects the choice of symbol for the thousands separator used in digit grouping, so the latter is also treated in this article.
可以看到,有很多地區都是用逗號(,)作為小數點的,如19,90€表示19.9歐元;但計算機程式中的浮點數必須用句點(.)作為小數點,如double price = 19.90;
表示浮點數19.9。所以在使用double的包裝類Double
的parseDouble(String)
或valueOf(String)
方法將字串表示的double值轉成double時,字串所表示的double值必須是用句點(.)分隔的浮點數,也就是計算機的浮點數表示形式。
Double.valueOf(String)
方法僅僅呼叫了Double.parseDouble(String)
並返回Double
物件。 Double.parseDouble(String)
方法返回原語型別的double
變數。
因此,我們就可以斷定這是一個本地化(Locale)的問題了。現在再來看一下編譯器(lint)給我們String.format()
方法的建議:
Implicitly using the default locale is a common source of bugs: Use String.format(Locale, …) instead. 隱式地使用預設的區域設定是常見Bug源,請使用String.format(Locale, …)等方法替換它。
也就是說,String.format(String format, Object... args)
會呼叫format(Locale.getDefault(), format, args)
使用使用者預設的區域設定返回格式化好且本地化好的字串,因使用者設定的不同而返回不同的字串,進而出現Bug。如果你只是想格式化字串而不是人為干預,應該用
String.format(Locale locale, String format, Object... args)
方法,Locale引數用Locale.US
就可以了。
因此,我們應該重視本地化問題:
將字串所有字元轉為大/小寫的方法String.toLowerCase()
/String.toUpperCase()
並不一定能將字元真正的大/小寫(如區域設定為土耳其時,i大寫後還是i),因此應該指定要使用的區域設定String.toLowerCase(Locale locale)
/String.toUpperCase(Locale locale)
。
格式化字串的方法String.format(String format, Object... args)
應該指定區域設定,以避免區域設定變化導致的Bug。
千萬不要將數字format()
成字串後再將該字串轉回原語型別,因為format()
後的字串可能不是合法的原語型別的數字了。即永遠不要出現類似這樣
Double.parseDouble(new DecimalFormat("#.##").format(doubleValue))
的程式碼。
建議使用NumberFormat
,如:
public static String remain1Places(String str) {
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
numberFormat.setMinimumFractionDigits(1);
numberFormat.setMaximumFractionDigits(1);
return numberFormat.format(Double.parseDouble(str));
}
public static String getPercent(String str) {
NumberFormat numberFormat = NumberFormat.getPercentInstance(Locale.getDefault());
numberFormat.setMinimumFractionDigits(1);
numberFormat.setMaximumFractionDigits(2);
return numberFormat.format(Double.parseDouble(str));
}
相關文章
- IOS 崩潰日誌分析iOS
- WkWebView 令人崩潰的崩潰WebView
- MySQL 崩潰恢復過程分析MySql
- WKWebView崩潰WebView
- Redis崩潰Redis
- Crittercism:KitKat崩潰率0.7% iOS 7.1崩潰率1.6%iOS
- crash日誌符號化,以分析崩潰符號
- APP防崩潰APP
- WWDC 2018:理解崩潰以及崩潰日誌
- TestBird 崩潰分析(Artisan) Android SDK 使用指南Android
- iOS Crash不崩潰iOS
- linux mint 崩潰Linux
- ios 崩潰集錦iOS
- app 崩潰的原因APP
- 執行緒崩潰為什麼不會導致 JVM 崩潰執行緒JVM
- A站大流量導致服務崩潰異常分析
- 記一次 .NET 某工控MES程式 崩潰分析
- 記一次 騰訊會議 的意外崩潰分析
- iOS載入單張圖片導致崩潰的分析iOS
- [原創]360網盾 廣告攔截模組崩潰分析
- 使用 google_breakpad 分析 Electron 崩潰日誌檔案Go
- 手把手教你檢視和分析iOS的crash崩潰iOS
- 記一次VMware的崩潰除錯分析過程除錯
- 電腦崩潰和當機原因分析總結大全(轉)
- MySQL 8.0.11 無故崩潰MySql
- iOS 避免常見崩潰(一)iOS
- iOS 避免常見崩潰(二)iOS
- InnoDB 崩潰恢復機制
- zookeeper叢集崩潰處理
- 幽蘭核心崩潰自救記
- 關於Mozilla崩潰的研究
- 突發:當機崩潰OOMOOM
- 圖形化還原崩潰地址 iOS的crash檔案分析iOS
- 崩潰的一天,西安一碼通崩潰背後的技術問題。
- python: invalid value encountered in divide以及invalid value encountered in double_scalars報錯PythonIDE
- win10 pr崩潰怎麼解決_win10 pr崩潰解決辦法Win10
- 硬碟亮紅燈伺服器崩潰資料恢復案例分析硬碟伺服器資料恢復
- 記一次 .NET某新能源檢測系統 崩潰分析