如何格式化带有两位小数的数字?

问题描述

这就是我要显示两位小数的金额。它工作正常,但我想知道这是否正确,或者这样做是否有任何缺点,什么是更好的方法

我想格式化“您的价格”和“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

以下是模型类Product.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.access$400(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$1$1.onSuccess(FirestoreClass.kt:212)
        at com.Trad.firestore.FirestoreClass$uploadImagetoCloudStorage$1$1.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 是格式化数字的绝佳方式。

但是,您在这里有一个更深层次的问题,那就是您不应该使用浮点数或双精度数来存储需要精确的值,例如钱

那是因为浮点数和双精度数存储为二进制分数,而不是十进制分数。正如您不能将 1/3 准确地表示为任何有限长度的十进制小数 (0.33333333333...) 一样,您也不能准确地将 1/10 表示为二进制小数 (0.0001100110011...)。因此,您要存储的大多数十进制数将被近似并四舍五入为可以存储的最接近的二进制分数。这并不总是很明显——当打印出来时,它们会再次四舍五入到最接近的小数部分,并且在很多情况下会“恢复”你想要的数字——但在很多情况下它很明显,特别是作为计算结果.

您可以在 Kotlin REPL 中看到效果:

>>> 0.1 + 0.2
res0: kotlin.Double = 0.30000000000000004

在这种情况下,最接近 0.1 和 0.2 的二进制小数相加得到一个比 0.3 更接近 0.30000000000000004 的二进制小数。

(在 StackOverflow 上有很多讨论这个的现有问题,例如 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

(注意:不要不要从 Float 或 Double 创建 BigDecimal,因为损坏已经造成。 如果您有一个整数值,则从整数类型开始,例如 Int 或长;否则,您需要从字符串开始才能获得准确的值。)