Điểm:2

Giảm barret để nhận phần còn lại 64 bit của số 128 bit

lá cờ ru

Trên github có cái này phần mã của SEAL của Microsoft:

SEAL_ITERATE(iter(toán hạng1, toán hạng2, kết quả), coeff_count, [&](auto I) {
    // Rút gọn z bằng cách sử dụng cơ số 2^64 giảm Barrett
    không dấu dài dài z[2], tmp1, tmp2[2], tmp3, mang theo;
    multi_uint64(get<0>(I), get<1>(I), z);

    // Nhân đầu vào và const_ratio
    // Vòng 1
    multi_uint64_hw64(z[0], const_ratio_0, &carry);
    multi_uint64(z[0], const_ratio_1, tmp2);
    tmp3 = tmp2[1] + add_uint64(tmp2[0], mang, &tmp1);

    // Vòng 2
    multi_uint64(z[1], const_ratio_0, tmp2);
    mang = tmp2[1] + add_uint64(tmp1, tmp2[0], &tmp1);

    // Đây là tất cả những gì chúng ta quan tâm
    tmp1 = z[1] * const_ratio_1 + tmp3 + mang theo;

    // Phép trừ Barrett
    tmp3 = z[0] - tmp1 * giá_trị_mô-đun;

    // Yêu cầu: Thêm một phép trừ nữa là đủ
    get<2>(I) = SEAL_COND_SELECT(tmp3 >= modulus_value, tmp3 - modulus_value, tmp3);
});

đó là nghĩa vụ phải làm Giảm Barrett, một kỹ thuật tính toán mô-đun mà không cần chia.

Nó trông giống như multi_uint64_hw64 nhân hai số 64 bit và chỉ nhận được 64 bit quan trọng nhất. nhân_uint64 nhận được một số 128 bit trong hai số 64 bit.Tuy nhiên, tôi không hiểu những gì đang được thực hiện và quan trọng nhất là ở đâu

$$a-\left\lfloor a\,s\right\rfloor\,n$$

xảy ra. Thậm chí không có chức năng sàn trên mã này.

fgrieu avatar
lá cờ ng
Mã nhân các đầu vào `get(I)` và `get(I)` thành `z`, sau đó giảm hằng số modulo `z` `modulus_value`$=n$, với kết quả `get(I)`. $s=1/n$ trong wiki được liên kết về giảm Barrett được chia tỷ lệ thành $r=\lfloor2^{128}/n\rfloor$, được tính toán trước bên ngoài dưới dạng `const_ratio_0` và `const_ratio_1` (64-bit thấp và cao chân tay). Nếu một cái gì đó vẫn còn bí ẩn, xin vui lòng xác định chính xác nó; và tốt nhất là phiên âm những gì bạn hiểu (bao gồm cả gợi ý của tôi) trong câu hỏi, đặt cho các biến những cái tên đẹp và nhất quán với ví dụ: $r_0+2^{64}r_1=r$ cho `const_ratio`, tương tự cho `z` và `tmp2`.
lá cờ ru
@fgrieu Về cơ bản, tôi không hiểu sự phân tách thành các bit quan trọng nhất và ít quan trọng nhất. Làm thế nào phép nhân có thể hoạt động với phần trên và phần dưới? Tôi hiểu cách `const_ratio` được chia thành 2 phần nhưng không hiểu cách mọi thứ được thực hiện sau đó
Maarten Bodewes avatar
lá cờ in
Có lẽ tôi quá đơn giản ở đây, nhưng với các phép toán số nguyên, bạn không cần sàn, vì làm tròn xuống cũng giống như quên mọi thứ đằng sau dấu phẩy.
Điểm:3
lá cờ ng

Mã của câu hỏi tính toán 64-bit lấy<2>(tôi)=$h:=f\,g\bmod n$ từ đầu vào:

  • 64-bit mô đun_giá trị=$n$ với $n\in[2,\,2^{63}]$
  • 64-bit lấy<0>(I)=$f$ với $f\in[0,\,2^{64}-1]$
  • 64-bit nhận được<1>(I)=$g$ với $g\in[0,\,2^{64}-1]$$f\,g<2^{64}\,n$, một điều kiện được đáp ứng nếu $f,g\in[0,\,n-1]$ (mà tôi đoán luôn là trường hợp trong ứng dụng).

Kết quả $h$ là phần còn lại của phép chia Euclide $f\,g$ qua $n$. Nó được định nghĩa toán học bởi $0\le h<m$$\exists q\in\mathbb Z,\ f\,g=q\cdot n+h$.

