CppCon 2017 學習:CNL: A Compositional Numeric Library

你說的這段關于浮點數的問題總結得很精準,我幫你整理一下,讓理解更清晰:

The Problem with Floating-Point(浮點數的問題)

  1. 復雜的表示結構
    浮點數由符號位 ±,有效數(significand/mantissa),和指數部分組成:
    ± 1. significand × 2 exponent \pm 1.\text{significand} \times 2^{\text{exponent}} ±1.significand×2exponent
    還存在一些特殊情況:
    • 特殊值(NaN、±∞)
    • 非規格化數(denormalized numbers)
    • 負零(-0)等
  2. 偶爾出現的怪異行為,容易出乎意料
    浮點運算并不總滿足常見的代數規律:
    • 不保證確定性(determinism)
    • 結合律(associativity)可能不成立
    • 交換律(commutativity)可能不成立
    • 甚至順序比較(ordering)有時也不完全直觀
  3. 數學函數(庫)缺乏constexpr支持
    標準庫里的數學函數(比如 sin, cos, sqrt)直到C++20才部分支持constexpr,
    這限制了編譯時計算和優化。
  4. 分辨率是可變的
    浮點數表示的精度不是均勻的,靠近0時精度更高,遠離0時精度降低,導致誤差不可控。
  5. 硬件成本高,能耗大
    浮點運算相較于整數運算需要更多的硅片資源和功耗,對嵌入式或移動設備是挑戰。

總結

浮點數雖然強大,但它的內部復雜結構、數學上的不完美性質以及硬件實現成本,導致在軟件設計和數值計算時需要格外小心。理解這些限制可以避免程序中的數值錯誤和性能問題。

這段內容講的是對整數處理的改進目標,以及CNL(Composable Numeric Library,假設是指類似庫)的設計理念,我幫你總結和理解一下:

Analysis(分析)

  • 浮點數的問題其實沒那么糟糕
    盡管有復雜和限制,但浮點數作為抽象還是有價值的。
  • 整數是一種對寄存器的強大抽象
    整數類型直接映射底層硬件寄存器,性能和效率都很好。
  • 但我們還可以做得更好
    傳統整數雖然簡單高效,但缺少現代C++容器和算法的靈活性和安全性。

Goal of CNL(CNL的目標)

“Do for int what the STL did for []”

(對整數做的事情,類似于STL對數組做的事情)

  • 提供零開銷抽象(zero-cost abstractions),即在不影響性能的前提下,增強語言級功能:
    比如用std::array<T, N>代替傳統的T a[N]數組,得到更好的接口和安全性。
    • std::array<T, N>::iterator i = std::begin(a); 實際上就是T* i,保持底層效率。
  • 保持熟悉的接口
    • auto const& third = a[2]; 仍然像訪問普通數組一樣簡單。
    • 支持范圍for循環,for (auto const& element : a) { ... },方便且語義清晰。
  • 允許用戶選擇啟用付出代價的功能
    • 比如使用a.at(n+1),如果越界則拋異常,比直接用operator[]更安全。

重點

讓整數類型(int)也像STL容器那樣靈活、可組合,支持更豐富的操作和接口。

組合示例

using fs_cache = std::unordered_map<std::filesystem::path, std::vector<byte>>;
  • 用標準庫容器組合出更復雜的數據結構,體現“組合”理念。

總結

CNL的愿景就是給整數類型帶來像STL容器那樣的抽象和便利,同時保持零開銷的效率,讓整數的使用更加安全、靈活和現代化。

Non-Goal (非目標)

不要對整數做STL數組沒做的事情。

  • 也就是說,設計時不要讓用戶為他們不需要的功能付出代價
  • CNL追求“零開銷”原則,避免增加用戶不想用的復雜性或性能負擔。
  • 這是對前面“為整數做STL那樣抽象”的一種限制,防止過度設計。

Fixed-Point Arithmetic(定點數算術)

定義示例代碼:
// cnl/fixed_point.h
namespace cnl {template<typename Rep = int, int Exponent = 0>class fixed_point {// ...private:Rep r;};
}
  • fixed_point 是CNL庫中定義的一個模板類,用來表示定點數
  • Rep是存儲實際數值的底層類型(默認是int)。
  • Exponent是定點數的小數點位置的指數(例如-8表示小數點左移8位,即小數部分精度為2^-8)。

Example usage(示例用法)

using cnl::fixed_point;
void f() {auto n = fixed_point<int, -8>{ 0.25 };std::cout << n * 5; // prints "1.25"
}
  • 創建了一個定點數n,底層類型是int,小數點指數是-8,表示精度是1/256。
  • 0.25被轉換成內部整數存儲。
  • n * 5進行了定點數乘法運算,輸出結果是1.25(定點數乘法正確處理了小數部分)。

你的理解要點

  • CNL追求零開銷且按需提供功能,避免不必要的負擔
  • 定點數(fixed_point)是整數的高級抽象,支持小數但底層用整數實現
  • 通過模板參數Exponent可以靈活控制小數點位置和精度。
  • 定點數讓你可以用整數硬件做浮點數那樣的計算,通常更高效且節能。

這幾頁的內容在強調使用 CNL 的 fixed_point 帶來的“好處”(The Good),特別是它在數值比較和操作中的表現。下面幫你詳細解釋:

代碼示例(連續幾頁相似代碼)

bool foo(float f) {auto fixed = fixed_point<int, -16>{f};auto fixed_plus_one = fixed + 1;return fixed_plus_one > fixed;
}
bool foo(float) {return true;
}

重點解讀

  1. fixed_point的構造與運算
    • fixed_point<int, -16>{f} 把浮點數 f 轉換為定點數,底層用 int 表示,小數點位置由 -16 指定(即精度約為 2^-16)。
    • fixed + 1 這里的 1 是定點數中整數的“1”,表示加了 1 個單位(即相當于加了 2^-16 * 65536 = 1.0)。
    • 這種操作是類型安全且語義明確的。加法和比較都是定點數之間的操作。
  2. 比較操作的正確性
    • fixed_plus_one > fixed 這樣的比較在浮點數里可能因為舍入誤差、精度問題導致不準確。
    • 用定點數則會變得更確定、更一致,符合數學預期。
  3. 重載版本
    • 你還看到第二個 bool foo(float) 版本直接返回 true,這可能是為了說明某種對比或者重載示例,但重點還是在上面的定點數版本。

“The Good”的含義

  • 使用 CNL 的 fixed_point,你能獲得更可預測、確定的數值運算結果
  • 避免了浮點數運算中常見的誤差和不確定性問題。
  • 同時,定點數運算通常更高效,也更節能,適合嵌入式、實時系統等場景。

你需要注意的

