fmt中uint128_fallback的实现分析

数据结构

class uint128_fallback {private:uint64_t lo_, hi_;public:constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}constexpr uint64_t high() const noexcept { return hi_; }constexpr uint64_t low() const noexcept { return lo_; }template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>constexpr explicit operator T() const {return static_cast<T>(lo_);}friend constexpr auto operator==(const uint128_fallback& lhs,const uint128_fallback& rhs) -> bool {return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;}friend constexpr auto operator!=(const uint128_fallback& lhs,const uint128_fallback& rhs) -> bool {return !(lhs == rhs);}friend constexpr auto operator>(const uint128_fallback& lhs,const uint128_fallback& rhs) -> bool {return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;}

加法

注意处理进位

  FMT_CONSTEXPR void operator+=(uint128_fallback n) {uint64_t new_lo = lo_ + n.lo_;uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);FMT_ASSERT(new_hi >= hi_, "");lo_ = new_lo;hi_ = new_hi;}friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)-> uint128_fallback {return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};}  

乘法

这里作者为了简单,只处理了和u32相乘的情形,不过程序本身就是只满足当前需求即可,不用盲目应对所有的事情,简单是一种美。

  friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)-> uint128_fallback {FMT_ASSERT(lhs.hi_ == 0, "");uint64_t hi = (lhs.lo_ >> 32) * rhs;uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;uint64_t new_lo = (hi << 32) + lo;return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};}

关于乘法原理,我一开始还没看懂,后来发现,这里的代码是针对u64是否进位,因为乘法的主体就是u64的数字。附上一段有趣的对话。

Me
可以介绍一下这段乘法的计算原理吗Assistant
当我们对一个 uint128_fallback 对象的低64位 lhs.lo_ 与一个 uint32_t 值 rhs 进行乘法运算时,可以将乘法分解为两个部分:高位部分和低位部分。首先,我们计算 lhs.lo_ 右移32位后的值与 rhs 的乘积,这部分乘积的结果是一个64位的值,存储在 hi 变量中。这个乘积的结果是原始乘法的高位部分,即 (lhs.lo_ >> 32) * rhs。接下来,我们计算 lhs.lo_ 与 ~uint32_t()(即将 uint32_t 的所有位取反)进行按位与的结果与 rhs 的乘积。这部分乘积的结果也是一个64位的值,存储在 lo 变量中。这个乘积的结果是原始乘法的低位部分,即 (lhs.lo_ & ~uint32_t()) * rhs。然后,我们将高位部分 hi 左移32位(相当于乘以 2^32)并与低位部分 lo 相加,得到一个128位的值 new_lo。这个值包含了原始乘法的结果的低64位。最后,我们根据进位情况来计算结果的高位部分 hi。如果 new_lo 小于 lo,说明低位部分的加法产生了进位,需要在 hi 的基础上加1。否则,进位为0,直接将 hi 的值赋给结果的高位部分。最终,我们将计算得到的 hi 和 new_lo 构造成一个新的 uint128_fallback 对象并返回。这个对象包含了原始乘法的结果的高64位和低64位。Me
为什么不计算lhs.hi_呢 2. 进位那里我还是没懂
Assistant
在给定的代码中,乘法运算只涉及到了 lhs 对象的低64位 lhs.lo_,并且在断言 FMT_ASSERT(lhs.hi_ == 0, ""); 中要求 lhs.hi_ 的值为0。因此,这段代码的设计可能是基于特定的使用场景和需求,只关注低64位的乘法运算。如果需要对整个128位进行乘法运算,就需要考虑 lhs.hi_ 的值。关于进位的处理,我们需要考虑低64位乘法运算的溢出情况。在这里,我们使用了一个简单的技巧来判断低位部分的加法是否产生了进位。具体来说,我们比较 new_lo 和 lo 的大小。如果 new_lo 小于 lo,说明低位部分的加法产生了进位。这是因为在加法运算中,如果加数和被加数的最高位都为1时,会产生进位。因此,我们在高位部分 hi 的基础上加1,以表示进位的存在。如果 new_lo 大于等于 lo,则说明没有发生进位,进位为0,直接将 hi 的值赋给结果的高位部分。这种处理方式是一种简单的处理进位的方法,适用于这个特定的乘法运算场景。在其他情况下,可能需要更复杂的进位处理逻辑,具体取决于具体的算法和需求。Me
哦,我想明白了,这里的进位是针对uint64_t的进位!
Assistant
是的,你理解得对!这里的进位确实是针对 uint64_t 类型的进位。由于我们在计算乘法结果时使用了两个 uint64_t 类型的变量 hi 和 lo 来保存乘法的高位和低位部分,因此在处理进位时也是针对这两个变量进行的。具体来说,在计算新的低位部分 new_lo 时,我们将 hi 左移了32位,并将其与 lo 相加。这个加法操作可能会产生进位,即在加法过程中产生了更高位的进位。我们在这里通过比较 new_lo 和 lo 的大小来判断是否发生了进位。如果 new_lo 小于 lo,说明产生了进位,我们需要在高位部分 hi 的基础上加1,以表示进位的存在。由于 hi 和 lo 都是 uint64_t 类型,因此进位的处理是基于这两个64位变量的运算。这种进位处理方式适用于处理64位无符号整数的进位情况。