Mã đầu tiên tính toán 128-bit z$=z:=f\,g$, thì kết quả lấy<2>(tôi)$=h:=z\bmod n$ qua giảm barrett:

  • Nó đã được tính toán trước (bên ngoài mã của câu hỏi) const_ratio$=r=\left\lfloor2^{128}/n\right\rfloor$
  • $\hat q:=\left\lfloor z\,r/2^{128}\right\rfloor$, đó là chính xác $q$ trong một theo mặc định (lưu ý: $\hat q$ là giá trị cuối cùng của biến tmp1).
  • $\hat h:=z-q\cdot n$, đó là chính xác $h$ trong khả năng vượt quá chính xác $n$ (Ghi chú: $\hat h$ là giá trị cuối cùng của biến tmp3).
  • $h:=\hat h-n$ khi nào $\hat h\ge n$, hoặc $h:=\hat h$ nếu không thì.

Mã này sử dụng các thuật toán của trường tiểu học để thực hiện phép tính nhiều chữ số, được chuyển đổi từ cơ số $10$ để căn cứ $2^{64}$ (với các tối ưu hóa và một biến thể được trình bày chi tiết trong phần tiếp theo). Tương đương với các chữ số được gọi là chân tay, ở đây 64-bit.

Sản phẩm z$=z$ được thể hiện như hai chi z[0]$=z_0$ (bậc thấp), z[1]$=z_1$ (bậc cao), do đó với $z=z_0+2^{64}\,z_1$, và $z_0, z_1\in[0,\,2^{64}-1]$.

Hệ số nhân Barrett const_ratio$=r=\left\lfloor2^{128}/n\right\rfloor$ được thể hiện tương tự như hai chi const_ratio_0$=r_0$const_ratio_1$=r_1$, nhờ vào mức thấp nhất của khoảng thời gian trong điều kiện tiên quyết $n\in[2,\,2^{63}]$.

Sản phẩm trung gian $z\,r$ luôn phù hợp với ba chi (mặc dù nói chung, tích của hai đại lượng được biểu thị bằng hai chi sẽ yêu cầu bốn chi).

Thương số dự kiến ​​theo mặc định $\hat q$ phù hợp với một chi duy nhất, kể từ khi $\hat q\le q$$q<2^{64}$, với điều kiện tiên quyết đầu vào được bảo hiểm mới nhất $f\,g<2^{64}\,n$.

Phần còn lại dự kiến $\hat h$ phù hợp với một chi duy nhất, kể từ khi $\hat h<2n$$2n\le2^{64}$, với phần sau nhờ vào phần cuối cao của khoảng trong điều kiện tiên quyết $n\in[2,\,2^{63}]$.


Chi tiết thuật toán của mã (như đã hỏi trong bình luận và tiền thưởng):

Tôi sẽ sử dụng một hình minh họa ở dạng thập phân. Trong cơ sở đó, kể từ khi $2\le n\le10/2$, const_ratio$=r$ chỉ có thể là $\left\lfloor100/2\right\rfloor=50$, $\left\lfloor100/3\right\rfloor=33$, $\left\lfloor100/4\right\rfloor=25$, $\left\lfloor100/5\right\rfloor=20$, nhưng tôi sẽ giả vờ const_ratio$=r=29$ bởi vì điều đó làm cho một ví dụ thú vị hơn. Vì lý do tương tự tôi sẽ sử dụng z$=z=34$, mặc dù không thể lấy tích đó dưới dạng tích của hai chữ số.

Sản phẩm z được lấy trong mã bởi multi_uint64(get<0>(I), get<1>(I), z) như hai chi z[0]z[1].

Thịt của tính toán là $\hat q:=\left\lfloor z\,r/2^{128}\right\rfloor$. Đó là tương tự trong cơ sở $2^{64}$ của $9:=\left\lfloor29\cdot34/100\right\rfloor$ trong cơ sở 10. Cả hai đối số $29$$34$ đến phép nhân có hai chữ số, nhưng đủ nhỏ để tích của chúng $986$ là ba chữ số (thay vì bốn) và chúng tôi chỉ quan tâm đến chữ số thứ ba từ bên phải. Thuật toán trường tiểu học để tính toán $986:=29\cdot34$ sẽ được trình bày như

      2 9 const_ratio
   x 3 4 z
    -----
    1 1 6
+ 8 7
  -------
    9 8 6

Trong thuật toán của trường tiểu học, có bốn phép nhân một chữ số (mà mã thực hiện) và một vài thao tác bổ sung (mã sắp xếp lại một chút):

  • 4 lần 9, 36; viết 6, giữ 3;
  • 4 lần 2, 8; thêm 3 (đã giữ), 11; viết đó.
  • 3 lần 9, 27; viết 7, giữ 2;
  • 3 lần 2, 6; thêm 2 (đã giữ), 8; viết đó.