  • 這里強調fixed_point的優勢是數值行為更穩定,且支持像整數一樣的運算。
  • 代碼簡潔,易讀,符合日常編程習慣。
  • 適用于對數值確定性要求高的應用。

這段代碼是 CNL(Compositional Numeric Library)中 定點數(fixed-point) 類型的一個簡化定義和用法示例,幫你逐步拆解理解:

1. fixed_point 類模板結構

namespace cnl {template<typename Rep = int, int Exponent = 0>class fixed_point {private:Rep r;  // 底層存儲整數};
}
  • Rep 是底層整數類型,默認是 int。這個整數用來存儲定點數的“原始數據”。
  • Exponent 是一個整數,表示小數點相對于整數的偏移量(用2的冪表示)。
  • 通過這個設計,fixed_point 把一個整數r和一個指數Exponent結合起來,來表示實數。

2. 定點數的基本思想

定點數就是用整數來表示帶小數的數,但小數點的位置是固定的,不象浮點數那樣有指數部分。它的值是:
value = r × 2 Exponent \text{value} = r \times 2^{\text{Exponent}} value=r×2Exponent

  • 例如,Exponent = -8 表示小數點往右移8位(相當于除以 2 8 = 256 2^8 = 256 28=256)。
  • 這時,r 存儲的是實際值乘以 256 后的整數。

3. 例子說明

auto n = fixed_point<int, -8>{ 0.25 };
std::cout << n * 5; // prints "1.25"
  • fixed_point<int, -8> 表示這個定點數用32位整數(int)存儲,且小數點位于整數的右邊8位(即精度為 1/256)。
  • 當用 0.25 初始化時,庫內部會把它轉成整數:
    r = 0.25 × 2 8 = 0.25 × 256 = 64 r = 0.25 \times 2^{8} = 0.25 \times 256 = 64 r=0.25×28=0.25×256=64
    所以內部存儲的 r 是64。
  • 當執行 n * 5 時:
    n ? 5 = ( 64 × 2 ? 8 ) × 5 = 64 × 5 × 2 ? 8 = 320 × 2 ? 8 = 320 256 = 1.25 n * 5 = (64 \times 2^{-8}) \times 5 = 64 \times 5 \times 2^{-8} = 320 \times 2^{-8} = \frac{320}{256} = 1.25 n?5=(64×2?8)×5=64×5×2?8=320×2?8=256320?=1.25
  • 打印結果是 1.25

4. 優點

  • 使用定點數能比浮點數更高效、確定性更強(特別是在嵌入式和硬件設計中)。
  • 能精確控制精度,不會出現浮點數的舍入誤差某些問題。
  • 但編程時需要處理定點數的加減乘除的實現細節,CNL 就是用模板封裝好了這些操作。

總結

  • fixed_point 用一個整數和一個指數參數來表示小數。
  • 通過 Exponent 控制小數點位置,實現固定的小數精度。
  • 初始化時會自動把浮點數轉為定點格式整數。
  • 運算時會考慮指數位移,實現正確的定點數數學。
  • 例子中 fixed_point<int, -8>{0.25} 存儲的內部整數是64,乘以5后輸出1.25。

這段代碼展示了使用 fixed_point 類型的一個「好的用法」示例,核心是展示 定點數的基本運算和比較,以及函數重載。具體解釋如下:

代碼結構

bool foo(float f) {auto fixed = fixed_point<int, -16>{f};       // 把 float 轉成 fixed_pointauto fixed_plus_one = fixed + 1;             // fixed_point + int 的加法運算return fixed_plus_one > fixed;                // 比較大小
}
bool foo(float) {return true;
}

逐步理解

1. auto fixed = fixed_point<int, -16>{f};

  • 這里將 float 轉換成了 fixed_point<int, -16> 類型。
  • -16 表示小數點向右移動16位,也就是精度是 2 ? 16 = 1 65536 2^{-16} = \frac{1}{65536} 2?16=655361?
  • 內部整數 r 存儲的是 f * 65536 的整數近似值。

2. auto fixed_plus_one = fixed + 1;

  • 這其實是用定點數和整數做加法。
  • 定點數加整數時,整數 1 需要被隱式轉換成 fixed_point<int, -16>{1},即內部整數存儲的是 1 × 2 16 = 65536 1 \times 2^{16} = 65536 1×216=65536
  • 加法后 fixed_plus_one 是一個新的 fixed_point 對象。

3. return fixed_plus_one > fixed;

  • 定點數支持比較操作。
  • 這個判斷會返回 true,因為 fixed_plus_onefixed 加上 1(在定點數的單位中),肯定大。

4. 重載的第二個函數

bool foo(float) {return true;
}
  • 這是第二個同名函數,參數是 float,無參數名,直接返回 true
  • 這個重載的存在意義取決于上下文,可能是為了對比或者覆蓋。

為什么這是「The Good」(好的用法)?

  • 使用了定點數,避免了浮點數的潛在不確定性。
  • 定點數可以進行算術和比較,接口和原生類型很接近,使用方便。
  • 通過重載,可以根據需要擴展不同的實現。

總結

  • fixed_point<int, -16> 是帶 16 位小數的定點數,實現了加法和比較。
  • 這個函數用定點數做了加法和比較,語義清晰,符合直覺。
  • 通過定點數,可以提高數值運算的確定性和精度控制。

這段代碼的重點是展示使用定點數(fixed_point)和普通整數類型時,可能會遇到的“坑”和不符合直覺的比較行為,特別是涉及有符號和無符號類型的比較以及編譯期斷言(static_assert)的失敗

代碼片段及解釋

static_assert(fixed_point<unsigned>{1} < fixed_point<signed>{-1}, "OK(!)");
static_assert(numeric_limits<int>::max() + 1, "error");
static_assert(unsigned{1} < signed{-1}, "evaluates to true");
static_assert(fixed_point{1u} < fixed_point{-1});

1. static_assert(fixed_point<unsigned>{1} < fixed_point<signed>{-1}, "OK(!)");

  • fixed_point<unsigned>{1} 是用無符號類型包裝的定點數,表示正數1。
  • fixed_point<signed>{-1} 是用有符號類型包裝的定點數,表示負數-1。
  • 按理說,1 不應該小于 -1,但這里斷言說成立("OK(!)"中感嘆號暗示這是“不正常”的結果)。
  • 這是因為不同符號類型的比較導致整數提升和類型轉換時產生了意料之外的結果。
  • 編譯器在模板比較時的規則可能把負數轉換成了無符號類型,變成了很大的正數,導致比較結果錯誤。

2. static_assert(numeric_limits<int>::max() + 1, "error");

