当前位置: 首页 > ds >正文

定点小数与分数

8 位定点小数

8 位有符号整型的值域是 [-128, 127] , 8 位定点小数就是将这个整型视为一个分数的分子,这个分数的分母是 21282^{128}2128

这样这个分数的值域就是:
[−1,127128] [-1, \frac{127}{128}] [1,128127]

于是就可以表示小于 1 的数了。


分辨率为:
128−1=127=1128 \frac{1}{2^{8-1}}=\frac{1}{2^7}=\frac{1}{128} 2811=271=1281

所以 8 位定点小数本质上就是一个整型值 int8_t, 只不过计算的时候要记得除以 128, 这样才能得到实际值。

定点小数与分数类

在 C++ 中可以直接设计一个分数类

	////// @brief 分数类///class Int64Fraction final :public base::ICanToString{private:int64_t _num = 0;int64_t _den = 1;public:/* #region 构造函数 */////// @brief 默认构造,分子为 0,分母为 1.///constexpr Int64Fraction() = default;////// @brief 从整型值构造。分子为 num, 分母为 1.////// @param num 分子。///template <typename T>requires(std::is_integral_v<T>)constexpr Int64Fraction(T num){_num = num;_den = 1;}////// @brief 通过分子,分母进行构造。/// @param num 分子/// @param den 分母///constexpr Int64Fraction(int64_t num, int64_t den){SetNum(num);if (num == 0){SetDen(1);}else{SetDen(den);}}////// @brief 通过浮点数构造。////// @param value///constexpr Int64Fraction(base::Double const &value){if (value.Value() == 0.0){SetNum(0);SetDen(1);return;}double db = value.Value();// 要保证分数计算过程不溢出,需要保证 factor * db <= INT64_MAX.int64_t factor = INT64_MAX / base::ceil(db);base::Int64Fraction int_part{static_cast<int64_t>(db)};db -= static_cast<double>(int_part);db *= factor;base::Int64Fraction fractional_part{static_cast<int64_t>(db), factor};*this += int_part + fractional_part;}/* #endregion *//* #region 分子分母 */////// @brief 获取分子。////// @return///constexpr int64_t Num() const{return _num;}////// @brief 设置分子。////// @param value///constexpr void SetNum(int64_t value){_num = value;}////// @brief 获取分母。////// @return///constexpr int64_t Den() const{return _den;}////// @brief 设置分母。////// @param value///constexpr void SetDen(int64_t value){if (value == 0){throw std::invalid_argument{"分母不能为 0."};}_den = value;}/* #endregion *//* #region 计算函数 */////// @brief 化简自身。//////constexpr void Simplify(){if (_den == 0){throw std::invalid_argument{"分母不能为 0."};}if (_num == 0){_den = 1;return;}// 分子分母同时除以最大公约数int64_t gcd_value = std::gcd(_num, _den);int64_t scaled_num = _num / gcd_value;int64_t scaled_den = _den / gcd_value;if (scaled_den < 0){// 如果分母小于 0,分子分母同时取相反数scaled_num = -scaled_num;scaled_den = -scaled_den;}_num = scaled_num;_den = scaled_den;}////// @brief 化简后的形式。////// @note 返回化简后的值,不改变自身。////// @return///constexpr Int64Fraction SimplifiedForm() const{base::Int64Fraction ret{*this};ret.Simplify();return ret;}////// @brief 倒数/// @return///constexpr Int64Fraction Reciprocal() const{base::Int64Fraction ret{_den, _num};return ret.SimplifiedForm();}////// @brief 取绝对值。////// @return///constexpr Int64Fraction Abs() const{if (*this < 0){return -*this;}return *this;}////// @brief 向下取整/// @return///constexpr int64_t Floor() const{int64_t ret = Div();if (*this < 0){if (Mod()){ret -= 1;}}else{/* 因为 C++ 除法近 0 截断,所以如果 Div >0 ,本来就是向下取整了,* 不用再额外的操作了。** Div = 0 就更不用说了,也不用什么额外的操作,直接返回 0 就完事了。*/}return ret;}////// @brief 向上取整/// @return///constexpr int64_t Ceil() const{int64_t ret = Div();if (*this > 0){if (Mod()){ret += 1;}}return ret;}////// @brief 获取分子除以分母的值/// @return///constexpr int64_t Div() const{return _num / _den;}////// @brief 获取分子除以分母的余数/// @return///constexpr int64_t Mod() const{return _num % _den;}////// @brief 降低分辨率。////// @param resolution///constexpr void ReduceResolution(base::Int64Fraction const &resolution){if (resolution <= 0){throw std::invalid_argument{CODE_POS_STR + "分辨率不能 <= 0."};}Simplify();if (_den >= resolution._den){// 本分数的分母比 resolution 的分母大,说明本分数的分辨率大于 resolution.//// 首先需要减小本分数的分母,将分辨率降下来。分子分母同时除以一个系数进行截断,// 从而降低分辨率。int64_t multiple = _den / resolution._den;// 首先将分辨率降低到 1 / resolution._den._num /= multiple;_den /= multiple;// 如果 resolution._num > 1, 则还不够,刚才的分辨率降低到 1 / resolution._den 了,// 还要继续降低。_num = _num / resolution._num * resolution._num;}else{// 本分数的分母比 resolution 的分母小。但这不能说明本分数的分辨率小于 resolution,// 因为 resolution 的分子可能较大。//// 将 resolution 的分子分母同时除以一个系数,将 resolution 的分母调整到与本分数的分母// 相等,然后看一下调整后的 resolution 的分子,如果不等于 0, 即没有被截断成 0, 说明原本的// 分子确实较大,大到足以放大 resolution 的大分母所导致的小步长,导致步长很大,分辨率低。// 这种情况下本分数的分辨率才是高于 resolution, 才需要降低分辨率。int64_t multiple = resolution._den / _den;int64_t div = resolution._num / multiple;if (div != 0){_num = _num / div * div;}}Simplify();}/* #endregion */constexpr Int64Fraction operator-() const{Int64Fraction ret{-_num, _den};return ret.SimplifiedForm();}/* #region 四则运算符 */constexpr Int64Fraction operator+(Int64Fraction const &value) const{// 通分后的分母为本对象的分母和 value 的分母的最小公倍数int64_t scaled_den = std::lcm(_den, value.Den());// 通分后的分子为本对象的分子乘上分母所乘的倍数int64_t scaled_num = _num * (scaled_den / _den);int64_t value_scaled_num = value.Num() * (scaled_den / value.Den());Int64Fraction ret{scaled_num + value_scaled_num,scaled_den,};return ret.SimplifiedForm();}constexpr Int64Fraction operator-(Int64Fraction const &value) const{Int64Fraction ret = *this + (-value);return ret.SimplifiedForm();}constexpr Int64Fraction operator*(Int64Fraction const &value) const{base::Int64Fraction ret;ret.SetNum(_num * value.Num());ret.SetDen(_den * value.Den());return ret.SimplifiedForm();}constexpr Int64Fraction operator/(Int64Fraction const &value) const{Int64Fraction ret{*this * value.Reciprocal()};return ret.SimplifiedForm();}/* #endregion *//* #region 自改变四则运算符 */constexpr Int64Fraction &operator+=(Int64Fraction const &value){*this = *this + value;return *this;}constexpr Int64Fraction &operator-=(Int64Fraction const &value){*this = *this - value;return *this;}constexpr Int64Fraction &operator*=(Int64Fraction const &value){*this = *this * value;return *this;}constexpr Int64Fraction &operator/=(Int64Fraction const &value){*this = *this / value;return *this;}/* #endregion */////// @brief 将分数转化为字符串/// @return///virtual std::string ToString() const override{return std::to_string(_num) + " / " + std::to_string(_den);}/* #region 强制转换运算符 */constexpr explicit operator int64_t() const{return Div();}constexpr explicit operator uint64_t() const{return static_cast<uint64_t>(Div());}constexpr explicit operator int32_t() const{return static_cast<int32_t>(Div());}constexpr explicit operator uint32_t() const{return static_cast<uint32_t>(Div());}constexpr explicit operator int16_t() const{return static_cast<int16_t>(Div());}constexpr explicit operator uint16_t() const{return static_cast<uint16_t>(Div());}constexpr explicit operator int8_t() const{return static_cast<int8_t>(Div());}constexpr explicit operator uint8_t() const{return static_cast<uint8_t>(Div());}constexpr explicit operator double() const{base::Int64Fraction copy{*this};double int_part = static_cast<double>(copy.Div());copy -= copy.Div();double fraction_part = static_cast<double>(copy.Num()) / static_cast<double>(copy.Den());return int_part + fraction_part;}/* #endregion *//* #region 比较 */////// @brief 本对象等于 another./// @param another/// @return///constexpr bool operator==(Int64Fraction const &another) const{if (Num() == 0 && another.Num() == 0){/* 2 个分子都为 0 直接返回相等,这样更加安全,避免分子都为 0* 分母不相等时错误地将两个分数判断为不相等。*/return true;}Int64Fraction f1 = SimplifiedForm();Int64Fraction f2 = another.SimplifiedForm();return f1.Num() == f2.Num() && f1.Den() == f2.Den();}////// @brief 本对象大于 another./// @param another/// @return///constexpr bool operator>(Int64Fraction const &another) const{// 先化简,避免分母为负数,然后使用交叉乘法比大小。Int64Fraction f1 = SimplifiedForm();Int64Fraction f2 = another.SimplifiedForm();int64_t num1{f1.Num()};int64_t den1{f1.Den()};int64_t num2{f2.Num()};int64_t den2{f2.Den()};return num1 * den2 > num2 * den1;}////// @brief 本对象小于 another./// @param another/// @return///constexpr bool operator<(Int64Fraction const &another) const{// 先化简,避免分母为负数,然后使用交叉乘法比大小。Int64Fraction f1 = SimplifiedForm();Int64Fraction f2 = another.SimplifiedForm();int64_t num1{f1.Num()};int64_t den1{f1.Den()};int64_t num2{f2.Num()};int64_t den2{f2.Den()};return num1 * den2 < num2 * den1;}////// @brief 本对象大于等于 another.////// @param another/// @return true/// @return false///constexpr bool operator>=(Int64Fraction const &another) const{if (*this == another){return true;}if (*this > another){return true;}return false;}////// @brief 本对象小于等于 another.////// @param another/// @return true/// @return false///constexpr bool operator<=(Int64Fraction const &another) const{if (*this == another){return true;}if (*this < another){return true;}return false;}/* #endregion */};

