java 如何在Kotlin中将double转换为没有零的字符串?

qpgpyjmq  于 2022-10-30  发布在  Java
关注(0)|答案(3)|浏览(354)

我有两个双精度值,但小数位数不同。

123.400 // three decimal places
567.80 // two decimal places

我尝试使用DecimalFormat将Double值转换为String

val stringFormat = DecimalFormat("#,###.000")
println(stringFormat.format(123.400))
println(stringFormat.format(567.80))

val stringFormatZeroAbsent = DecimalFormat("#,###.###")
println(stringFormatZeroAbsent.format(123.400))
println(stringFormatZeroAbsent.format(567.80))

由于“0”表示数字,“#”表示数字,零表示不存在,因此输出为:

123.400
567.800
123.4
567.8

我想要的实际结果是:

123.400
567.80

那个零号没有缺席。

nhn9ugyo

nhn9ugyo1#

您可以在这里简单地使用String.format(String, Double),并通过pattern固定三个小数位:

fun main() {
    val a: Double = 123.400
    val b: Double = 567.800

    val aStr: String = String.format("%.3f", a)
    val bStr: String = String.format("%.3f", b)

    println("${a.toString()} and ${b.toString()}")
    println("$aStr and $bStr")
}

这将输出

123.4 and 567.8
123.400 and 567.800
j2datikz

j2datikz2#

就像人们在评论中所说的那样,Double实际上并不 * 存储 * 特定数量的尾随零-- 1.2300000000001.23 是以同样的方式存储的。即使你指定了特定数量的零,这些信息也会丢失。
如果你需要存储那些带有任意数量的尾随零的数字,你可能应该把它们存储为String s --这样当你打印它们的时候,它们就会正确地显示出来。

val numberOne = "123.400"
val numberTwo = "567.80"

// just print them to get the formatting you want
println(numberOne)
// convert to Double to use it
numberOne.toDouble() * 2

另一个选择是创建一个类来处理这些额外的数据。

data class TrailingDouble(private val asString: String) {
    val value = asString.toDouble()
    override fun toString() = asString
}

在这里您公开了一个value属性,而不是让调用方一直调用toDouble
或者你可以做些更安全的事

class TrailingDouble private constructor(private val asString: String) {
    // validation done in the instance getters, which are the only thing
    // that can instantiate one of these objects
    val value = asString.toDouble()
    override fun toString() = asString

    // override equals etc

    companion object {
        fun newInstance(doubleString: String) =
            if (doubleString.toDoubleOrNull() != null) TrailingDouble(doubleString) else null
        // or if you like
        fun String.toTrailingDoubleOrNull() =
            toDoubleOrNull()?.let { TrailingDouble(this) }
    }
}

通过使用私有构造函数,你可以保证只创建TrailingDouble的有效示例。这意味着你不能真正使用一个 * 数据类 *(你可以用生成的copy函数绕过私有构造函数),所以你必须编写自己的equalshashCode函数。
如果愿意,您可以对实际的Double执行相同的方法,指定所需的尾随零的数量:

class TrailingDouble(val value: Double, trailingZeroes: Int) {
    private val asString: String

    init {
        val decimalFormat = List(trailingZeroes.coerceAtLeast(1)) { '0' }.joinToString("")
        asString = DecimalFormat("#,###.$decimalFormat").format(value)
    }

    override fun toString() = asString
    // equals etc
}

基本上,您可以传入double(不需要验证)和所需的尾随零的数目,它将生成适当的格式字符串以创建字符串表示。
这一个要简单得多,但它确实要求您知道特定Double需要多少个尾随零(同样,该信息不包含在Double本身中,它是单独的)。

v9tzhpje

v9tzhpje3#

我有两个双精度值,但小数位数不同。
双精度浮点数不能存储这些信息。事实上,它们甚至不能存储小数。毕竟计算机是二进制的。这里有一个秘密:

double d = 0.1;

那一行,完全是个谎言,它没有在d中存储0.1,那是因为不可能存储这个数
在那之前让你去:什么......那个?想想看:我把1除以3,然后让你们把它写在一张纸上,你从0. 33333333开始,从哪里结束?看到了吗?作为一个使用十进制的人,你不能把这个数字以十进制的格式存储在一张纸上。
计算机不做十进制,他们做二进制。他们可以完美地存储0.50.25也没有问题。但是0.1?不,不行。十分之一在十进制中很好,但是在二进制中就不行了(很明显,除了2的因子,没有什么是这样的)。所以,在十进制中,你可以在一张纸上完美地写上“1/10”,但是你不可能在一张纸上完美地写“1 3”,计算机只能完美地写X Y,其中Y是2的因子。1 65536 -没问题。1 10?不。
那么为什么上面的代码会编译呢?因为double/float数学运算会舍入到最接近的可表示数,而且每个数学运算都是类似的舍入。所有的舍入,无论何时何地,都是无声的,没有办法知道这种舍入会带来多少错误。
让我们来看看它的实际效果吧!

double d = 0;
for (int i = 0; i < 8; i++) d += 0.1;
double e = 0.8;
System.out.println(d == e); // prints... f... false? What?
System.out.println(e); // prints "0.8"
System.out.println(d); // prints "0.7999999999999999"

因此,当在“我想要x个数字”的意义上谈论“格式化”一个double时,这是完全误导的,因为该变量中的内容是以二进制而不是十进制写出来的数字,并且被舍入到甚至在十进制中也没有意义的东西。
简单的结果是所有的double都有轻微的错误,如果您编写的系统或应用程序中这种轻微的错误是不可接受的(例如:一切财务-你不想失去一个随机美分,由于不可能预测舍入误差!!),那么你不能使用double
此问题有两种广泛的解决方案:
1.使用原子,以intlong表示。
例如,不要将一个价值1.5美元的商品存储为double price = 1.50,而要将其存储为int price = 150;--换句话说,要找到原子(最小的难以/不可能分割的单位)对于任何它是你试图完美地存储和存储 * 那个 *.考虑到有这样一个原子,当要求你分割时,你无论如何都会被冲洗,你需要考虑一下,没有一个固定的方法来做。例如,如果我是一家银行,我需要收取100欧元的交易费用,-3个公司的合伙企业是平等的伙伴,那么我该怎么做?收取每个€ 33,34?收取每个€ 33,33和吃的美分?掷骰子,随机收取一个伙伴€ 33,34,和其他两个€33,33?要求当事人指定一个主账户,由该账户收取全部费用,如果没有,则收取33.34欧元?在每个账户中跟踪0.3333333333333333333333美分的剩余费用(把余数作为舍入误差扔掉,真的没有人会关心),即使一般在金融系统中,你不能传达分数分的概念吗
这个问题没有正确或错误的答案,这意味着a / b在这里不可能工作-它怎么知道你想要的是这5个不同的东西中的哪一个?
1.使用java.math.BigDecimal类,它以decimal的形式存储,并且可以完美地存储。但是请注意,如果您要求BigDecimal将1除以3,它会抛出异常,因为这是不可能的,所以要小心。

相关问题