  • numeric_limits<int>::max()int 能表示的最大正整數。
  • 加1 會導致整數溢出(在有符號整數中是未定義行為)。
  • 斷言判斷的條件會變成非零(或其他值),這里用作演示“這會導致編譯錯誤或未定義行為”。
  • 因為溢出,斷言不成立或導致錯誤。

3. static_assert(unsigned{1} < signed{-1}, "evaluates to true");

  • 比較無符號 1u 和有符號 -1 時,-1 會被轉換為無符號數。
  • 在無符號表示中,-1 變成了一個很大的數(通常是 UINT_MAX),
  • 因此,比較結果是 true,即 1u < (unsigned)-1,符合C++的整數提升規則,但這很容易讓人迷惑。
  • 這是C++中最經典的有符號和無符號混合比較的陷阱

4. static_assert(fixed_point{1u} < fixed_point{-1});

  • 類似第1點,使用默認模板參數(Rep推斷為unsignedint)的fixed_point進行比較。
  • 因為 1u 是無符號,-1 是有符號,比較可能涉及類型轉換,導致斷言成立或失敗不符合預期。

總結:

  • 整數有符號與無符號混合比較時要格外小心,因為會發生隱式類型轉換,導致錯誤的結果。
  • fixed_point 這種包裝類型也會繼承這些基本類型的陷阱。
  • static_assert 在這里用來做編譯時斷言,暴露出類型轉換帶來的問題。
  • 這就是所謂的 “The Bad”,提醒我們定點數庫和使用者都要注意這些邊緣行為。

這段代碼和注釋,核心是在講 fixed_point<int, -8> 類型的乘法結果類型,以及 C++ 的模板類型推斷。讓我幫你拆解理解:

auto n = fixed_point<int, -8>{1.5};
auto nn = n * n;    // fixed_point<int, -16>;
static_assert(std::is_same_v<decltype(nn), fixed_point<int, -16>>);

背景:fixed_point 類型

fixed_point<int, -8> 表示一個定點數,用一個 int 存儲,且小數部分占 8 位(即小數點左移 8 位),也就是小數精度是 2^(-8)。

  • 例如,fixed_point<int, -8> 的底層數值為整數,但實際值是 value * 2^{-8}

關鍵點:乘法后的類型變化

  • nfixed_point<int, -8>
  • 計算 nn = n * n,兩個定點數相乘。
    如果兩個定點數都是小數點左移 8 位,即 scale 是 2 ? 8 2^{-8} 2?8,那么相乘結果小數點位置會發生變化。
    為什么是 fixed_point<int, -16>
  • 乘法后,數值的 scale 應該是乘法 scale 的相乘: 2 ? 8 ? 2 ? 8 = 2 ? 16 2^{-8} * 2^{-8} = 2^{-16} 2?8?2?8=2?16
  • 也就是說,結果的定點數精度是小數點左移 16 位。
  • 因此,乘法的結果類型是 fixed_point<int, -16>

static_assert 作用

static_assert(std::is_same_v<decltype(nn), fixed_point<int, -16>>);

這句斷言保證編譯期檢查,nn 的類型必須是 fixed_point<int, -16>,否則編譯失敗。

總結

  • 你用的是一個帶有模板參數來控制定點精度的 fixed_point 類型
  • 乘法時,精度參數會相加(這里 -8 + -8 = -16),確保乘法結果精度正確
  • 斷言保證這個規則在代碼中得到體現

這段代碼涉及定點數(fixed_point)的除法操作,以及除法結果的精度和底層類型的變化。你貼出的兩個不同寫法的結果類型明顯不同,這背后的原因很值得深入理解。

代碼回顧

constexpr auto n = fixed_point<int, -8>{1.5};
constexpr auto d = fixed_point<int, -8>{2.25};
constexpr auto q = n / d;    
// q 的類型是 fixed_point<int, 0>;
constexpr auto q2 = cnl::divide(n, d);
// q2 的類型是 fixed_point<long, -31>;

重點:為什么 n / dcnl::divide(n, d) 的類型不同?

1. n / d 直接用運算符除法

  • nd 都是 fixed_point<int, -8>
  • 定點數除法通常會按下面思路做:

定點數除法大致相當于:

$$

\frac{n}{d} = \frac{n_{\text{raw}} \times 2^{scale}}{d_{\text{raw}}}

$$

其中 n_rawd_raw 是底層整數值,scale 是小數點位移。

  • 但如果實現不夠復雜,默認的除法操作符很可能:
    • 返回一個整數定點數(scale = 0),
    • 或者把結果強制縮放為一個無小數部分的類型,精度丟失了。
      所以,q 結果的類型變成了 fixed_point<int, 0>,意味著結果變成了“整數定點數”,丟失了小數精度。

2. cnl::divide(n, d) 使用的是 CNL 庫專門的除法函數

