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

定点小数与分数

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.dtcms.com/a/289424.html

相关文章:

  • Laravel 框架NOAUTH Authentication required 错误解决方案-优雅草卓伊凡
  • Leetcode 124. 二叉树中的最大路径和
  • 面向对象基础笔记
  • 提升H7-TOOL自制nRF54L15脱机烧写算法文件速度,1MB程序仅需11秒,并且支持了UICR编程
  • C++23中的std::expected:异常处理
  • 以“融合进化 智领未来”之名,金仓Kingbase FlySync:国产数据库技术的突破与创新
  • SpringBoot集成Skywalking链路跟踪
  • CAN通讯理论与实践:调试和优化全讲解
  • 20250720-2-Kubernetes 调度-资源限制对Pod调度的影响(1)_笔记
  • 基于深度学习的目标检测:从基础到实践
  • 尚庭公寓--------登陆流程介绍以及功能代码
  • 常见的离散积分方法
  • 基于bert-lstm对微博评论的情感分析系统设计与实现
  • 《每日AI-人工智能-编程日报》--2025年7月20日
  • Direct3D 11学习(一)
  • Charles 的 Windows proxy 对爬取瑞数6 网站接口数据的作用分析
  • 高性能架构模式——单服务器高性能模式(PPC与TPC)
  • 创新几何解谜游戏,挑战空间思维极限
  • 【51单片机仿真复位电阻电容参数】2022-5-17
  • TD3与SAC强化学习算法深度对比
  • BLIP、InternVL Series(下)
  • SSH开启Socks5服务
  • 强化学习_Paper_ICLR2024_When Should We Prefer DECISION TRANSFORMERS for offline-RL
  • 【分布式 ID】详解百度 uid-generator(基础篇)
  • java12基础(day12)
  • 零基础学习性能测试第一章-为什么会有性能问题
  • 【读技术报告】Manner Agent如何管理上下文
  • 从 AlphaGo 到具身机器人:AI 四力阶梯的突破之旅
  • 爬虫实战案例(两个)
  • Open64 WHIRL