位移

  FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {if (shift == 64) return {0, hi_};if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};}FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {if (shift == 64) return {lo_, 0};if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};}

位拷贝

这里大佬使用了模板选择的方式,先给一个使用案例,再来理解大佬的代码

struct uint32_pair {uint32_t u[2];
};TEST(util_test, bit_cast) {auto s = fmt::detail::bit_cast<uint32_pair>(uint64_t{42});EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), 42ull);s = fmt::detail::bit_cast<uint32_pair>(~uint64_t{0});EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), ~0ull);
}

好的,大佬这里的注释写道是做位拷贝的,对于一个To大于From长度的类型,先做存储空间的扩容,扩充到长度一样的情况之后再拷贝,如果4字节扩容到比From大,则编译失败。

// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
// and 128-bit pointers to uint128_fallback.
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
inline auto bit_cast(const From& from) -> To {constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));struct data_t {unsigned value[static_cast<unsigned>(size)];} data = bit_cast<data_t>(from);auto result = To();if (const_check(is_big_endian())) {for (int i = 0; i < size; ++i)result = (result << num_bits<unsigned>()) | data.value[i];} else {for (int i = size - 1; i >= 0; --i)result = (result << num_bits<unsigned>()) | data.value[i];}return result;
}// Implementation of std::bit_cast for pre-C++20.
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
#ifdef __cpp_lib_bit_castif (is_constant_evaluated()) return std::bit_cast<To>(from);
#endifauto to = To();// The cast suppresses a bogus -Wclass-memaccess on GCC.std::memcpy(static_cast<void*>(&to), &from, sizeof(to));return to;
}

这里,我一开始对于bit_cast理解错误,以为存在了递归调用。然后自己写了一个错的例子,果然就递归调用了,事实上大佬使用了std::enable_if_t< sizeof To > sizeof From>来避免了递归,因为调用后就会转到std::enable_if_t< sizeof To == sizeof From>的调用上。

附一个自己写的错误的例子,会递归调用导致编译失败。

#include <iostream>
#include <limits>
#include <type_traits>#define FMT_ENABLE_IF(T) std::enable_if_t<(T)>template <typename T>
constexpr auto num_bits() -> int
{return std::numeric_limits<T>::digits;
}template <typename To, typename From, typename = FMT_ENABLE_IF(sizeof(To) >= sizeof(From))>
inline auto bit_cast(const From& from) -> To {constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));struct data_t {unsigned value[static_cast<unsigned>(size)];} data = bit_cast<data_t>(from);auto result = To();if (true) {for (int i = 0; i < size; ++i)result = (result << num_bits<unsigned>()) | data.value[i];} else {for (int i = size - 1; i >= 0; --i)result = (result << num_bits<unsigned>()) | data.value[i];}return result;
}struct uint32_pair {uint32_t u[2];
};int main()
{auto s = bit_cast<uint32_pair>(uint64_t{42});
}

最后,大佬应该是为了类型严谨,使用了模板的手法做预处理,确实更为安全,一般写法如下,弊病是会忽略大小端的差异,也会有内存越界的风险(考虑From的类型长度小于To的时候)。

