如何将数字格式化为两位小数?

5

以下是我用来显示两位小数的方法,它可以正常工作,但是我想知道这种方法是否正确,或者是否存在任何缺点,以及更好的做法是什么?

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

1
为什么不使用Double或Float来表示货币? - Adam Millerchip
1个回答

5

使用DecimalFormat是格式化数字的完美方式。

然而,您在这里面临着更深层次的问题,即您绝不能使用Float或Double存储需要精确的值,例如货币

这是因为Float和Double以二进制小数形式存储,而不是十进制小数。正如您无法将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上有许多现有的问题讨论这个问题,例如这里。)

因此,如果您需要精确的货币价值(而您几乎总是需要的!),那么您应该以其他方式存储它们。例如,如果您只需要两位小数(即派),则将派数作为整数存储。或者,如果您不需要进行任何计算,则可以将数字存储为字符串(否则是一个坏主意…)。

然而,在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或Long)开始;否则,您需要从String开始获取精确值。)

我向你要一朵花,但你给了我一个满是花的花园。谢谢。 但是,作为一个初学者,我仍然有一些问题无法自己解决。我在上面的代码中将“Double”更改为“BigDecmal”,但当我在“model”类中进行相同的更改时,我会收到错误消息:“浮点文字与预期类型BigDecimal不符”。这是我的数据类中的两行代码。'val mrp: BigDecimal= 0.00, val price: BigDecimal = 0.00, - Codist
我现在解决的方法是,将我的(模型类)数据类变量类型保持为“Double”,并将问题中的代码更改如下。它可以工作且没有错误。但我不知道这是否是应该这样做的。val mrp: BigDecimal = model.mrp.toBigDecimal() val price: BigDecimal = model.price.toBigDecimal()。 修改后的代码已更新在问题中,请检查是否正确。 - Codist
1
根据我上一段所述,如果您在模型中将值存储为Double,则损失已经造成。您可以使用例如“0.00”。toBigDecimal()或BigDecimal(“0.00”)来初始化BigDecimal,或者(如果在那一点上不需要2DP精度)是0.toBigDecimal()或BigDecimal.ZERO。 - gidds
是的,你之前提到过,但我没能做到。在阅读了你最后一条评论后,我对我的模型类进行了更改。不幸的是,当我上传项目到Firestore时出现了错误。我已经在我的问题中上传了最新的“logcat”和代码。我一直在试图自己解决它,但我无法做到。 - Codist
我不熟悉Firestore,恐怕无法回答。也许这可以作为一个单独的问题提出来? - gidds
我已经修复了所有错误,现在它可以正常工作了。谢谢。 - Codist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接