I'm writing a multiplatform library and stuck with casting a `Double` . Here is an example: ```val n...
a
I'm writing a multiplatform library and stuck with casting a
Double
. Here is an example:
Copy code
val n = 1.1 as Any
if (n is Number) {
    println(n.toLong())
    println(n.toLong() == 1L)
}
if (n is Int) {
    println(n.toLong())
    println(n.toLong() == 1L)
}
Both conditions are resolved to
true
(I know that Javascript hasn't integer numbers so its ok). But what is more interesting it prints:
Copy code
1
true
1
false
When I use
kotest
it fails with
expected:<1L> but was:<1.1L>
.
1.1L
looks really strange for me.
b
Is this happening on js?
Looks like a bug. Maybe raise an issue? My guess is that it has something to do with autocasting to Int
e
JS doesn't keep extra type information and all of Kotlin's basic numeric types (except Long) are represented as JS Number, so I think this is expected
🤨 1
b
But then first if would also print false
e
why?
Number.toLong()
actually performs conversion, while
Int.toLong()
can assume that it's already truncated
👍 1
which isn't actually true here due to the upcast, but I'm pretty sure that's what's happening here
a
I'm not sure it is a bug. More like a feature on the JS platform. But I can't think of what to do with this. I've got a
Map<String, Any?>
and I need to process somehow values from it.
Solved by checking platform at runtime:
Copy code
private val isJs = (0.1 as Number) is Int

if (isJs && v is Number) {
    return v.toLong()
}
😿 1
🤨 1
b
More reliable way to check platform is expect/actual values
Copy code
// commonMain
expect val isJS:Boolean

// jsMain
actual val isJS = true

// jvmMain
actual val isJS = false
👍🏻 1
a
Some time ago had troubles with
detekt
and
expect-actual
but then refactored
detekt
tasks. Recently have tested
expect-actual
and it works.
t
Actually js number type is a float. It doesn't feel entirely correct to map Int to it...
b
JS number type is literally Number (supertype of Float, Double, Int...)
t
@Big Chungus wrong - JS does not have Float, Double, nor Int types. https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number-objects Although it does not implies that Number is a float, but 64-bit float meets specification requirements, while Int doesn't. Of course Number type could also be union and have multiple binary representations, but seems unlikely that common js engines implement it this way. "The JavaScript 
Number
 type is a double-precision 64-bit binary format IEEE 754 value, like 
double
 in Java or C#." https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
b
That's actually what I meant. Js only has Number type. Kotlin also has Number type that's (in non js targets) is a supertype of Int, Float, Double...
But to maintain mpp stdlib, on kotlin js target Number == Float == Double == Int
t
but. Js Number type is not the same as Kotlin Number type. It's more like Float Kotlin type.
b
In kotlin and java Float is Number. Same as Int is Number
It's just a least common denominator
t
But on Number !== Float in Kotlin. My point was: "Actually js number type is a float. It doesn't feel entirely correct to map Int to it..."
b
Looks like you're talking about implementation details between platforms, while I'm focusing on kotlin represntations. That's not to say that either of them is incorrect.
t
Well... I just wanted to point out, that it doesn't feel correct to use JS Number as representation of Int. It does violate Int contract:
Copy code
Represents a 32-bit signed integer. On the JVM, non-nullable values of this type are represented as values of the primitive type int.
Int kotlindoc. And we can see strange side effects, as OP posted, caused by violation of Kotlin semantics.
b
Fair enough. But seeing how messy JS world is I think discrepancies like these are common 😀
😢 1
s
JS Number type (64-bit floating point) can safely store 32-bit integers. When we do Int arithmetic in Kotlin we keep this contract by using bitwise OR operator (
|
) tricks or
Math.imul
(JS engines are good at compiling these to native 32-bit integer instructions). The problem that breaks contract seems to be with
is
type checks. It might be worth checking for 32-bitness there.
👍🏻 1
t
@Svyatoslav Kuzmich [JB] But cannot safely store 64-bit Long and it seems it's being used for Long as well ; )
s
True. Thats why we use a class with two Ints instead
^^ JS BigInt would probably be faster, but we haven’t managed to switch to it yet.
t
Oh, ok. So only typechecking is an issue and you already have ticket for it : ) https://youtrack.jetbrains.com/issue/KT-33358
e
I wonder if BigInt64Array(1) would be faster than BigInt, since the former only has to support fixed size int64 (equivalent to kotlin.Long) while the latter supports arbitrarily large values
1
t
@ephemient Hmm... probably you would need to convert to BigInt to perform any math operation.
e
@Tomasz Krakowiak it already works out of the box, accessing a BigInt64Array returns BigInts that perform 64-bit arithmetic
t
@ephemient But if operations are performed on BigInt, than you don't save nor time nor memory
e
they're special BigInt-like objects that are 64-bit
t
While they are in array yes. But, are they when you take them out oof the array too perform arithmetic operation?
Hmm... theoretically js compiler could notice such special case and optimize that... but I would rather expect that sooner would Int and Longs would be introduced to ES.
e
hmm, fair point. there's no syntax shortcut like
|0
that current JS implementations use to recognize limited-precision on integer operations, for bigint