Могу ли я преобразовать Int64 непосредственно в Int?

В последнее время я использую SQLite.swift для создания базы данных приложений. И я определяю все свои столбцы INTEGER с типом Int64, как объясняется в документации.

Но время от времени мне нужно, чтобы Int64 было просто Int. Итак, мой вопрос: если я сделаю это:

//Create a table with Int instead of Int64
let test_id = Expression<Int>("test_id")
let tests = db["tests"]

db.create(table: tests, ifNotExists: true){ t in
    t.column(test_id)
}


class func insertTest(t: Int) -> Int{
    //insert.rowid returns an Int64 type
    let insert = tests.insert(test_id <- t)
    if let rowid = insert.rowid{
        //directly cast Int64 into Int
        return Int(rowid)
    }
    return 0
}

Будет ли это правильно?

Конечно, я протестировал его. И это работает, но я читал этот вопрос в Stackoverflow< /а>

И кажется, что у меня могут быть проблемы с 32-битными устройствами...

Если это неправильно, как я могу преобразовать Int64 в Int?


person Renato Parreira    schedule 26.09.2015    source источник
comment
Как вы думаете, что произойдет, если значение Int64 больше 2^31, а Int 32-битный?   -  person zaph    schedule 26.09.2015
comment
@zaph переполняется? Я не знаю...   -  person Renato Parreira    schedule 26.09.2015
comment
@zaph ты можешь мне объяснить?   -  person Renato Parreira    schedule 26.09.2015
comment
Если INT64 имеет ключ, который использует более 32 бит, и вы присваиваете его Int, который имеет только 32 бита, лишние биты теряются и значение неверно. Есть ресурсы, где можно прочитать о том, как значения хранятся в памяти, или вы можете просто изучить правила. Понимание этого, возможно, лучше, но у всех есть уровень абстракции, ниже которого они не опускаются. Существует также уровень абстракции, который на самом деле практически невозможно опустить ниже, например, знание точного физического адреса в оперативной памяти, по которому значение находится на текущем обычно используемом уровне кодирования в современных схемах распределения.   -  person zaph    schedule 26.09.2015
comment
Это похоже на попытку перелить содержимое бутылки на 36 унций в бутылку на 32 унции, последние 4 унции просто прольются на пол и будут потеряны.   -  person zaph    schedule 26.09.2015
comment
@zaph да, теперь я понимаю, спасибо и вам!   -  person Renato Parreira    schedule 26.09.2015


Ответы (4)


Преобразование Int64 в Int путем передачи значения Int64 инициализатору Int всегда будет работать на 64-битной машине, а на 32-битной машине произойдет сбой, если целое число выходит за пределы диапазона Int32.min ... Int32.max.

В целях безопасности используйте инициализатор init(truncatingIfNeeded:) (ранее известный как init(truncatingBitPattern:) в более ранних версиях Swift) для преобразования значения:

return Int(truncatingIfNeeded: rowid)

На 64-битной машине truncatingIfNeeded ничего не сделает; вы просто получите Int (который в любом случае имеет тот же размер, что и Int64).

На 32-битной машине это отбрасывает верхние 32 бита, но если они все нули, то вы не потеряли никаких данных. Так что, пока ваше значение будет соответствовать 32-битному Int, вы можете сделать это без потери данных. Если ваше значение выходит за пределы диапазона Int32.min ... Int32.max, это изменит значение Int64 на то, что соответствует 32-битному Int, но не приведет к сбою.


Вы можете увидеть, как это работает на игровой площадке. Поскольку Int в Playground является 64-битным Int, вы можете явно использовать Int32 для имитации поведения 32-битной системы.

let i: Int64 = 12345678901  // value bigger than maximum 32-bit Int

let j = Int32(truncatingIfNeeded: i)  // j = -539,222,987
let k = Int32(i)                        // crash!

Обновление для Swift 3/4

В дополнение к init(truncatingIfNeeded:), который все еще работает, Swift 3 вводит отказоустойчивые инициализаторы для безопасного преобразования одного целочисленного типа в другой. Используя init?(exactly:), вы можете передать один тип для инициализации другого, и он возвращает nil, если инициализация не удалась. Возвращаемое значение является необязательным и должно быть развернуто обычными способами.

Например:

let i: Int64 = 12345678901

if let j = Int32(exactly: i) {
    print("\(j) fits into an Int32")
} else {
    // the initialization returned nil
    print("\(i) is too large for Int32")
}

Это позволяет применить оператор объединения nil для предоставления значения по умолчанию, если преобразование не удалось:

// return 0 if rowid is too big to fit into an Int on this device
return Int(exactly: rowid) ?? 0
person vacawama    schedule 26.09.2015
comment
В текущих версиях Swift 3 и 4 Int(truncatingBitPattern:) не существует, а Int(truncating:) принимает NSNumber, поэтому не применимо к Int64. Правильный вызов теперь Int(truncatingIfNeeded:). Я добавил отдельный ответ, чтобы уточнить. - person jedwidz; 13.09.2018
comment
@jedwidz, спасибо за предупреждение. Я исправил ответ. - person vacawama; 13.09.2018

Если вы уверены, что значение Int64 можно точно представить как Int, используйте Int(truncatingIfNeeded:), например:

let salary: Int64 = 100000
let converted = Int(truncatingIfNeeded: salary)

Для сборок, ориентированных на 32-разрядные устройства, диапазон для Int ограничен от -2147483648 до 2147483647, как и Int32. Старшие биты значений за пределами этого диапазона будут незаметно отброшены. Это приводит к мусору, часто противоположного знака.

Если значение может быть вне диапазона, и вы хотите обработать это условие, используйте Int(exactly:), например:

if let converted = Int(exactly: salary) {
    // in range
    ... converted ...
} else {
    // out-of-range
    ...
}

В конкретном случае rowid использование Int64 вместо Int было преднамеренным выбором дизайна API, и усечение до Int могло быть ошибкой.

person jedwidz    schedule 13.09.2018

Это боль в заднице. Это монументально глупее, чем в Objective-C. Но вот как вы это делаете.

Если вы используете UInt64, вы делаете это так.

let thatVarYouWantToConvert: UInt64 = 1
let myInt: Int = Int(exactly:thatVarYouWantToConvert ?? 0)

Если вы используете UInt64?, вы делаете это так.

let thatVarYouWantToConvert: UInt64? = 1
let myInt: Int = Int(exactly:thatVarYouWantToConvert ?? 0) ?? 0

Как я уже сказал, «это монументально глупее, чем в Objective-C».

Протестировано в Swift 4.2, Xcode 10.2.1.

person Alex Zavatone    schedule 02.08.2019

На самом деле я тоже работал с этим фреймворком, и в основном я просто использовал противоположное решение. Всякий раз, когда вы видите, что типы не совпадают, просто делайте

Int64(yourInt) 

(протестировано с Xcode 7, Swift 2.0)

person René Blaser    schedule 23.02.2016
comment
Это может привести к снижению производительности на старых телефонах и снижению совместимости библиотек. Если вы конвертируете туда и обратно без причины, это приведет к большому количеству ненужных подслушиваний, использованию памяти и потенциальным ошибкам в вашем коде. Вероятно, лучше просто преобразовать в Int, если вы знаете, что нет никакого возможного сценария, в котором вашему приложению придется иметь дело с числами больше, чем 2^31-1. Если вам, возможно, придется использовать числа больше этого, используйте Int64. Просто не конвертируйте во время выполнения, если в этом нет необходимости. - person Ben Aubin; 05.07.2018