Phép nhân đầu tiên trong bốn phép nhân này xảy ra trong đoạn mã multi_uint64_hw64(z[0], const_ratio_0, &carry), nhân với chi bậc thấp của $r$ với chi bậc thấp của $z$, giống như chúng ta nhân chữ số bậc thấp 4 của 34 với chữ số bậc thấp 9 của 29. Lưu ý rằng "viết 6" là vô nghĩa trong trường hợp này, vì bất kỳ chữ số nào nó viết sẽ được tách biệt sang cột bên phải của phép tính mà không có bất kỳ cơ hội nào để tác động đến chữ số ngoài cùng bên trái và bị bỏ qua khi chúng ta chia cho 100 và làm tròn xuống (tương đương, chỉ giữ lại chữ số thứ ba từ đúng). Đó là lý do tại sao 64-bit bậc thấp của sản phẩm 128-bit thậm chí không được tính toán, như đã lưu ý trong câu hỏi. Tương đương với 3 Trong 36 được giữ trong mang.

Phép nhân thứ hai xảy ra trong multi_uint64(z[0], const_ratio_1, tmp2), nhân với chi bậc cao của $r$ với chi bậc thấp của $z$, với kết quả là hai nhánh của tmp2; 64-bit tmp[0] nhận tương đương với 8 Trong 8, và tmp[1] nhận tương đương với 0 (chú ý một hàng đầu 0 bị loại bỏ trong cách viết thông thường của số nguyên thập phân). tương đương với 8 cộng 3 xảy ra trong add_uint64(tmp2[0], mang theo, &tmp1), với chữ số bậc thấp 1 của kết quả 11 Trong tmp1, và carry mới 1 trong đầu ra của chức năng đó. Điều đó được sử dụng như toán hạng bên phải trong tmp3 = tmp2[1] + ⦠(điều này tình cờ bị bỏ qua trong thuật toán của trường tiểu học với ví dụ cụ thể mà tôi đã lấy từ 0 đã bị triệt tiêu), mang lại kết quả tương đương với bên trái 1 Trong 116. [Lưu ý về đầu ra của add_uint64: nó được tạo ra bởi static_cast<unsigned char>(*result < toán hạng1), so sánh *kết quảtoán hạng, sau đó quay thật đến 1, sai đến 0. thực hiện sau *kết quả = toán hạng1 + toán hạng2, điều đó cho biết liệu phần bổ sung này có tạo ra một lần thực hiện hay không. Một số trình biên dịch nhận ra thành ngữ này, sử dụng bit C của từ trạng thái và sử dụng lại C trong phần bổ sung sắp tới].

Phép nhân thứ ba xảy ra trong multi_uint64(z[1], const_ratio_0, tmp2), nhân với chi bậc thấp của $r$ với chi bậc cao của $z$, dẫn đến các chi trong tmp2, như chúng tôi làm 3 x 9 = 27. Lần này chúng ta cần cả hai chi/chữ số: tương đương với 7 đi tới tmp2[0] và tương đương với 2 đi tới tmp2[1]. Đây là một biến thể của thuật toán trường tiểu học: nó ngay lập tức được thêm vào tmp1 (tương đương giữa 1 Trong 116) đến chi bậc thấp với add_uint64(tmp1, tmp2[0], &tmp1), thực hiện tương đương với 1 + 7 = 8, không mang. Kết quả 8 được lưu trữ trong tmp1 bởi vì ngữ nghĩa của add_uint64 cần một điểm đến, nhưng nó thực sự bị bỏ qua, bởi vì chúng tôi không quan tâm đến chữ số ở giữa trong 986. Sản lượng thực hiện bởi add_uint64 được sử dụng như toán hạng bên phải trong mang = tmp2[1] + â¦, thực hiện tương đương với 1 + 0 = 1 trong ví dụ của chúng tôi. Mặc dù tên mang, chứa chi/chữ số 64 bit đầy đủ.

Phép nhân thứ tư xảy ra trong z[1] * const_ratio_1, nhân với chi bậc cao của $r$ với chi bậc cao của $z$, như chúng tôi làm 3 x 2 = 6. Ở đây ngữ cảnh đảm bảo kết quả phù hợp với một chi duy nhất, do đó có thể sử dụng toán tử C riêng cho phép nhân. Kết quả sau đó được sử dụng làm toán tử bên trái của ⦠+ tmp3 + mang theo, thực hiện tương đương với 6 + 1 + 1 = 8. Một lần nữa, bối cảnh đảm bảo các giá trị này $\hat q$, được lưu trữ trong tmp1, khớp với một nhánh/chữ số.

