Các phép toán số
Mô tả các thao tác bao bọc và kiểm tra số nguyên, các ca, lỗi chuyển đổi kiểu và kết quả dấu phẩy động.
Wave Foundation
biểu diễn số nguyên
Số nguyên bit N có các bit giá trị N. Số nguyên không dấu nằm trong khoảng từ 0 đến 2^N − 1 và số nguyên có dấu nằm trong khoảng từ −2^(N−1) đến 2^(N−1) − 1. signedness của thao tác xác định cách diễn giải chuỗi bit.
Bảng dưới đây giải thích kết quả tính toán. IR Ví dụ về mã sử dụng cách trình bày của máy in hiện tại.
Cộng, trừ, nhân
Các số nguyên cơ bản add·sub·mul giữ các bit N thấp hơn của kết quả. overflow không gây ra trap. Thao tác checked trả về cùng một kết quả wrap, cũng như Bool, cho biết kết quả toán học có nằm ngoài phạm vi của signed hoặc unsigned tương ứng hay không.
| hoạt động | Wrap Kết quả | Checked overflow |
|---|---|---|
| u8: 255 + 1 | 0 | true |
| i8: 127 + 1 | −128 | true |
| u8: 0 − 1 | 255 | true |
| i8: 12 × 3 | 36 | false |
Giao diện người dùng cho các ngôn ngữ phá vỡ quá trình thực thi tại overflow phải sử dụng trap_if rõ ràng cho kết quả overflow của thao tác checked. Thao tác mặc định không ngầm áp dụng chính sách overflow của ngôn ngữ nguồn.
Wrap và IR thể hiện sự kiểm tra rõ ràng
Các mô-đun sau được định cấu hình là Rust builder và là đầu ra máy in hiện tại đã vượt qua trình xác minh. Trình phân tích cú pháp văn bản và phần phụ trợ thực thi vẫn chưa có sẵn.
module {
format_version 2
semantics_version 1
target "x86_64-whale-linux"
datalayout { ptr=64, endian=little }
declare @f0 "add_u8": whale () -> u8, linkage internal
declare @f1 "require_no_overflow": whale () -> u8, linkage internal
fn @add_u8() -> u8, id @f0 {
entry:
%v0: u8 = const u8 255
%v1: u8 = const u8 1
%v2: u8 = add u8 %v0, %v1
ret u8 %v2
}
fn @require_no_overflow() -> u8, id @f1 {
entry:
%v3: u8 = const u8 255
%v4: u8 = const u8 1
%v5: tuple<u8, bool> = uadd_chk u8 %v3, %v4
%v6: u8 = extract %v5, 0
%v7: bool = extract %v5, 1
trap_if bool %v7, reason="integer overflow"
ret u8 %v6
}
}
Theo hợp đồng số học, add_u8 gói 256 thành kết quả 8 bit 0. require_no_overflow trích xuất kết quả được gói bằng extract ..., 0 và cờ tràn Bool với extract ..., 1. Nếu cờ là true, trap_if dừng thực thi trước khi trả về. Đây là kết quả được chỉ định, không phải kết quả đầu ra từ trình thông dịch được triển khai.
Phép chia và số dư
Phép chia số nguyên hoặc số dư cho 0 gây ra bẫy. Chia giá trị có dấu tối thiểu cho −1 sẽ bao bọc giá trị tối thiểu. Phần còn lại trong trường hợp này là bằng không.
| hoạt động | kết quả |
|---|---|
| i8: −128 / −1 | −128 |
| i8: −128 % −1 | 0 |
| Chia cho số nguyên 0 | trap |
| phần dư của số nguyên 0 | trap |
sự thay đổi
Khi dịch chuyển giá trị bit N, chuỗi bit của count được hiểu là unsigned và phần còn lại chia cho N được sử dụng. Nó không tạo ra trap chỉ vì count nằm ngoài phạm vi từ 0 đến N−1.
Trong các giá trị 8 bit, count 0·8·16 đều là các dịch chuyển 0 bit. Chuỗi bit 11111111 gồm 8 bit count được dịch chuyển 7 bit. Điều này tương tự ngay cả khi chuỗi bit này đại diện cho signed −1. unsigned Quá trình phân tích diễn ra trước các phép tính còn lại.
Các ngôn ngữ nguồn từ chối tiêu cực hoặc quá mức count phải kiểm tra rõ ràng trước ca làm việc.
chuyển đổi kiểu
| chuyển đổi | ý nghĩa |
|---|---|
| Zero extension | Tăng chiều rộng bằng cách điền các bit bậc cao bằng 0 |
| Sign extension | Tăng chiều rộng bằng cách nhân đôi bit dấu |
| cắt bit | Chỉ giữ lại các bit bậc thấp tương ứng với độ rộng đích |
| Beat diễn giải lại | Giải thích cùng một chuỗi bit với các loại khác nhau |
| Chuyển đổi số không mất dữ liệu | Nếu nó không thể được biểu thị trong khi vẫn giữ nguyên giá trị số, trap |
Ví dụ: chuyển đổi 8 bit 11111111 thành 16 bit zero extension trở thành 0000000011111111 và sign extension trở thành 1111111111111111. Ngay cả khi các bit đầu vào giống nhau thì chúng vẫn là các hoạt động khác nhau.
chuyển đổi float-to-int bỏ phần thập phân theo hướng về 0, sau đó kiểm tra phạm vi số nguyên. NaN và vô cực gây ra bẫy. Khi chuyển đổi sang i8, 127,9 trở thành 127, trong khi 128,0 bẫy. Bool chuyển đổi thành số nguyên 0 hoặc 1. Đã ký i1 không thể đại diện cho 1, vì vậy nó không thể là đích đến của chuyển đổi này.
Việc chuyển đổi một địa chỉ thành một số nguyên không khôi phục quyền truy cập của con trỏ vào số nguyên đó. Vui lòng tham khảo tính hợp lệ của con trỏ.
số học dấu phẩy động
Các giá trị dấu phẩy động có chuỗi bit chính xác f16·f32·f64. Thao tác làm tròn đến giá trị gần nhất trong chiều rộng đã khai báo và, nếu chính xác ở giữa, sử dụng ties-to-even, chọn một giá trị có số bit có ý nghĩa nhỏ nhất trong số các chữ số có nghĩa.
Hoạt động mặc định là fast-math, ẩn FMA, không cho phép xử lý bắt buộc bằng 0 đối với các giá trị nhỏ. Phép nhân theo sau phép cộng sẽ giữ nguyên các bước làm tròn tương ứng của chúng và phần phụ trợ không được ngầm kết hợp chúng thành một thao tác.
Kết quả của phép toán số có thể là NaN hoặc vô cùng. NaN từ các phép toán và thay đổi độ rộng được chuẩn hóa thành một lượng NaN tĩnh cố định trên mỗi chiều rộng. Lưu và sao chép giữ nguyên các bit NaN gốc. Do đó, hành vi sẽ khác khi chuyển tải trọng NaN vào bộ nhớ mà không cần thực hiện các phép toán số học và khi tính toán nó.
Nó không hiển thị cờ trạng thái dấu phẩy động. Mặc dù số học dấu phẩy động cơ bản cho phép NaN·kết quả vô cực, nhưng phép chuyển đổi float→int áp dụng các quy tắc trap ở trên.
Lưu trữ các hằng số chính xác
Sử dụng FloatBits variant hoặc bất kỳ chuỗi bit thập lục phân nào có chiều rộng chính xác. Đẳng thức của các giá trị được lưu trữ được so sánh với chuỗi bit chứa âm 0 và NaN payload. Trình xác minh sẽ từ chối nếu độ rộng của loại IR và payload khác nhau.
use ir::{FloatBits, ModuleBuilder, Target, Type};
fn main() {
let bits = FloatBits::parse(32, "0xffc01234").unwrap();
assert_eq!(bits, FloatBits::F32(0xffc01234));
let target = Target::X86_64WhaleLinux;
let mut module = ModuleBuilder::new(target.name(), target.data_layout());
let mut function = module.begin_function("payload", vec![], Type::F32);
let value = function.const_float_bits(Type::F32, bits);
function.ret(Some(value));
function.finish();
let module = module.finish();
ir::verify_module(&module).unwrap();
assert!(ir::print_module(&module).contains("const f32 0xffc01234"));
println!("{}", bits);
}
0xffc01234
Chuỗi bit f16, f32 hoặc f64 bắt đầu bằng 0x, theo sau là chính xác 4, 8 hoặc 16 chữ số thập lục phân tương ứng. 0x80000000 đại diện cho f32 số 0 âm; 0x7f800000 đại diện cho cực dương. const_float chuyển đổi giá trị máy chủ f64 bằng số; sử dụng const_float_bits để bảo toàn các bit gốc. Biểu diễn lưu trữ chính xác không ngụ ý một chương trình phụ trợ thực thi dấu phẩy động hoàn chỉnh. Số học tại thời điểm biên dịch vẫn sử dụng máy chủ trung gian f64, do đó, hợp đồng làm tròn đầy đủ cho mỗi chiều rộng được khai báo vẫn chưa được triển khai.