  • CNL(Compositional Numeric Library)是一個支持定點數精度推導的庫
  • 它在除法操作中通常會根據輸入的精度和底層類型推斷出更合適的結果類型
  • 這里得到的結果是 fixed_point<long, -31>
    • 底層用 long 類型存儲(可能是 64 位)
    • 精度是小數點左移 31 位,意味著結果有非常高的精度
    • 這是因為 divide 函數通過擴展底層類型寬度,避免了精度丟失和溢出

原理小結

操作結果類型說明
n / dfixed_point<int, 0>精度丟失,結果變為整數定點數
cnl::divide(n, d)fixed_point<long, -31>精度提升,使用更寬類型和更高小數位數
cnl::divide 能正確推斷并保持高精度,因為它:
  • 使用更寬的底層整數類型來避免溢出(long
  • 調整了小數位數(-31),以便結果保留小數信息

額外說明:定點數除法的難點

  • 定點數除法需要避免除法后精度大量丟失
  • 一般思路是先提升精度(擴大分子)再除
  • 否則會出現四舍五入誤差或直接舍棄小數部分
  • 標準 / 運算符為了簡潔可能沒有做到這一點

以及如何解決“除法”問題,尤其是在固定點數(fixed-point)和分數(fraction)表示法中的處理方式。

你給的內容總結:

  1. 乘法例子:
    5.5 * 5.5 = 30.25
    55. * 0.55 = 30.25
    
    兩種寫法表示的數不同,但結果一樣。這說明了小數和整數乘以縮放因子的關系。
  2. 除法例子:
    1 / 100 = 0.01
    10 / 5.5 = 1.818181818181...
    
    除法產生的是小數或無限循環小數。
  3. 分數模板類(C++):
    template<typename Integer>
    class fraction {Integer numerator, denominator;// ...
    };
    
    這是用整數表示分數,避免浮點數計算中的精度誤差。
  4. 小數的乘法和除法“規律”表達式:
    AAA.BBBBB * CCCCCC.DD = AAACCCCCC.BBBBBDD
    AAA.BBBBB / CCCCCC.DD = AAADD.BBBBBCCCCCC
    
    這是假設的表達式,描述了乘除時小數點移動和數字組合的變化。
  5. 固定點數除法(用CNL庫):
    constexpr auto n = fixed_point<int, -8>{1.5};
    constexpr auto d = fixed_point<int, -8>{2.25};
    constexpr auto q = cnl::divide(n, d);  // fixed_point<long, -31>; 理解
    
    這是用固定點數做除法,結果的精度比輸入更高(小數位更多),避免精度損失。

重點:如何解決“除法”問題?

1. 精度問題

除法經常會產生無限小數(比如1/3),所以單純用固定小數位數存儲會導致精度丟失或四舍五入誤差。

2. 解決辦法:
  • 用分數類(fraction)表示:
    分數用兩個整數(分子和分母)表示,保證精確,無限小數問題通過保持分母不變來避免精度損失。
    需要實現加減乘除的規則。
  • 用更高精度的固定點數類型:
    在除法時,結果通常需要比輸入更高的小數位數(更多的fractional bits),例如你的例子中從-8位擴展到了-31位。這樣保證結果更精確。
  • 縮放法(整數代替小數):
    先將數放大一定倍數轉為整數再計算,最后再縮小。例如:
    5.5 / 2.25 
    = (550 / 225)  // 乘以100
    
    這樣用整數運算減少浮點誤差。
  • 接受近似結果(浮點數或固定點數四舍五入):
    如果不需要極端精度,可以用浮點數除法,或者固定點數除法后四舍五入。

簡單總結

  • 固定點數除法需要管理好小數位,避免截斷。
  • 分數表示法保證除法精確,但復雜度高。
  • 用庫(如CNL)可以自動幫你提升精度,簡化計算。

“Elasticity”(彈性)和你給出的固定點數(fixed_point)乘法例子,我來幫你分析一下它的本質和“理解”這個關鍵點。

你的代碼片段和問題核心:

auto n = fixed_point<uint8_t, -8>{0.99609375};
auto nn = n * n;  // fixed_point<int, -16>{0.9922027587890625};
auto n = fixed_point<int, -31>{0.99609375};
auto nn = n * n;  // fixed_point<int, -62>{?!?!?!?!?!?!};  理解

重點理解:

  1. fixed_point<T, Exponent>
    這里的模板參數,T 是底層整數類型,Exponent 是小數點位置的偏移(負數表示小數位)。
  • fixed_point<uint8_t, -8> 表示:用 8 位無符號整數,帶 8 位小數位(相當于值范圍在0~1之間,精度約為1/256)。
  • fixed_point<int, -31> 表示:用 32 位有符號整數,帶31位小數位,精度非常高。
  1. 乘法為什么小數位數變成了兩倍?
    乘兩個固定點數時,實際運算是乘整數部分,指數會疊加(因為兩數乘積小數位數是兩數小數位數的和)。
  • 例如,fixed_point<uint8_t, -8> * fixed_point<uint8_t, -8> 結果變成 fixed_point<int, -16>,小數位變成了16位(-8 + -8 = -16)。
  • 乘法后,數據類型往往也需要升級(比如從8位整數提升到16位整數)以防溢出。
  1. “Elasticity” 的意思
    就是結果的類型“彈性”地根據操作自動調整:
  • 位寬擴大(uint8_t變int)
  • 指數(小數位數)相加,保證精度不丟失
  1. 第二個例子為什么 fixed_point<int, -62> 出現問題?
    fixed_point<int, -31> * fixed_point<int, -31>理論上指數是-62,這樣小數位數翻倍了,但是:
  • 這里類型還是int(32位有符號整數),但是指數是-62,意味著數值被放大了 2^62倍(為了表達小數點位置),而int只能表示 32 位整數,遠遠不夠,溢出問題必然出現。
  • 這就是為什么結果類型一般不會單純是fixed_point<int, -62>,因為32位整數不足以存儲這么大范圍的值。
  • 實際上,CNL庫等固定點庫會自動把結果類型提升到更寬的整數類型(比如64位或128位整數),以容納更高精度和更大范圍。

結論和理解

  • 乘法時,小數位數是指數的和(-8 + -8 = -16,-31 + -31 = -62)。
  • 為了避免溢出,底層整數類型必須“彈性”地擴大(比如從8位擴到16位,32位擴到64位)。
  • 如果類型沒有自動提升(比如仍然是int),會導致結果溢出或錯誤。
  • 這就是“Elasticity” — 類型和精度根據運算自動伸縮。

舉個更完整的例子

using fixed8 = fixed_point<uint8_t, -8>;  // 8位小數,8位整數
using fixed16 = fixed_point<uint16_t, -16>; // 16位小數,16位整數
using fixed31 = fixed_point<int32_t, -31>;  // 31位小數,32位整數
using fixed62 = fixed_point<int64_t, -62>;  // 62位小數,64位整數
auto n8 = fixed8{0.99609375};
auto nn8 = n8 * n8;  // 類型是fixed16,正確,值在0.99左右
auto n31 = fixed31{0.99609375};
auto nn31 = n31 * n31; // 類型應該是fixed62(用int64_t作為底層類型)

你提供的代碼示例以及注釋,核心就在于 CNL 庫中的“彈性整數(elastic_integer)”和“彈性固定點數(elastic_fixed_point)”的設計理念和實現機制。下面我幫你詳細分析并“理解”這個設計。

1. 什么是 elastic_integer?

template<int Digits, class Narrowest = int>
class elastic_integer {WideEnoughInteger r; // 用于存儲足夠寬的整數類型/* other stuff */
};
  • Digits 表示需要多少二進制位的精度(有效位數),例如31位、62位等。
  • Narrowest 表示最窄的底層整數類型(默認是 int)。
  • WideEnoughInteger 是根據 DigitsNarrowest 計算出來的實際底層整數類型,能保證至少 Digits 位。
    — 例如,當你需要31位,CNL會選擇至少能存31位的整數類型(如int32_t);當需要62位時,會自動升級成64位整數。

2. 彈性整數實例說明:

auto e = elastic_integer<31>{0x7FFFFFFF};  // 最大31位數(0x7FFFFFFF是31位的最大值)
auto ee = e * e;                           // elastic_integer<62>{...},乘法后位數翻倍
auto _2ee = ee + ee;                      // elastic_integer<63>{...},加法后位數增加1
  • e 是31位寬的彈性整數,最大值約是 2^31-1。
  • e * e 乘法結果最多會是 2^(31*2)-1,所以彈性整數自動變成了62位。
  • ee + ee 是在62位基礎上再進位,變成了63位。
  • 這體現了“彈性”的核心:運算后,類型根據需要自動調整位寬以防溢出

3. 彈性固定點數:

auto fpe = fixed_point<elastic_integer<31>, -31>{0.99609375};
auto sq = fpe * fpe;  // fixed_point<elastic_integer<62>, -62>{0.9922027587890625}
  • fixed_point<elastic_integer<31>, -31> 表示:
    • 底層整數是31位寬的彈性整數。
    • 小數位是31位(指數是-31)。
  • fpe * fpe
    • 乘法時,整數位寬從31自動變為62(彈性整數自動擴展)。
    • 小數指數從-31變為-62(乘法導致小數位數相加)。
    • 這樣結果保留了更高精度,防止數據溢出或精度丟失。

4. 除法示例:

auto q = sq / sq; // fixed_point<elastic_integer<124>, -62>{1}
  • sq 是帶有 62 位整數位寬和 -62 小數位的固定點數。
  • 除法時,結果的整數位寬從62 * 2 = 124,指數不變(-62)。
  • 這樣確保了除法結果的精度和安全性。

5. “理解”總結

  • 彈性整數(elastic_integer)是CNL庫提供的一種動態調整整數位寬的模板類,自動根據運算結果調整位數,避免溢出。
  • 彈性固定點數(elastic_fixed_point)是基于彈性整數實現的固定點數類型,乘法和除法時位寬和指數自動調整,確保高精度計算。
  • 這種設計讓固定點數運算既有高精度,又避免了傳統固定點數計算中常見的溢出和精度丟失問題。
  • CNL庫通過模板元編程在編譯期完成這些類型推導和位寬調整,效率高且安全。

CNL庫中 safe_integer 類型的“運行時安全”與“constexpr 限制”的沖突問題,我幫你詳細解釋一下。

1. safe_integer 是什么?

#include <cnl/safe_integer.h>
using cnl::safe_integer;
auto i = safe_integer<uint8_t>{255};
auto j = i + 1; // safe_integer<int>{256}
  • safe_integer 是 CNL 里的帶溢出檢查的整數類型。
  • 你初始化 i255uint8_t 的最大值)。
  • i + 1 理論上是 256,超出了 uint8_t 范圍,CNL 會自動升級類型到更寬的整數(比如int),保證結果安全。
  • j 是安全的,結果是 safe_integer<int>{256}

2. 溢出時拋異常

safe_integer<uint8_t> k = i + 1;  // throw std::overflow_error
  • 你試圖把超出范圍的結果賦給 safe_integer<uint8_t>,這時發生溢出,safe_integer拋出std::overflow_error異常
  • 這保證了運行時安全,不允許溢出。

3. constexpr 限制導致編譯錯誤

constexpr safe_integer<uint8_t> k = i + 1;

編譯錯誤:

fatal error: constexpr variable 'k' must be initialized by a constant expression
note: subexpression not valid in a constant expressionreturn condition ? value : throw std::overflow_error("");
  • 這是因為 safe_integer 的實現里,溢出時是通過**throw std::overflow_error 拋異常**實現的。
  • C++標準要求 constexpr 上下文里不能拋異常,拋異常不被允許做為常量表達式。
  • 因此,當表達式計算出溢出必須拋異常時,不能用 constexpr 初始化,編譯失敗。

4. 為什么 static_assert 沒問題?

static_assert(cnl::_impl::identical(safe_integer<int>{256}, k));
  • 這個可能是在不觸發異常或者比較的是不帶溢出檢查的版本或者已經升級的類型,編譯時能正常執行比較。
  • 但前提是不要拋異常,否則編譯器也會報錯。

5. 總結和理解