当一个 int8_t 变量 a 被视为 8 位定点小数,用它来初始化这个分数时就是

base::Int64Fraction frac{a, 128};

即把这个 int8_t 变量 a 作为分数的分子,128 作为分母,得到的分数值就是这个定点小数要表示的值。

视为每一位的权重发生改变了

在这里插入图片描述

N 位定点小数

8 位定点小数可以看作是分子是 8 位有符号整型,分母是 28−1=27=1282^{8-1} =2^7=128281=27=128 的分数,则 N 位定点小数可以看作是分子是 N 位有符号整型,分母是 2N−12^{N-1}2N1 的分数。


值域为:
[−1,2N−1−12N−1] [-1, \frac{2^{N-1}-1}{2^N-1}] [1,2N12N11]

分辨率为:
12N−1 \frac{1}{2^{N-1}} 2N11

相关的软考例题

在这里插入图片描述
在这里插入图片描述
例12 的那些选项应该是打错了,实际上这些选项想要表示的应该是 2 的幂,只不过 2 右边的内容没有往 2 的右上角偏,而是错误地与 2 平齐了。

上文说定点小数实际上就是一个补码形式的有符号整型,只不过你要当作它有一个分母存在。N 位的整型共有 2N2^N2N 种不同的值,所以被解释成定点小数后也是有这么多个值,所以选 A.

