I have a simple problem for which I didn`t find a solution. I am having large negative number ex(-6763.98) what I want is something like this ex($-6.78K). A found a lot of solutions that work for positive numbers but none that work for negative. This is the code that I am having right now.
const val COUNT_DIVISOR = 1000
const val COUNT_DIVISOR_FLOAT = 1000.0
fun getFormattedNumber(count: Long): String {
if (count < COUNT_DIVISOR) return "" + count
val exp = (ln(count.toDouble()) / ln(COUNT_DIVISOR_FLOAT)).toInt()
return resources.getString(
R.string.decimal_format_long_number_price,
count / COUNT_DIVISOR_FLOAT.pow(exp.toDouble()), EXTENSION[exp - 1]
)
}
The natural logarithm is not defined for negative values so the function ln will return NaN (not a number) for negative inputs.
From ln Kotlin documentation.
Special cases:
ln(NaN) is NaN
ln(x) is NaN when x < 0.0
ln(+Inf) is +Inf
ln(0.0) is -Inf
You have to make sure that the input is always a positive value in order to calculate the exponent correctly.
val exp = (ln(abs(count.toDouble())) / ln(COUNT_DIVISOR_FLOAT)).toInt()
Another problem is the first if check, which returns the input value itself for all inputs smaller than COUNT_DIVISOR. You have to allow large negative inputs through there as well.
if (count > -COUNT_DIVISOR && count < COUNT_DIVISOR) return "" + count
All together
const val COUNT_DIVISOR = 1000
const val COUNT_DIVISOR_FLOAT = 1000.0
fun getFormattedNumber(count: Long): String {
if (count > -COUNT_DIVISOR && count < COUNT_DIVISOR) return "" + count
val exp = (ln(abs(count.toDouble())) / ln(COUNT_DIVISOR_FLOAT)).toInt()
return resources.getString(
R.string.decimal_format_long_number_price,
count / COUNT_DIVISOR_FLOAT.pow(exp.toDouble()), EXTENSION[exp - 1]
)
}
If you want the result to always have 2 decimal places, consider any of these
val result = count / COUNT_DIVISOR_FLOAT.pow(exp.toDouble())
// This will use the root language/region neutral locale
// it will use the dot '.' as the decimal separator
"%.2f".format(Locale.ROOT, result)
// This will use the default locale
// it will use '.' or ',' as the decimal separator, based on the user settings on the target system
"%.2f".format(result)
val localeDefinedByYou = ... // or define a specific locale
"%.2f".format(localeDefinedByYou, result)
Related
I have this code that keeps giving me a "Val cannot be reassigned" error but I can't seem to change the variable to a var instead of val. I simply want to be able to set a string value to my cell reference so I can access the values later like this myStringsArrayList.add(deviceData.cellOne).
Here is my code:
val cells = listOf(
deviceData.cellOne,
deviceData.cellTwo,
deviceData.cellThree,
deviceData.cellFour,
deviceData.cellFive,
deviceData.cellSix,
deviceData.cellSeven,
deviceData.cellEight,
deviceData.cellNine,
deviceData.cellTen,
deviceData.cellEleven,
deviceData.cellTwelve,
deviceData.cellThirteen,
deviceData.cellFourteen
)
for ((i, cell) in cells.withIndex()) {
val value = data[2 + i].toDouble() / 100 + 3.52
val cellNumberString = (i + 1).toString()
val formattedString = "Cell $cellNumberString: %.2fV".format(value)
cell = formattedString // THIS IS WHERE THE PROBLEM IS (cell is a val)
}
Does anyone know how I can get around this and achieve the functionality that I want?
I tried using a listIterator() but it hasn't seemed to work the way that I want it to.
Here is my attempt with the listIterator():
val cells = mutableListOf(
deviceData.cellOne,
deviceData.cellTwo,
deviceData.cellThree,
deviceData.cellFour,
deviceData.cellFive,
deviceData.cellSix,
deviceData.cellSeven,
deviceData.cellEight,
deviceData.cellNine,
deviceData.cellTen,
deviceData.cellEleven,
deviceData.cellTwelve,
deviceData.cellThirteen,
deviceData.cellFourteen)
val iterate = cells.listIterator()
while (iterate.hasNext()) {
var cell = iterate.next()
val value = data[2 + iterate.nextIndex()].toDouble() / 100 + 3.52
val cellNumberString = (iterate.nextIndex() + 1).toString()
val formattedString = "Cell $cellNumberString: %.2fV".format(value)
cell = formattedString
}
You can't make that a var, and there's no reason to anyway!
for ((i, cell) in cells.withIndex()) {
...
cell = formattedString // THIS IS WHERE THE PROBLEM IS (cell is a val)
}
You're creating a for loop there on a collection, with an index, and giving it a block of code to run for each loop. So when the loop runs, you're provided with two parameters - the current item from the collection, and its index in that collection.
These are just internal variables for use in the loop - you can't reassign them, because they're the values being passed in. Even if you could, you'd just be changing the values of those local variables.
What you're probably trying to do is update the cells list, taking the current item in the loop, finding it in cells, and replacing it. You'd have to actually update cells to do that! Change the list itself. You could do that with cells[i] = formattedString - but because you're currently iterating over that cells collection, you shouldn't modify it!
You could copy the source list, but the typical Kotlin way is to create a new list, using map (which transforms values):
cells.mapIndexed { i, cell ->
val value = data[2 + i].toDouble() / 100 + 3.52
val cellNumberString = (i + 1).toString()
// last expression is the return value, i.e. the formatted string
"Cell $cellNumberString: %.2fV".format(value)
}
That will spit out a new (immutable) list where each cell has been mapped to that formatted string version.
You could make cells a var and just reassign it:
cells = cells.mapIndexed { ... }
or just chain the map call when you initialise the val, so that end result is what gets assigned:
val cells = listOf(
...
).mapIndexed { ... }
But you're not actually using cell in that loop anyway, just using the index to generate values. You can create a list like this:
val data = List(14) { i ->
val value = data[2 + i].toDouble() / 100 + 3.52
// you can calculate the index inside the string by using braces
"Cell ${i + 1}: %.2fV".format(value)
}
It all depends whether you need to keep that list of devicedata values around for anything (if so use it to create another list)
You may wanna use Interator this way:
val list = mutableListOf("One", "Two", "Three", "Four")
println(list.joinToString(" "))
val iterator = list.listIterator()
while(iterator.hasNext()) {
val value = iterator.next()
if (value == "Two") {
iterator.set("xxxxxx")
}
}
println(list.joinToString(" "))
Use the 'set' method.
I have String with decimal points of value. When I convert the String value to Float like this
val i : String = "123.70"
val j = i.toFloat()
Log.e("Tag", "-->" + j)
the result printed is
Tag-->123.7
But, what I want is
Tag-->123.70
Is there any possible way to get my desired result?
You can simply use a String that takes a decimal and formats it to a specific amount of decimal places.
Example:
fun main() {
// you have a String with a decimal value
val i : String = "123.70"
// then you parse/convert it to a Float
val j : Float = i.toFloat()
// and then, you output the value of the Float in a specific format
println("%.2f".format(j))
}
This prints
123.70
which should be working in the LogCat as well:
Log.e("Tag", "-->%.2f".format(j))
internally it doesn't affect it is the same but if you want to show it as 2 decimal places you can use String.format("%.2f", number).
val number = 10.7086134
val rounded = String.format("%.3f", number) // rounds to 3 decimal places
In Android-Kotlin I am getting float number from backend (for example num = 10000000.47)
When I try to String.format it and add that number in my balanceTextview it shows it with exponent (something like 1.0E10).
I want to show number normally without exponent and with 2 decimals. (Without presicion loss!)
Tried to use DecimalFormat("#.##") but it didn't help me. Maybe I'm doing something wrong?
num = 10000000.47f
val dec = DecimalFormat("#.##")
var result = dec.format(num)
my result is: 10000000
It losts my decimal places
The issue is your number type. According to the documentation:
For variables initialized with fractional numbers, the compiler infers the Double type. To explicitly specify the Float type for a value, add the suffix f or F. If such a value contains more than 6-7 decimal digits, it will be rounded.
With an example that shows how information may get lost:
val pi = 3.14 // Double
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, actual value is 2.7182817
If the value is specified as Double instead of Float, i.e.
val num = 10000000.47
instead of
val num = 10000000.47f
then your approach works as expected, but could be shortened to:
"%.2f".format(num)
(note that the shorter version will also print "100" as "100.00" which is different from your approach but potentially still desired behaviour)
If you receive a Float from the backend then the information is already lost on your side. Otherwise you should be able to fix the issue by improved parsing.
The extension function format is only available in the JVM. In Kotlin/native, you can use this instead:
fun Float.toPrecision(precision: Int) =
this.toDouble().toPrecision(precision)
fun Double.toPrecision(precision: Int) =
if (precision < 1) {
"${this.roundToInt()}"
} else {
val p = 10.0.pow(precision)
val v = (abs(this) * p).roundToInt()
val i = floor(v / p)
var f = "${floor(v - (i * p)).toInt()}"
while (f.length < precision) f = "0$f"
val s = if (this < 0) "-" else ""
"$s${i.toInt()}.$f"
}
In Kotlin, as in Java, there is no overflow error in arithmetic operations. I know that there are special Java operations that test overflow and throw exceptions that need to be handled.
I would want a simpler way. So I thought of a model, which is not so efficient, but it is very simple and effective.
Suppose someone wants to test a 2 long numbers multiplication: a * b
I use
if ( a.doDouble()* b.toDouble() - a*b != 0.0 )
println("Overflow")
else
println("Ok")
The justification is simple. Within the universe of Long the difference between a number and its Double is always 0, even at extreme values, when the Double does not reach all precision. In this case, adding or subtracting a small number does not even change the equality test:.
var l1= -Long.MAX_VALUE
var d1 = l1.toDouble()
if (d1-l1==0.0) println("-MaxLong")
if (d1+100-l1==0.0) println("it still -MaxLong")
var l2= Long.MAX_VALUE
var d2 =l2.toDouble()
if (d2-l2==0.0) println("MaxLong")
if (d2+100-l2==0.0) println("it still MaxLong")
This generates the output:
-MaxLong
it still -MaxLong
MaxLong
it still MaxLong
Is it correct or I'm missing something?
Even if it's correct, is there any other solution better than this?
Update 1: Notice that other possibility is testing if Double calculation is greater that longValue.MAXVALUE. However, it fails!
var n1= Long.MAX_VALUE/2+1
var n2= Long.MAX_VALUE/2+1
println((n1.toDouble()+n2.toDouble()) -
Long.MAX_VALUE.toDouble()==0.0)
println((n1.toDouble()+n2.toDouble()) > Long.MAX_VALUE.toDouble())
It prints:
true
false
Update 2: Although my solution seems to work, it doesn't!
Alexey Romanov, points me in his accepted answer the following situation:
val lo1 = Long.MAX_VALUE - 600
val lo2 = 100L
var do1: Double = lo1.toDouble()
var do2:Double = lo2.toDouble()
var d= do1+do2
var l=lo1+lo2
println(d-l==0.0)
As the result is inside Long range, it should gives true, but it gives false, because Double calculation is not exact!
As he said, the best way is really using special functions like multiplyExact encapsulated in an user function.
Unfortunately, its resources only can be used in Android from API 24 onwards, so it rests the other solution from Alexey Romanov, that consists in test the inverse operation.
So, for instance, in the multiplication one should do:
var a = Long.MIN_VALUE
var b = -1L
var c = a*b
if (b!=0 && c/b != a)
println("overflow $c")
else
println("ok $c")
It prints overflow -9223372036854775808
Among traditional operations, there are usually concerns with addition, subtraction, and multiplication, which are the object of the functions addExact, subtractExact, multipyExact functions, that are easily emulated using inverse operations, as cited.
Negation (inv()) also has the negateExact function to deal with the negation of Long.MIN_VALUE, which is invalid as it has no positive counterpart. Less commented is the division, which has no specialized function in Java to lead with overflow. However it gives problem in a single case: Long.MIN_VALUE / -1 is invalid.
Within the universe of Long the difference between a number and its Double is always 0
No, not really.
println(Long.MAX_VALUE)
println(BigDecimal(Long.MAX_VALUE.toDouble()))
prints
9223372036854775807
9223372036854775808
You tried to check this:
var l2= Long.MAX_VALUE
var d2 =l2.toDouble()
if (d2-l2==0.0) println("MaxLong")
But the problem is that arithmetic operations on JVM (and in most languages, really) can only work on values of the same type, so the compiler inserts toDouble() and you really calculate d2 - l2.toDouble().
If you want a simple test, you can do
val product = a*b
if ((b != 0 && product/b != a) || (a == Long.MIN_VALUE && b == -1)) {
println("Overflow")
} else {
// can use product here
println("OK")
}
but really, using multiplyExact instead of doing it manually makes more sense. Or use Kotlin's nullable types and define
fun multiplyExact(x: Long, y: Long): Long? =
try { java.math.multiplyExact(x, y) } catch (e: ArithmeticException) { null }
EDIT: to demonstrate a fault in your test, consider addition (I am pretty sure it's wrong for multiplication as well, but it's harder to find suitable numbers):
val largeNumber = Long.MAX_VALUE - 600
val smallNumber = 100L
// prints true, even though there's no overflow
println((largeNumber.toDouble() + smallNumber.toDouble()) - (largeNumber + smallNumber) != 0.0)
The reason is that largeNumber.toDouble() + smallNumber.toDouble() == largeNumber.toDouble() while (largeNumber + smallNumber).toDouble() == Long.MAX_VALUE.toDouble().
You should know that Long DataType has a fixed number of bytes Oracle Docs
The long data type is a 64-bit signed two's complement
integer. It has a minimum value of -9,223,372,036,854,775,808 and a
maximum value of 9,223,372,036,854,775,807 (inclusive). Use this data
type when you need a range of values wider than those provided by int.
//if it is not within the range then its an overflow (infinity/undefined)
if(a*b < Long.MIN_VALUE || a*b > Long.MAX_VALUE)
println("Overflow")
else
println("Ok")
Edit
Truly and unfortunately the above method is not reliable. See below table from a run test on android studio with JDK 8
##### Overflow Test #########
Long.MAX_VALUE = 9223372036854775807
Long.MIN_VALUE = -9223372036854775808
Long.MAX_VALUE - 2 = 9223372036854775805
Long.MAX_VALUE - 1 = 9223372036854775806
Long.MAX_VALUE - 0 = 9223372036854775807
Long.MAX_VALUE + 0 = 9223372036854775807
Long.MAX_VALUE + 1 = -9223372036854775808
Long.MAX_VALUE + 2 = -9223372036854775807
Long.MAX_VALUE * 2 = -2
Long.MAX_VALUE / 2 = 4611686018427387903
Long.MIN_VALUE - 2 = 9223372036854775806
Long.MIN_VALUE - 1 = 9223372036854775807
Long.MIN_VALUE - 0 = -9223372036854775808
Long.MIN_VALUE + 0 = -9223372036854775808
Long.MIN_VALUE + 1 = -9223372036854775807
Long.MIN_VALUE + 2 = -9223372036854775806
Long.MIN_VALUE * 2 = 0
Long.MIN_VALUE / 2 = -4611686018427387904
Long.MIN_VALUE + Long.MAX_VALUE = -1
Long.MAX_VALUE - Long.MIN_VALUE = -1
Long.MAX_VALUE * Long.MIN_VALUE = -9223372036854775808
Long.MAX_VALUE / Long.MIN_VALUE = 0
Long.MIN_VALUE / Long.MAX_VALUE = -1
Long.MAX_VALUE + Long.MAX_VALUE = -2
Long.MIN_VALUE + Long.MIN_VALUE = 0
Double.MAX_VALUE = 1.7976931348623157E308
Double.MAX_VALUE * 2 = Infinity
Double.MAX_VALUE + Double.MAX_VALUE = Infinity
Long.MAX_VALUE * Double.MAX_VALUE = Infinity
Double.MAX_VALUE > Long.MAX_VALUE = true
Double.MIN_VALUE < Long.MIN_VALUE = true
Looking at the log you would notice anytime Long.MAX_VALUE reaches its peak instead of hitting Infinity like Double.MAX_VALUE, the bit is switched and its next value becomes Long.MIN_VALUE and it goes on and on like that.
So now we see why the above method isn't reliable. Hence we can assume that in java Long is a DataType with zero Infinity.
Method modified introducing floating point constants in-between
//using floating points forces larger memory allocation
//this prevents bit switch after crossing max or min value of Long
if(a * 1.0 * b < Long.MIN_VALUE || a * 1.0 * b > Long.MAX_VALUE)
println("Either a Double or Long Overflow")
else
println("Ok")
I am summing double value from arraylist its giving additional decimals as 99999, how to fix this, please guide
ex
class ExDet{var expName:String ="",var expAmount:Double = 0.0}
val arrayList = ArrayList<ExDet>()
arrayList.add(ExDet("Abc 1",45.66))
arrayList.add(ExDet("DEF 1",10.0))
arrayList.add(ExDet("Lee 1",600.89))
arrayList.add(ExDet("Ifr 1",200.9))
var amt = arrayList.sumByDouble{ it.expAmount }
Expected Value of Amount is :
Amt = 857.45
But it returns
Amt = 857.4499999
Sample Code to Test
data class ExDet(var expName:String ="" ,var expAmount:Double=0.0)
fun main(args: Array<String>) {
val arrayList = ArrayList<ExDet>()
arrayList.add(ExDet("Abc 1",45.66))
arrayList.add(ExDet("DEF 1",10.0))
arrayList.add(ExDet("Lee 1",600.89))
arrayList.add(ExDet("Ifr 1",200.9))
var amt = arrayList.sumByDouble{ it.expAmount }
println("Amount is : $amt")
}
The issue you are confronted with is that floating point numbers are build on top of base 2, not base 10.
Think how you can easily represent a third as a fraction (1/3), but when you convert to decimal you get a repeating (recurring) number after the radix point (i.e. 0.33...). Some decimal numbers are recurring when represented in base-2, e.g. x.9. The computer has a finite number of bits, so the (base-2) number is truncated. All the truncation errors can add up.
You need to round to the required precision (e.g. round(x * 100) / 100).
If you are only interested in how it is displayed then you can use the format function with something like "%.2f".
String.format("%.2f", value)