sau đó tmp3 = z[0] - tmp1 * modulus_value thực hiện $\hat h:=z-q\cdot n$. Bối cảnh đảm bảo kết quả chính xác về mặt toán học phù hợp với một nhánh/chữ số (được lưu trữ trong tmp3) mặc dù $q\cdot n$ không làm. Điều này cho phép sử dụng các toán tử C gốc, loại bỏ hoàn toàn việc tính toán chi bậc cao.

sau đó SEAL_COND_SELECT(tmp3 >= modulus_value, tmp3 - modulus_value, tmp3) tính toán $h$ từ $\hat h$ trừ có điều kiện $n$ khi nào $\hat h\ge n$. Toán tử lựa chọn bị ẩn trong macro.

Hai ví dụ cho cơ sở $2^{64}$ (các giá trị ở dạng thập lục phân lớn có khoảng trắng được chèn giữa các chi):

mô-đun_value 000076513ae0b1cd
const_ratio 00000000000229e6 7f4ca82ba3a115f1
lấy<0>(I) 00005f0fd669f2c7
nhận được<1>(I) 000041a1f91ef16f
z 00000000185f2ae8 a455846cb7cf9b49
tmp1 000034bb854f9a8d
tmp3 00000fcebfd55b60
nhận được<2>(I) 00000fcebfd55b60

mô-đun_value 686f4b7702a9c775
const_ratio 0000000000000002 7387d66ffd685b82
lấy<0>(I) 536094611fa2b19b
nhận được<1>(I) 675ef5187093ff63
z 21aac8fcf31d6421 62e675ba16d513f1
tmp1 5287278703394bb1
tmp3 72b1d3d2b9f5e50c
nhận được<2>(I) 0a42885bb74c1d97

Lưu ý: đối với $n\in[2^{63}+1,\,2^{64}-1]$, số lượng $\hat h$ có thể tràn một chi và mã khi nó đứng không thành công. Ví dụ. cho đầu vào $f=g=2^{32}$, chúng tôi nhận được $z=2^{64}$ do đó $\hat q=0$ (bất cứ gì $n>2^{63}$), do đó $\hat h=z=2^{64}$ và một đầu ra của $0$ hơn là sự thật $h=2^{64}-n$. Nguồn đầy đủ có nhận xét "lớp Mô-đun đại diện cho mô-đun số nguyên không âm lên tới 61 bit", do đó, các vấn đề như vậy đối với quy mô lớn $n$ chỉ xảy ra khi mã gọi bị lỗi. Ngoài ra, nếu tôi hiểu đúng, $n=2^{60}-2^{14}+1$ là mục tiêu chính.


Thay thế: Đối với nội dung nào đó thực hiện chức năng giống như mã của câu hỏi, trong 4 dòng mã ngắn thay vì 11, cho tất cả $n\in[1,\,2^{64}-1]$, không cần tính toán trước, có thể nhanh hơn, nhưng chỉ tương thích với các trình biên dịch + CPU x64 gần đây, hãy xem phần đầu tiên của những đoạn mã này (thứ hai là một biến thể nhỏ không hạn chế $f\,g<2^{64}\,n$ ). Tôi không đưa ra tuyên bố nào về tính kịp thời của một trong hai mã.

lá cờ ru
Tại sao `add_uint64` luôn trả về $0$ hoặc $1$? Không có khả năng thực hiện lớn hơn?
fgrieu avatar
lá cờ ng
@Guerlando OC: `add_uint64` thêm hai nhánh được truyền dưới dạng đối số thứ nhất và thứ hai, do đó hai đại lượng trong $[0,\ 2^{64}-1]$. Do đó, kết quả chính xác về mặt toán học là $[0,\ 2^{65}-2]$, do đó phù hợp với một nhánh 64 bit và một bit. Điều đó tương tự như tổng của hai chữ số thập phân khớp với một chữ số và mang 0 (ví dụ: 4+5=9) hoặc 1 (ví dụ: 9+9=18).

Đăng câu trả lời

Hầu hết mọi người không hiểu rằng việc đặt nhiều câu hỏi sẽ mở ra cơ hội học hỏi và cải thiện mối quan hệ giữa các cá nhân. Ví dụ, trong các nghiên cứu của Alison, mặc dù mọi người có thể nhớ chính xác có bao nhiêu câu hỏi đã được đặt ra trong các cuộc trò chuyện của họ, nhưng họ không trực giác nhận ra mối liên hệ giữa câu hỏi và sự yêu thích. Qua bốn nghiên cứu, trong đó những người tham gia tự tham gia vào các cuộc trò chuyện hoặc đọc bản ghi lại các cuộc trò chuyện của người khác, mọi người có xu hướng không nhận ra rằng việc đặt câu hỏi sẽ ảnh hưởng—hoặc đã ảnh hưởng—mức độ thân thiện giữa những người đối thoại.