Что происходит при бросании большого числа типа int? - PullRequest
5 голосов
/ 10 апреля 2020

Мне было интересно, что произойдет, если я приведу очень большое значение с плавающей точкой к целому числу. Это пример, который я написал:

fn main() {
    let x = 82747650246702476024762_f32;//-1_i16;
    let y = x as u8;
    let z = x as i32;
    println!("{} {} {}", x, y, z);
}

и вывод:

$ ./casts                                     
82747650000000000000000 0 -2147483648

Очевидно, что число с плавающей запятой не поместится ни в одно из целых чисел, но так как Rust так сильно объявляет, что это безопасно, я бы ожидал какую-то ошибку. В этих операциях используются команды llvm fptosi и fptoui, которые выдают так называемое значение яда , если значение не соответствует типу, к которому оно было приведено. Это может привести к неопределенному поведению, что очень плохо, особенно при написании кода на Rust.

Как я могу быть уверен, что мой метод с плавающей точкой в ​​int не приводит к неопределенному поведению в Rust? И почему Rust даже допустил это (как известно, для создания безопасного кода)?

Ответы [ 2 ]

4 голосов
/ 11 апреля 2020

Если вы используете as для приведения числа с плавающей запятой к целочисленному типу, а число с плавающей запятой не подходит ¹ в типе назначения, результатом будет неопределенное значение², и большинство вещей, которые вы можете сделать с помощью это вызывает неопределенное поведение.

Это, безусловно, неудачно! Это проблема # 10184 на GitHub, и это один из немногих способов, которыми вы можете все еще вызывать неопределенное поведение в безопасном Rust. На самом деле, это самая старая ошибка в Rust.

Сегодня, если вы запускаете Rust с флагом -Z saturating-float-casts, вместо этого с плавающей точкой в ​​целочисленное значение выполняется насыщение (то есть слишком большие или маленькие значения преобразуется в T::MAX или T::MIN соответственно; NaN преобразуется в 0). Включение этого флага, скорее всего, замедлит большинство приведений с плавающей запятой, поскольку они должны сначала проверять границы типов, поэтому по умолчанию он еще не включен. Однако в будущем следует ожидать, что безопасное, но более медленное насыщение станет поведением as, и появится новый API для ручного использования быстрого преобразования, но - unsafe.

(Раньше существовала похожая проблема для определенных приведений к целочисленным значениям , но она была решена путем насыщения таких приведений всегда. Это изменение не считалось снижением производительности, и нет способ выбрать старое поведение.)

Также читайте


¹ «Подгонка» здесь означает либо NaN, либо число такой большой величины, что оно не может быть аппроксимировано меньшим типом. 8.7654321_f64 все равно будет усечен до 8 приведением as u8, даже если значение не может быть точно представлено целевым типом - потеря точности не приводит к неопределенному поведению, а только вне диапазона.

² «Ядовитое» значение в LLVM, как вы правильно заметили в вопросе, но сама Rust не различает guish между undef и отравляющими значениями.

0 голосов
/ 11 апреля 2020

Часть вашей проблемы "как" выполняет броски с потерями, то есть вы можете без проблем привести u8 к u16, но u16 не всегда может уместиться в u8, поэтому u16 усекается. Вы можете прочитать о поведении «как» здесь .

Rust спроектирован так, чтобы быть безопасным для памяти, что означает, что вы не можете получить доступ к памяти, которую вы не должны, или иметь гонки данных, (если вы не используете небезопасный), но у вас все еще могут быть утечки памяти и другое нежелательное поведение.

То, что вы описали, является неожиданным поведением, но оно все еще хорошо определено, это очень разные вещи. Два разных компилятора ржавчины скомпилировали бы это с кодом, который имел бы тот же результат, если бы это было неопределенное поведение, тогда у разработчика компилятора могла бы быть эта компиляция к тому, что они хотели, или не компилировали вообще.

Редактировать: Как указано в комментариях и других ответах приведение числа с плавающей точкой к int с использованием, поскольку в настоящее время вызывает неопределенное поведение, это связано с известной ошибкой в компиляторе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...