Многие многочисленные дубликаты этого вопроса задают вопрос о влиянии округления с плавающей запятой на конкретные числа. На практике легче понять, как это работает, глядя на точные результаты вычислений, а не просто на чтение. Некоторые языки предоставляют способы сделать это - например, преобразование float
или double
в BigDecimal
в Java.
Так как это вопрос, связанный с языком, ему нужны языковые агностические инструменты, такие как как Десятичный преобразование с плавающей запятой .
Применяя его к числам в вопросе, рассматриваемым как удваивает:
0,1 преобразуется в 0,1000000000000000055511151231257827021181583404541015625,
0,2 преобразуется в 0.200000000000000011102230246251565404236316680908203125,
0,3 конвертируется в 0,29999999999999999989897769753748434595763683319091796875 и
0,30000000000000004 преобразуется в 0,3000000000000000444089209850062616169452667236328125.
Добавление первых двух чисел вручную или в десятичный калькулятор, такой как Full Precision Calculator , показывает точную сумму фактических входов: 0.3000000000000000166533453693773481063544750213623046875.
Если округлить до эквивалента 0,3, ошибка округления будет 0.0000000000000000277555756156289135105907917022705078125. Округление до эквивалента 0,30000000000000004 также дает ошибку округления 0,0000000000000000277555756156289135105907917022705078125.
Возвращаясь к конвертеру с плавающей запятой, необработанный шестнадцатеричный показатель для 0.30000000000000004 равен 3fd3333333333334, который заканчивается четной цифрой и, следовательно, является правильным результатом.
Как правильно указано @AndroidEx, присваивания не являются выражениями в Kotlin, в отличие от Java. Причина в том, что выражения с побочными эффектами обычно обескуражены. См. это обсуждение по аналогичной теме.
Одно из решений состоит в том, чтобы разделить выражение и перенести присвоение из блока условий:
a = b
if (a != c) { ... }
Другое один из них - использовать функции из stdlib, такие как let
, который выполняет лямбда с приемником в качестве параметра и возвращает результат лямбда. apply
и run
имеют схожую семантику.
if (b.let { a = it; it != c }) { ... }
if (run { a = b; b != c }) { ... }
Благодаря inlining это будет так же эффективно, как и обычный код взятый из лямбда.
Ваш пример с InputStream
будет выглядеть как
while (input.read(bytes).let { tmp = it; it != -1 }) { ... }
. Также рассмотрите функцию readBytes
для чтения a ByteArray
из InputStream
.
простой способ в котлине
if (kotlin.run{ a=b; a != c}){ ... }
Присвоения не являются выражениями в Kotlin, поэтому вам нужно сделать это снаружи:
var a: Int? = 1
var b: Int? = 2
var c: Int? = 1
a = b
if (a != c)
print(true)
Для вашего другого примера с InputStream
вы могли бы сделать:
fun readFile(path: String) {
val input: InputStream = FileInputStream(path)
input.reader().forEachLine {
print(it)
}
}
Я думаю, это может вам помочь:
input.buffered(1024).reader().forEachLine {
fos.bufferedWriter().write(it)
}
Как почти все здесь отметили, присваивания не являются выражениями в Котлине. Однако мы можем принуждать присваивание к выражению, используя литерал функции:
val reader = Files.newBufferedReader(path)
var line: String? = null
while ({ line = reader.readLine(); line }() != null) {
println(line);
}