template <typename To, typename From, typename = std::enable_if_t<sizeof(To) == sizeof(From)>>
inline auto bit_cast(const From& from) -> To {To result;std::memcpy(&result, &from, sizeof(To));return result;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/606061.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[VUE]2-vue的基本使用

目录 vue基本使用方式 1、vue 组件 2、文本插值 3、属性绑定 4、事件绑定 5、双向绑定 6、条件渲染 7、axios 8、⭐跨域问题 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅…

气膜建筑:舒适、智能、可持续

气膜建筑之所以能够拥有广阔的发展空间&#xff0c;源于其融合了诸多优势特点&#xff0c;使其成为未来建筑领域的前沿趋势。 气膜建筑注重环境可持续性和能源效率。在材料和设计上&#xff0c;它采用可回收材料、提高热保温效果&#xff0c;并积极利用太阳能等可再生能源&…

【洛谷学习自留】p9226 糖果

解题思路&#xff1a; 简单的计算题&#xff0c;用n对k取余&#xff0c;如果余数为0&#xff0c;则输出k的值&#xff0c;否则输出&#xff08;k-余数&#xff09;的值。 代码实现&#xff1a; import java.util.Scanner;public class p9226 {public static void main(Strin…

Go、Docker、云原生学习笔记全攻略:从零开始,一步步走向精通!(2024版)

第一章、Go语言学习宝典 一、介绍 01.Go 语言的前生今世 二、开发环境搭建 01.Go 语言开发环境搭建 三、初识GO语言 01.Go 多版本管理工具 02.第一个 Go 程序“hello&#xff0c;world“ 与 main 函数 03.Go 常用命令介绍 04.Go 项目代码布局 05.探索 GO 项目依赖包管…

【2023 CCF 大数据与计算智能大赛】基于TPU平台实现超分辨率重建模型部署 基于QuickRNet的TPU超分模型部署

2023 CCF 大数据与计算智能大赛 《赛题名称》 基于QuickRNet的TPU超分模型部署 巴黎欧莱雅 林松 智能应用业务部算法工程师 中信科移动 中国-北京 gpu163.com 团队简介 巴黎欧莱雅团队包含一个队长和零个队员。 队长林松&#xff0c;研究生学历&#xff0c;2019-202…

【一份老网工珍藏多年的网络配置笔记,很重要!】

01、交换机、路由器的几种配置模式及模式转换 1. 用户模式 登录到交换机&#xff08;路由器&#xff09;时会自动进入用户模式&#xff0c;提示符为 switchname>。在该模式下只能够查看相关信息&#xff0c; 对 IOS的运行不产生任何影响。 2. 特权模式 用户模式下&#xff…

精进单元测试技能 —— Pytest断言的艺术!

本篇文章主要是阐述Pytest在断言方面的应用。让大家能够了解和掌握Pytest针对断言设计了多种功能以适应在不同测试场景上使用。 了解断言的基础 在Pytest中&#xff0c;断言是通过 assert 语句来实现的。简单的断言通常用于验证预期值和实际值是否相等&#xff0c;例如&#…

蒙牛×每日互动合作获评中国信通院2023“数据+”行业应用优秀案例

当前在数字营销领域&#xff0c;品牌广告主越来越追求品效协同。针对品牌主更注重营销转化的切实需求&#xff0c;数据智能上市企业每日互动&#xff08;股票代码&#xff1a;300766&#xff09;发挥自身数据和技术能力优势&#xff0c;为垂直行业的品牌客户提供专业的数字化营…

实时云渲染的技术原理是什么?一篇文章讲透云渲染

实时云渲染依赖互联网的连接和高性能服务集群&#xff0c;为客户实时提供渲染画面&#xff0c;它有以下几个关键技术&#xff1a; 1、GPU上云 利用云服务器强大的GPU替代本地GPU进行图形处理&#xff0c;提高性能、可替换性和扩展性。2、视频流化技术 这项技术能在远程执行用户…

【ArcGIS微课1000例】0087:经纬度格式转换(度分秒转度、度转度分秒)

ArcGIS软件可以很方便的直接实现度分秒转度、度转度分秒。 文章目录 一、转换预览二、工具介绍三、案例解析一、转换预览 借助ArcGIS快速实现度分秒与度及其他格式的坐标转换。例如:度分秒→度 度分秒: 度: 二、工具介绍 转换坐标记法:将一个或两个字段包含的坐标记法从一…

ElasticSearch删除索引的命令

ElasticSearch删除索引的命令 当前命令操作是在Kibana中操作 删除单个索引 DELETE 索引名称 DELETE test删除多个索引 DELETE 索引名称1,索引名称2 DELETE test1,test2删除全部索引 DELETE /_allDELETE /*删除索引的中的全部数据 POST test/_delete_by_query {&qu…

XREAL推出其新款AR眼镜:XREAL Air 2 Ultra,体量轻内置音效

这款眼镜堪称科技的杰作&#xff0c;它以钛合金为框架&#xff0c;尽显轻盈与精致。配备的双3D环境传感器&#xff0c;宛如双眼般敏锐&#xff0c;能精准捕捉头部运动&#xff0c;让你在虚拟与现实间自由穿梭。120Hz的超高刷新率与500尼特的亮度&#xff0c;让你在4米之外感受1…

搭建React开发环境-webpack实现

周末在家学会React前端知识&#xff0c;记录下来&#xff0c;方便备查。 webpack版本&#xff1a;webpack5 编译器&#xff1a;vscode 第一步、新建项目及初始化 1&#xff09;新建项目文件夹 可命名为 my_webpack 2&#xff09;初始化项目 使用命令 npm init -y&#xff0c;…

Centos7 MongoDB安装

基础配置&#xff08;Centos7.5镜像&#xff0c;2核2GB内存&#xff0c;50GB硬盘&#xff09; 主机名IPmongodb192.168.100.124 &#xff08;在MongoDB官网有linux安装MongoDB的步骤&#xff09; &#xff08;1&#xff09;配置yum源 [rootmongodb ~]# cat /etc/yum.repos.d…

Go语言学习之旅-开篇

Go语言学习之旅-开篇 前言 最近对Go语言非常感兴趣&#xff0c;准备花一段时间来学习&#xff0c;此系列文章用于学习笔记整理与学习记录。 简介 Go&#xff08;又称 Golang&#xff09;是 Google的 Robert Griesemer&#xff0c;Rob Pike 及 Ken Thompson 开发的一种静态强…

DDIA 第十一章:流处理

本文是《数据密集型应用系统设计》&#xff08;DDIA&#xff09;的读书笔记&#xff0c;一共十二章&#xff0c;我已经全部阅读并且整理完毕。 采用一问一答的形式&#xff0c;并且用列表形式整理了原文。 笔记的内容大概是原文的 1/5 ~ 1/3&#xff0c;所以你如果没有很多时间…

vue2中this.$emit(“update:xx“,value)和xx.sync的用法在vue3中有什么变化

在 Vue 3 中&#xff0c;v-model 语法和 this.$emit("update:xx", value) 的用法略有变化&#xff0c;而 .sync 修饰符已经不再存在。下面是 Vue 2 中和 Vue 3 中的比较&#xff1a; Vue 2 中的写法&#xff1a; 使用 this.$emit("update:xx", value)&am…

如何修复 SQL Server 数据库中的恢复挂起状态?

当我们想与关系数据库交互时&#xff0c;SQL 就会出现并帮助用户与数据库进行交互。SQL 从高级语言中获取用户的输入&#xff0c;然后访问将代码转换为机器可理解的形式。SQL 确实会恢复数据库文件&#xff0c;但有时 SQL 服务器恢复暂挂阶段会进入帐户&#xff0c;这会停止恢复…

Java Collections类排序学习

jdk自带排序学习&#xff0c;比如我们写一个排序代码 List score new ArrayList();score.add(1);score.add(12);score.add(45);score.add(67);Collections.sort(score); 来看一下sort的实现 /*** Sorts the specified list into ascending order, according to the* {linkpla…

系统编程--VIM特辑

这里写目录标题 vim三种工作模式进入文本模式的快捷键在命令模式下进行文本编辑删除快捷键复制粘贴查找替换查找替换 vim其他操作 vim打造简易IDE vim 三种工作模式 具体可见第二章对vim的详细介绍 需要注意的是&#xff0c;在末行模式下执行完一次命令&#xff0c;就可以直接…