  • safe_integer 通過運行時拋異常保障安全,但異常不允許在 constexpr 里拋出
  • 所以當你寫:
    constexpr safe_integer<uint8_t> k = i + 1;
    
    編譯器會拒絕,因為無法保證編譯時無異常。
  • 如果想要在編譯期檢測溢出,需要用別的機制(比如靜態斷言或不拋異常的版本)。
  • CNL庫設計權衡了運行時安全編譯時計算能力,兩者有時是矛盾的。

6. 解決辦法

  • 運行時安全時,不要用 constexpr 來初始化可能溢出的 safe_integer。
  • 如果必須用 constexpr,確保表達式不會溢出,或者用不拋異常的安全機制。
  • 或者在編譯時用 static_assert 做范圍檢查,避免溢出。

CNL庫中的固定點數(fixed_point)類型的模板參數推導(Deduction)和用戶自定義字面量(UDLs, User Defined Literals),我幫你詳細分析“理解”這段代碼的含義和設計原理。

1. 固定點數的模板參數推導(Class Template Argument Deduction)

auto x = fixed_point{42ul}; // fixed_point<unsigned long, 0>{42}
auto y = fixed_point{128};  // fixed_point<int, 0>{1}
  • fixed_point 是一個模板類,一般寫作 fixed_point<Rep, Exponent>,其中:
    • Rep 是底層整數類型(比如 int, unsigned long
    • Exponent 是固定點數的小數點位置(指數,負數表示小數位)
  • 這里 fixed_point{42ul} 直接用 unsigned long 42 初始化,推導為 fixed_point<unsigned long, 0>,即整數值 42,沒有小數部分。
  • fixed_point{128} 這句看起來應該是 fixed_point<int, 0>{128},表示整數128。
  • 你寫的注釋是 fixed_point<int, 0>{1},可能是筆誤(或者特意指某種行為)。通常 fixed_point{128} 表示整數128,指數為0。

2. 使用 CNL 的字面量操作符(User Defined Literals)

using cnl::literals;
auto z = fixed_point{128_c};  // fixed_point<int, 7>{128}
  • _c 是 CNL 定義的一個 用戶自定義字面量,它讓你創建帶有指數的固定點數字面量。
  • 128_c 實際上代表 fixed_point<int, 7>{128}
    • 這里指數是7,意味著小數點向左移動了7位,數值變成了 128 * 2^7,但因為整數是128,固定點表示其精度和位置與普通整數不同。

3. 大整數字面量和指數推導

auto a = fixed_point{0b10000000000000000000000000000000000000000_c};  
// a === fixed_point<int, 40>{0b10000000000000000000000000000000000000000l}
auto b = fixed_point{0b11111111111111111111111111111111111111111_c};
// b === fixed_point<long, 0>{0b11111111111111111111111111111111111111111l}
  • 這里用二進制字面量結合 _c 后綴創建了固定點數。
  • a 的字面量值非常大,帶有指數40,所以推導為 fixed_point<int, 40>,表示整數部分是該值乘以 2^40,這是高精度固定點數。
  • b 的值是全1的二進制串,推導為 fixed_point<long, 0>,表示沒有小數位,是普通整數。

4. “理解”總結

  • 固定點數模板支持通過構造參數自動推導底層整數類型和指數(小數點位置)。
  • CNL定義的用戶字面量 _c 用來創建帶指數的固定點數字面量,更方便和語義明確。
  • 通過二進制字面量和 _c 后綴可以直接創建非常大或精度極高的固定點數。
  • 這種設計極大簡化了固定點數的構造和代碼表達,同時保證了類型安全和高精度。

CNL 庫中的幾個重要主題:

  1. **彈性整數(elastic_integer)和用戶定義字面量(UDL)**的類型推導
  2. CNL 與 Boost.Multiprecision 的互操作性
  3. 用 Boost.Multiprecision 擴展固定點數的數值范圍和精度
    我幫你詳細“理解”每一部分:

1. 彈性整數 + 用戶定義字面量(UDL)

auto c = elastic_integer{2017_c};  // elastic_integer<11>{2017}
auto e = 0x7f000_elastic;           // fixed_point<elastic_integer<7>, 12>{0x7f000}
  • 2017_c 是 CNL 定義的用戶字面量,用來直接創建帶位寬信息的整數類型(這里推導為11位彈性整數),方便寫出適當寬度的整數類型。
  • 0x7f000_elastic 是用UDL定義的彈性整數作為固定點數的底層表示,fixed_point<elastic_integer<7>, 12>表示使用7位寬彈性整數,指數為12的固定點數。
  • 這樣可以用字面量直接控制整數的寬度和精度,減少模板參數書寫。

2. CNL 與 Boost.Multiprecision 互操作性

#include <cnl/auxiliary/boost.multiprecision.h>
using namespace boost::multiprecision;
template<int NumBits, int Exponent = 0>
using mp_fixed_point = cnl::fixed_point<number<cpp_int_backend<NumBits, NumBits, signed_magnitude, unchecked, void>>,Exponent
>;
  • 這里通過模板別名定義了 mp_fixed_point,結合了 CNL 的 fixed_point 和 Boost 的任意精度整數類型 number<cpp_int_backend<...>>
  • cpp_int_backend 是 Boost.Multiprecision 中的任意精度整數后端,可以自由指定位數(NumBits),且支持有符號數、無溢出檢查(unchecked)等。
  • 利用 Boost.Multiprecision 的強大整數類型,CNL 的固定點數可以支持非常大的數值范圍和極高精度,超出普通整數能表達的范圍。

3. 應用場景示例:Googol 和 Googolth

  • Googol (10^100)
  • Googolth (1 / Googol)
    這些巨大和超小的數值可以用上述的 mp_fixed_point 表示,說明結合了 Boost.Multiprecision 的固定點數能夠處理極端大/小數。

4. 但 Googolplex (10^(Googol)) 不行

  • Googolplex 是指數再指數級別(10的10^100次方),數值龐大到無法用普通定點甚至大整數類型直接表達(位數遠遠超過可用內存和計算能力)。
  • 這說明即使是 Boost.Multiprecision 也有物理和實現上的限制,無法無限擴展。
  • 在實際編程中,要考慮數值表示范圍的極限。

總結:

  • UDL 和彈性整數簡化了類型推導和數值表達,使代碼更直觀、類型安全。
  • CNL 能與 Boost.Multiprecision 無縫結合,實現高精度固定點數運算,適合極大或極小數值需求。
  • 但是極端數值(如googolplex)超出了任何實際計算機數值類型的表示范圍。
  • 這體現了CNL設計的靈活性和局限性。

CNL與Boost.SIMD的互操作性,以及CNL未來方向和一些具體用法的總結,我幫你詳細拆解和“理解”這段內容:

1. Boost.SIMD 與 CNL 固定點向量化

#include <cnl/auxiliary/boost.simd.h>
using boost::simd::pack;
template<class T, std::size_t N, int Exponent>
using fixed_point_pack = fixed_point<pack<T, N>, Exponent>;
using fpp = fixed_point_pack<int, 4, -16>;
using initializer = initializer<fpp>;
auto expected = fpp{initializer{7.9375+-1, -8.+.125, 0+-5, 3.5+-3.5}};
auto augend = fpp{initializer{7.9375, -8., 0, 3.5}};
auto addend = fpp{initializer{-1, .125, -5, -3.5}};
auto sum = augend + addend;
  • boost::simd::pack<T, N> 是Boost.SIMD庫里的SIMD向量模板類,表示一個長度為N的同類型數據包。
  • fixed_point_pack 是用SIMD包裹的固定點類型,也就是說,底層的整數表示用SIMD向量。
  • fpp 類型是4個 int 的SIMD包裹,指數為-16,意味著每個元素是小數點后16位的固定點數。
  • 你可以用 initializer 初始化這4個元素。
  • augendaddend 是兩個固定點SIMD向量,sum 是它們的元素級加法。
  • 這樣結合SIMD,能實現高性能的并行固定點運算。

2. CNL的今天和未來

  • CNL(Compositional Numeric Library)支持:
    • 任意寬度的整數和固定點類型。
    • 完整的四舍五入和溢出處理。
    • precise_integer 模板,支持自定義的舍入策略,像 closest_rounding_tag
    • safe_integerprecise_integer 的全套運算符重載。
    • 以及各種自由函數,像帶溢出檢測的 add(saturated_overflow, ...) 和帶四舍五入的 divide(closest_rounding_tag, 2, 3)
  • 未來會有更好的字面量支持,例如:
auto a = 0b1111.1111_elastic;  // fixed_point<elastic_integer<8>, -4>
  • 這表示用彈性整數作底層整數的固定點數,指數為-4,小數點向右4位,字面量結合了二進制和彈性整數的便捷性。

3. 總結理解

  • CNL結合了現代C++模板元編程和外部庫(Boost.SIMD、Boost.Multiprecision)來擴展數值類型的能力。
  • 利用SIMD包裹整數,可以做并行的固定點向量運算,提升性能。
  • precise_integersafe_integer 帶來安全且精確的算術計算,支持溢出和舍入策略。
  • 未來的CNL會繼續強化字面量的表達能力,讓數值代碼寫得更簡潔、可讀且高效。
  • 你舉的代碼正是這種現代數值庫結合高性能與類型安全的典范。

用CNL固定點數的簡單示例,演示固定點數的創建、加法和輸出。

#include <iostream>
#include <cnl/fixed_point.h>
int main() {using namespace cnl;// 定義一個固定點數類型,底層用int,指數-8(表示小數點右移8位,即精度1/256)using fixed8 = fixed_point<int, -8>;// 創建兩個固定點數變量fixed8 a = 1.5;    // 實際存儲的是1.5 * 256 = 384fixed8 b = 2.25;   // 實際存儲的是2.25 * 256 = 576// 做加法fixed8 c = a + b;  // 3.75// 輸出結果std::cout << "a = " << a << "\n"; // 1.5std::cout << "b = " << b << "\n"; // 2.25std::cout << "c = a + b = " << c << "\n"; // 3.75return 0;
}

解釋:

  • fixed_point<int, -8> 表示底層用 int,小數點右移8位,即精度是1/256。
  • 給定浮點數初始化,會自動轉成固定點數內部整數表示。
  • 支持算術運算和輸出,std::cout會自動轉換成浮點格式打印。
    運行結果:
a = 1.5
b = 2.25
c = a + b = 3.75

下面給你寫一個結合 Boost.SIMDCNL固定點數 的簡單示例,演示如何用 boost::simd::pack 來表示SIMD向量,配合CNL的 fixed_point 做向量化的固定點數加法。

環境說明

  • 你需要安裝 Boost.SIMD 和 CNL(John McFarlane的庫),確保頭文件和庫路徑正確。
  • 編譯時用支持SIMD指令的編譯器(gcc、clang、MSVC都支持)。

示例代碼

#include <iostream>
#include <cnl/fixed_point.h>
#include <cnl/auxiliary/boost.simd.h>
#include <boost/simd/pack.hpp>
int main() {using namespace cnl;using boost::simd::pack;// 定義一個固定點數SIMD向量類型:// pack<int, 4> 表示4個int的SIMD包,指數-8代表小數點右移8位using fixed_point_simd = fixed_point<pack<int, 4>, -8>;// 通過初始化器列表初始化兩個SIMD固定點向量fixed_point_simd a{7.5, 8.0, -3.25, 4.125};fixed_point_simd b{1.25, -2.5, 3.75, -4.125};// SIMD向量加法fixed_point_simd c = a + b;// 輸出結果auto print_pack = [](const fixed_point_simd& p) {for (int i = 0; i < 4; ++i) {std::cout << static_cast<double>(p[i]) << " ";}std::cout << "\n";};std::cout << "a = "; print_pack(a);std::cout << "b = "; print_pack(b);std::cout << "a + b = "; print_pack(c);return 0;
}

運行結果示例:

a = 7.5 8 -3.25 4.125 
b = 1.25 -2.5 3.75 -4.125 
a + b = 8.75 5.5 0.5 0 

說明:

  • fixed_point<pack<int, 4>, -8>:底層整數用 pack<int,4> 表示4個int組成的SIMD向量,指數-8表示小數點后8位精度。
  • fixed_point 支持向量運算,操作符會在4個元素上同時執行。
  • print_pack 通過索引訪問每個元素并轉換成 double 方便打印。

這里的 -8fixed_point 模板參數中用來表示小數點位置的 指數(Exponent),它決定了固定點數的縮放比例

具體解釋:

  • fixed_point<Rep, Exponent>
    • Rep 是底層的整數類型,比如這里是 pack<int, 4>,表示4個 int 組成的SIMD向量。
    • Exponent 是一個整數,表示這個固定點數的小數點相對于整數位的偏移。

這個指數的含義

  • 指數是二進制尺度的冪指數,Exponent = -8 表示:
    實際值 = 底層整數值 2 8 = 底層整數值 × 2 ? 8 \text{實際值} = \frac{\text{底層整數值}}{2^{8}} = \text{底層整數值} \times 2^{-8} 實際值=28底層整數值?=底層整數值×2?8
    也就是說,底層整數表示的數值需要除以 2 8 = 256 2^8 = 256 28=256 才是“真實的”浮點數。

舉例說明:

  • 假設底層整數值是 384,那么實際對應的固定點數是:
    384 × 2 ? 8 = 384 / 256 = 1.5 384 \times 2^{-8} = 384 / 256 = 1.5 384×2?8=384/256=1.5
  • 同理,整數 -832 表示:
    ? 832 / 256 = ? 3.25 -832 / 256 = -3.25 ?832/256=?3.25

為什么用指數來表示?

  • 用指數表示小數點位置比用小數位數更靈活,可以非常高效地用位運算實現乘除縮放。
  • 指數為負,表示小數點向右移(即分母是2的冪),指數為正,表示小數點向左移(分子是2的冪)。

總結:

fixed_point<pack<int, 4>, -8>
  • 4個 int 的SIMD包作為底層表示
  • 指數 -8 表示固定點數的值 = 整數值 × 2 ? 8 2^{-8} 2?8 = 整數值 / 256
  • 這樣就能高效表示小數,精度到1/256

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/84057.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/84057.shtml
英文地址,請注明出處:http://en.pswp.cn/web/84057.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux基礎重定向及組合重定向

一、基礎重定向操作符 ?類別? ?操作符? ?含義? ?示例? ?備注? ?標準輸出? > 覆蓋寫入 stdout 到文件 ls > file.txt 文件不存在則創建&#xff0c;存在則清空內容 >> 追加 stdout 到文件末尾 date >> log.txt 保留原有內容 ?標準…

佰力博科技與您探討鐵電分析儀適用場景

鐵電分析儀是一種用于測試和研究鐵電材料性能的精密儀器&#xff0c;其適用場景非常廣泛&#xff0c;涵蓋了材料科學、物理學、電子工程等多個領域。 1、材料科學與工程 鐵電分析儀廣泛應用于鐵電材料的研究&#xff0c;包括薄膜、厚膜、塊體材料以及電子陶瓷等。它能夠測試材料…

JVM 內存模型與垃圾回收機制全解析:架構、算法、調優實踐

Java 作為一門面向對象的編程語言&#xff0c;其核心優勢之一是 “一次編寫&#xff0c;到處運行” 的跨平臺特性。這一特性背后&#xff0c;Java 虛擬機&#xff08;JVM&#xff09;扮演著至關重要的角色。JVM 不僅負責解釋執行字節碼&#xff0c;還通過內存管理和垃圾回收機制…

自然語言處理相關基本概念

基本概念章節總結 一、語言學&#xff08;Linguistics&#xff09; 定義 研究語言的本質、結構和發展規律的科學&#xff0c;涵蓋語音、文字、語法等屬性。分支包括歷時語言學、共時語言學、描述語言學等。 核心內容 分析語言的形態、句法、語義等層面&#xff0c;如詞素&…

Vue購物車應用實現教程

文章目錄 1. 項目介紹2. 開發環境準備3. 設計購物車界面4. 創建Vue實例和數據模型5. 實現購物車功能5.1 從本地存儲加載數據5.2 監聽數據變化保存到本地存儲5.3 實現全選/反選功能5.4 計算選中商品的總價和總數量5.5 實現修改商品數量功能5.6 實現刪除商品功能5.7 實現結算功能…

雙因子認證如何讓Windows系統登錄更安全?SLA操作系統雙因素認證解決方案深度解析

引言&#xff1a;數字化轉型下的身份認證危機 在云計算與遠程辦公普及的2025年&#xff0c;企業信息系統正面臨前所未有的安全挑戰。微軟Azure Virtual Desktop漏洞事件、Citrix數據泄露等安全事件頻發&#xff0c;暴露出傳統密碼認證體系的致命缺陷。據《2025年云安全威脅報告…

FPGA基礎 -- Verilog語言要素之值集合

一、Verilog 值集合&#xff08;Value Set&#xff09; Verilog 是一種面向硬件建模的描述語言&#xff0c;為了更真實地模擬硬件行為&#xff0c;它并不僅僅像 C 語言那樣只有 0 和 1 兩種值&#xff0c;而是采用了四值邏輯&#xff08;Four-valued logic system&#xff09;…

開源一個芯片自由的脫機下載器

一、什么是脫機下載器 簡單來說&#xff0c;脫機下載器就是在不連接電腦、不用專業軟件的情況下&#xff0c;也能幫你把程序燒錄進芯片的工具。只要插上電源、按個按鈕&#xff0c;固件就自動下載進 MCU&#xff0c;非常適合量產、售后、維修等場景。 二、芯片自由的背后&…

Rust 學習筆記:關于模式匹配的練習題

Rust 學習筆記&#xff1a;關于模式匹配的練習題 Rust 學習筆記&#xff1a;關于模式匹配的練習題問題一問題二問題三 Rust 學習筆記&#xff1a;關于模式匹配的練習題 參考視頻&#xff1a; https://www.bilibili.com/video/BV1YxojYJESm 問題一 以下代碼能否通過編譯&…

利用tkinter函數構造MD5加密的可視化操作界面

GitHub文檔地址&#xff1a; https://github.com/gao7025/auto_entry_md5.git 引言 利用tkinter構造一個圖形界面的創建函數&#xff0c;主要實現了文件選擇、MD5加密處理、結果預覽和下載等功能。下面是主要涉及的功能模塊&#xff1a;主框架、文件選擇部分、MD5加密部分、結…

ICEM CFD網格生成 | 基本概念與界面工具

基本概念◆ 名稱定義 網格&#xff1a;網格是空間離散的單元&#xff0c;用于如下數值仿真 結構 流體 電磁 其他 單元 0D – 節點單元 質量點 約束&#xff0c;加載位置 1D –線單元 Bars, beams, rods, springs 2D 網格邊界 2D – 表面/殼單元 - 四邊形 - 三角…

簡化您的工作流程:在 Azure 中構建高效的邏輯應用程序

簡介 在當今的數字化環境中,自動化工作流程和服務集成對于追求效率和敏捷性的企業至關重要。Azure Logic Apps 使開發人員和 IT 專業人員能夠創建集成應用、數據、服務和系統的自動化工作流程。在本文中,我們將逐步講解使用 Azure 門戶創建 Logic Apps 的過程,并通過演示來說…

AI 技術落地實戰:開發流程優化、行業場景重塑與前沿應用洞察

在人工智能技術如火如荼發展的當下&#xff0c;AI 工具、大模型以及它們在各行業的應用&#xff0c;正以前所未有的態勢重塑著開發者的工作模式和各領域的發展格局。從智能編碼助手讓編程變得高效便捷&#xff0c;到自動化測試平臺提升軟件質量&#xff0c;從大模型在垂直行業的…

文本生成AI+圖像識別:電商詳情頁信息提取實戰

行業問題&#xff1a;傳統采集難以應對“圖文視頻化”的電商信息 在電商平臺不斷“視頻化”的趨勢下&#xff0c;傳統的網頁采集手段正逐漸失效。以抖音為例&#xff0c;商品信息已不僅限于圖文詳情&#xff0c;而是通過短視頻、圖像混排、語音解說等形式呈現。商品的名稱、優…

linux權限基礎

權限的概念 linux中&#xff0c;權限是用于控制【用戶】對 【文件】進行操作控制的工具。用戶權限文件權限 用戶權限 用戶 用戶組&#xff1a;具有相同特性的用戶的集合體。 文件權限 linux中&#xff0c;一切皆文件&#xff0c;包括普通文件&#xff0c;目錄&#xff0c;文件…

讓C++處理JSON類型配置文件更輕松-Hjson-cpp詳解

讓C處理JSON類型配置文件更輕松-Hjson-cpp詳解 一、Hjson-Cpp簡介Hjson-Cpp簡介核心特性安裝與集成基本用法示例常用API說明與JSON互轉錯誤處理性能建議高級特性1. 類型安全訪問2. 文件操作3. 自定義解析規則 二、使用教程下載使用 一、Hjson-Cpp簡介 Hjson-Cpp簡介 Hjson-Cp…

單例模式的好處

為什么要使用單例模式 1.資源管理&#xff1a; 唯一性&#xff1a;某些資源在整個應用程序中只需要一個實例&#xff0c;例如日志記錄器、配置管理器、數據庫連接池等。單例模式可以確保這些資源的唯一性&#xff0c;避免重復創建和管理。 全局訪問&#xff1a;單例模式提供了…

LangChain 結構化輸出指南

LangChain 結構化輸出指南 概述 對于許多應用程序&#xff08;如聊天機器人&#xff09;&#xff0c;模型需要直接用自然語言回應用戶。然而&#xff0c;在某些場景下&#xff0c;我們需要模型以結構化格式輸出。例如&#xff0c;我們可能希望將模型輸出存儲在數據庫中&#…

探究webView與html的通訊

最近出來個新需求&#xff1a; 需求描述&#xff1a; 將uniapp的代碼打包成一個app&#xff0c;并實現原本的功能。 原uniapp是一個H5項目&#xff0c;主要的步驟流程是上傳用戶的身份證進行二要素認證&#xff0c;成功后再進行三方活體認證&#xff0c;然后三方回跳到項目中的…

高級定時器TIM1、TIM8

高級定時器在通用定時器的基礎上增加了一些功能&#xff0c;如&#xff1a;重復計數器、帶死區控制的互補輸出通道、斷路輸入等。 捕獲/比較通道的輸出部分(通道1至3) 捕獲/比較通道的輸出部分(通道4) ①重復計數器RCR 基本和通用定時器發生溢出時&#xff0c;會直接生成更新時…