例13 所谓的定点整数其实就是有符号整型。所以选 B.

http://www.xdnf.cn/news/16011.html

相关文章:

  • langchain调用本地ollama语言模型和嵌入模型
  • 线程状态线程安全
  • gradle微服务依赖模版
  • 软件反调试(5)- 基于注册表实时调试器检测
  • [Python] -项目实战7- 用Python和Tkinter做一个图形界面小游戏
  • 我的世界-推理
  • 基于Event Sourcing和CQRS的微服务架构设计与实战
  • 连接语言大模型(LLM)服务进行对话
  • 随着GPT-5测试中泄露OpenAI 预计将很快发布 揭秘GPT-5冲击波:OpenAI如何颠覆AI战场,碾压谷歌和Claude?
  • [硬件电路-58]:根据电子元器件的控制信号的类型分为:电平控制型和脉冲控制型两大类。
  • 威力导演 12:革新级影音创作平台——专业特效与极致效率的完美融合
  • 算法题(176):three states
  • 100个GEO基因表达芯片或转录组数据处理27 GSE83456
  • [simdjson] 实现不同CPU调度 | 自动硬件适配的抽象
  • JAVA面试宝典 -《API设计:RESTful 与 GraphQL 对比实践》
  • Linux操作系统之线程(四):线程控制
  • RabbitMQ核心组件浅析:从Producer到Consumer
  • 【Django】DRF API版本和解析器
  • ubuntu-linux-pycharm-社区版安装与django配置
  • 高性能熔断限流实现:Spring Cloud Gateway 在电商系统的实战优化
  • Linux网上邻居局域网络共享工具Samba及Smb协议,smbd,nmbd服务,smbpasswd,pdbedit命令,笔记250720
  • 数组算法之【合并两个有序数组】
  • 无线通信相关概念
  • 【机器学习深度学习】魔塔社区模型后缀全解析:Base、Chat、Instruct、Bit、Distill背后的技术密码
  • 【Elasticsearch】冷热集群架构
  • 力扣 hot100 Day50
  • 在Ubuntu22系统上离线部署ai-infra-guard教程【亲测成功】
  • windows C#-本地函数
  • 【计算机组成原理】原码、补码和移码
  • ZooKeeper学习专栏(一):分布式协调的核心基石