如何格式化带两位小数的数字?
How to format numbers with two decimal places?
这就是我正在做的,以显示两位小数的金额。它工作正常,但我想知道这样做是否正确或有任何缺点,更好的方法是什么?
我想格式化 'Your Price' 和 'MRP' 的金额。我该怎么做?
holder.itemView.tv_dashboard_item_title.text = model.title
holder.itemView.tv_dashboard_item_price.text = "Your Price ₹${model.price}"
holder.itemView.tv_dashboard_item_mrp.text = "MRP ₹${model.mrp}"
val mrp:Double? = model.mrp
val price: Double? = model.price
val save=DecimalFormat("#,##0.00")
val save2: Double = mrp!! - price!!
val saves=save.format(save2)
holder.itemView.tv_dashboard_item_you_save.text = "You Save ₹ $saves"
谢谢。
编辑
修改后的代码。
val decFormat = DecimalFormat("#,##0.00")
holder.itemView.tv_dashboard_item_title.text = model.title
val mrp: BigDecimal = model.mrp.toBigDecimal()
val price: BigDecimal = model.price.toBigDecimal()
val save: BigDecimal = mrp - price
val saveAmount = decFormat.format(save)
holder.itemView.tv_dashboard_item_price.text = decFormat.format(price)
holder.itemView.tv_dashboard_item_mrp.text = decFormat.format(mrp)
holder.itemView.tv_dashboard_item_you_save.text = "You Save ₹ $saveAmount"
编辑 2
以下是型号classProduct.kt
@Parcelize
data class Product(
val user_id: String = "",
val user_name: String = "",
val title: String = "",
val mrp: BigDecimal= "0.00".toBigDecimal(),
val price: BigDecimal = "0.00".toBigDecimal(),
val description: String = "",
val stock_quantity: String = "",
val image: String = "",
var brand_name:String="",
var manufacturer:String="",
var main_category:String="",
var sub_category:String="",
var product_id: String = "",
) : Parcelable
AddProductActivity.kt
中的函数上传产品详细信息
private fun uploadProductDetails() {
val username =
this.getSharedPreferences(Constants.TRAD_PREFERENCES, Context.MODE_PRIVATE)
.getString(Constants.LOGGED_IN_USERNAME, "")!!
val product = Product(
FirestoreClass().getCurrentUserID(),
username,
et_product_title.text.toString().trim { it <= ' ' },
et_product_mrp.text.toString().toBigDecimal(),
et_product_price.text.toString().toBigDecimal(),
et_product_description.text.toString().trim { it <= ' ' },
et_product_quantity.text.toString().trim { it <= ' ' },
mProductImageURL,
et_product_brand_name.text.toString().trim { it <= ' ' },
et_product_manufacturer.text.toString().trim { it <= ' ' },
et_product_main_category.text.toString().trim { it <= ' ' },
et_product_sub_category.text.toString().trim { it <= ' ' },
)
FirestoreClass().uploadProductDetails(this@AddProductActivity, product)
}
Logcat
--------- beginning of crash
2021-06-19 18:36:11.605 6674-6674/com.trad E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.trad, PID: 6674
java.lang.IllegalArgumentException: Could not serialize object. Numbers of type BigDecimal are not supported, please use an int, long, float or double (found in field 'mrp')
at com.google.firebase.firestore.util.CustomClassMapper.serializeError(CustomClassMapper.java:555)
at com.google.firebase.firestore.util.CustomClassMapper.serialize(CustomClassMapper.java:122)
at com.google.firebase.firestore.util.CustomClassMapper.access0(CustomClassMapper.java:54)
at com.google.firebase.firestore.util.CustomClassMapper$BeanMapper.serialize(CustomClassMapper.java:902)
at com.google.firebase.firestore.util.CustomClassMapper.serialize(CustomClassMapper.java:178)
at com.google.firebase.firestore.util.CustomClassMapper.serialize(CustomClassMapper.java:104)
at com.google.firebase.firestore.util.CustomClassMapper.convertToPlainJavaTypes(CustomClassMapper.java:78)
at com.google.firebase.firestore.UserDataReader.convertAndParseDocumentData(UserDataReader.java:231)
at com.google.firebase.firestore.UserDataReader.parseMergeData(UserDataReader.java:87)
at com.google.firebase.firestore.DocumentReference.set(DocumentReference.java:166)
at com.trad.firestore.FirestoreClass.uploadProductDetails(FirestoreClass.kt:246)
at com.trad.ui.activities.AddProductActivity.uploadProductDetails(AddProductActivity.kt:280)
at com.trad.ui.activities.AddProductActivity.imageUploadSuccess(AddProductActivity.kt:254)
at com.trad.firestore.FirestoreClass$uploadImageToCloudStorage.onSuccess(FirestoreClass.kt:212)
at com.trad.firestore.FirestoreClass$uploadImageToCloudStorage.onSuccess(FirestoreClass.kt:27)
at com.google.android.gms.tasks.zzn.run(com.google.android.gms:play-services-tasks@@17.2.0:4)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8512)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
使用 DecimalFormat
是格式化数字的完美方式。
但是,您这里有一个更深层次的问题,那就是您不应该 永远不要使用 Float 或 Double 来存储需要精确的值,例如 money。
那是因为浮点数和双精度数存储为二进制小数,而不是小数。正如您不能将 1/3 精确表示为任何有限长度的小数 (0.33333333333...),您也不能将 1/10 精确表示为二进制分数 (0.0001100110011...)。因此,您要存储的大多数十进制数将被近似并四舍五入为最接近 可以 存储的二进制分数。这并不总是很明显——当打印出来时,它们会再次四舍五入到最接近的小数部分,并且在许多情况下会“恢复”你想要的数字——但在很多情况下它是显而易见的,尤其是作为计算结果.
在Kotlin REPL中可以看到效果:
>>> 0.1 + 0.2
res0: kotlin.Double = 0.30000000000000004
在这种情况下,最接近 0.1 和 0.2 的二进制分数相加得到的二进制分数更接近 0.30000000000000004 而不是 0.3。
(Whosebug 上已有很多问题在讨论这个,比如here。)
因此,如果您需要您的金钱价值是准确的(而且您几乎总是这样做!),那么您应该以其他方式存储它们。例如,如果您只需要小数点后两位(即派萨数),则只需将派萨数存储为整数即可。或者,如果您不需要进行任何计算,则可以将数字存储为字符串(否则这是个坏主意……)。
然而,在 Kotlin(和 Java)中最通用和灵活的方式是 使用 BigDecimal。它在内部使用小数来精确表示任何小数,达到您需要的任何精度,并且您可以轻松地进行计算和其他操作。
在Java中使用起来很笨拙和啰嗦,但是Kotlin的运算符重载让它变得很自然,例如:
>>> val p1 = "0.1".toBigDecimal()
>>> val p2 = "0.2".toBigDecimal()
>>> p1 + p2
res3: java.math.BigDecimal = 0.3
DecimalFormat
也支持:
>>> java.text.DecimalFormat("#,##0.00").format(p1 + p2)
res4: kotlin.String! = 0.30
(注意:不要从浮点数或双精度数创建一个 BigDecimal,因为损坏已经完成。如果你有一个整数值,那么从一个整数开始Int 或 Long 等类型;否则,您需要从字符串开始才能获得准确的值。)
这就是我正在做的,以显示两位小数的金额。它工作正常,但我想知道这样做是否正确或有任何缺点,更好的方法是什么?
我想格式化 'Your Price' 和 'MRP' 的金额。我该怎么做?
holder.itemView.tv_dashboard_item_title.text = model.title
holder.itemView.tv_dashboard_item_price.text = "Your Price ₹${model.price}"
holder.itemView.tv_dashboard_item_mrp.text = "MRP ₹${model.mrp}"
val mrp:Double? = model.mrp
val price: Double? = model.price
val save=DecimalFormat("#,##0.00")
val save2: Double = mrp!! - price!!
val saves=save.format(save2)
holder.itemView.tv_dashboard_item_you_save.text = "You Save ₹ $saves"
谢谢。
编辑
修改后的代码。
val decFormat = DecimalFormat("#,##0.00")
holder.itemView.tv_dashboard_item_title.text = model.title
val mrp: BigDecimal = model.mrp.toBigDecimal()
val price: BigDecimal = model.price.toBigDecimal()
val save: BigDecimal = mrp - price
val saveAmount = decFormat.format(save)
holder.itemView.tv_dashboard_item_price.text = decFormat.format(price)
holder.itemView.tv_dashboard_item_mrp.text = decFormat.format(mrp)
holder.itemView.tv_dashboard_item_you_save.text = "You Save ₹ $saveAmount"
编辑 2
以下是型号classProduct.kt
@Parcelize
data class Product(
val user_id: String = "",
val user_name: String = "",
val title: String = "",
val mrp: BigDecimal= "0.00".toBigDecimal(),
val price: BigDecimal = "0.00".toBigDecimal(),
val description: String = "",
val stock_quantity: String = "",
val image: String = "",
var brand_name:String="",
var manufacturer:String="",
var main_category:String="",
var sub_category:String="",
var product_id: String = "",
) : Parcelable
AddProductActivity.kt
中的函数上传产品详细信息
private fun uploadProductDetails() {
val username =
this.getSharedPreferences(Constants.TRAD_PREFERENCES, Context.MODE_PRIVATE)
.getString(Constants.LOGGED_IN_USERNAME, "")!!
val product = Product(
FirestoreClass().getCurrentUserID(),
username,
et_product_title.text.toString().trim { it <= ' ' },
et_product_mrp.text.toString().toBigDecimal(),
et_product_price.text.toString().toBigDecimal(),
et_product_description.text.toString().trim { it <= ' ' },
et_product_quantity.text.toString().trim { it <= ' ' },
mProductImageURL,
et_product_brand_name.text.toString().trim { it <= ' ' },
et_product_manufacturer.text.toString().trim { it <= ' ' },
et_product_main_category.text.toString().trim { it <= ' ' },
et_product_sub_category.text.toString().trim { it <= ' ' },
)
FirestoreClass().uploadProductDetails(this@AddProductActivity, product)
}
Logcat
--------- beginning of crash
2021-06-19 18:36:11.605 6674-6674/com.trad E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.trad, PID: 6674
java.lang.IllegalArgumentException: Could not serialize object. Numbers of type BigDecimal are not supported, please use an int, long, float or double (found in field 'mrp')
at com.google.firebase.firestore.util.CustomClassMapper.serializeError(CustomClassMapper.java:555)
at com.google.firebase.firestore.util.CustomClassMapper.serialize(CustomClassMapper.java:122)
at com.google.firebase.firestore.util.CustomClassMapper.access0(CustomClassMapper.java:54)
at com.google.firebase.firestore.util.CustomClassMapper$BeanMapper.serialize(CustomClassMapper.java:902)
at com.google.firebase.firestore.util.CustomClassMapper.serialize(CustomClassMapper.java:178)
at com.google.firebase.firestore.util.CustomClassMapper.serialize(CustomClassMapper.java:104)
at com.google.firebase.firestore.util.CustomClassMapper.convertToPlainJavaTypes(CustomClassMapper.java:78)
at com.google.firebase.firestore.UserDataReader.convertAndParseDocumentData(UserDataReader.java:231)
at com.google.firebase.firestore.UserDataReader.parseMergeData(UserDataReader.java:87)
at com.google.firebase.firestore.DocumentReference.set(DocumentReference.java:166)
at com.trad.firestore.FirestoreClass.uploadProductDetails(FirestoreClass.kt:246)
at com.trad.ui.activities.AddProductActivity.uploadProductDetails(AddProductActivity.kt:280)
at com.trad.ui.activities.AddProductActivity.imageUploadSuccess(AddProductActivity.kt:254)
at com.trad.firestore.FirestoreClass$uploadImageToCloudStorage.onSuccess(FirestoreClass.kt:212)
at com.trad.firestore.FirestoreClass$uploadImageToCloudStorage.onSuccess(FirestoreClass.kt:27)
at com.google.android.gms.tasks.zzn.run(com.google.android.gms:play-services-tasks@@17.2.0:4)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8512)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
使用 DecimalFormat
是格式化数字的完美方式。
但是,您这里有一个更深层次的问题,那就是您不应该 永远不要使用 Float 或 Double 来存储需要精确的值,例如 money。
那是因为浮点数和双精度数存储为二进制小数,而不是小数。正如您不能将 1/3 精确表示为任何有限长度的小数 (0.33333333333...),您也不能将 1/10 精确表示为二进制分数 (0.0001100110011...)。因此,您要存储的大多数十进制数将被近似并四舍五入为最接近 可以 存储的二进制分数。这并不总是很明显——当打印出来时,它们会再次四舍五入到最接近的小数部分,并且在许多情况下会“恢复”你想要的数字——但在很多情况下它是显而易见的,尤其是作为计算结果.
在Kotlin REPL中可以看到效果:
>>> 0.1 + 0.2
res0: kotlin.Double = 0.30000000000000004
在这种情况下,最接近 0.1 和 0.2 的二进制分数相加得到的二进制分数更接近 0.30000000000000004 而不是 0.3。
(Whosebug 上已有很多问题在讨论这个,比如here。)
因此,如果您需要您的金钱价值是准确的(而且您几乎总是这样做!),那么您应该以其他方式存储它们。例如,如果您只需要小数点后两位(即派萨数),则只需将派萨数存储为整数即可。或者,如果您不需要进行任何计算,则可以将数字存储为字符串(否则这是个坏主意……)。
然而,在 Kotlin(和 Java)中最通用和灵活的方式是 使用 BigDecimal。它在内部使用小数来精确表示任何小数,达到您需要的任何精度,并且您可以轻松地进行计算和其他操作。
在Java中使用起来很笨拙和啰嗦,但是Kotlin的运算符重载让它变得很自然,例如:
>>> val p1 = "0.1".toBigDecimal()
>>> val p2 = "0.2".toBigDecimal()
>>> p1 + p2
res3: java.math.BigDecimal = 0.3
DecimalFormat
也支持:
>>> java.text.DecimalFormat("#,##0.00").format(p1 + p2)
res4: kotlin.String! = 0.30
(注意:不要从浮点数或双精度数创建一个 BigDecimal,因为损坏已经完成。如果你有一个整数值,那么从一个整数开始Int 或 Long 等类型;否则,您需要从字符串开始才能获得准确的值。)