• 整理C++常用整数运算的所有细节


    前段时间写我的安全整数类checked,顺便就通读了标准中关于整数运算的部分,还发现了不少坑,以及自己没有了解的细节,这里就总结一下。要注意的是,C和C++在这部分的逻辑不相同

    第一步,单操作数的类型提升(integral promotion)

    • 对于C++中的所有字符类型,char,signed char,unsigned char,wchar_t,char16_t,char32_t,和所有小于等于int的类型,比如short,unsigned short,他们会被提升为对应的整数类型,这个对应的整数类型的选取逻辑是这样的:如果源类型的值域包含于int,那么就选择int,否则依次尝试unsigned int,long,unsigned long,long long,unsigned long long。
    • bool会被转换为int,其中false=0,true=1
    • bit field和enum不讨论

    所以这里要注意,整数类型提升不保证符号性不变,但是一定不会发生溢出

    用C++代码获取提升后的类型可以利用decltype和单目+运算符

    template<class T>
    using integral_promoted_type_t = decltype(+T{});
    

    第二步,双操作数的类型提升(usual arithmetic conversion)

    • 对于二元运算,经过了上面所述的类型提升,如果运算符左右边的类型仍不相同,那么会进行进一步的提升,使得两个操作数的类型相同,逻辑上基本就是小的类型向大的类型转换,标准引入了 conversion rank 的概念,也是为了说清楚什么是“小的类型”和“大的类型”。
    • 对于三目运算符,会对冒号两侧的操作数进行如上转换,逻辑不变
    • 移位运算符不进行本转换

    这个转换是造成整数表示溢出的罪魁祸首

    这个转换的结果类型可以用标准库设施 std::common_type 获取

    第三步,正式进行数学运算

    走过以上的类型提升步骤之后,左右操作数的类型都相同了,此时可以进行数学运算。

    • 有符号数运算溢出,是UB
    • 移位的右操作数是负数,是UB
    • 移位数>=左操作数的bit数,是UB
    • 有符号数左移,修改了符号位,是UB
    • 特别的,C++14中规定有符号左移是以相同二进制表示的无符号数进行左移,然后再转换回有符号,转换的行为依然是实现定义的
    • 有符号数右移,行为是实现定义的,用人话说就是,标准没有规定有符号数右移一定是符号扩展(但是目前所有的编译器和CPU实现都是符号扩展)
    • 有符号数进行位运算,修改了符号位,不算UB
    • A @= B等同于A = A @ B,遵循上面提到的类型提升过程
    • ++和--的运算过程等同于+=1和-=1,遵循上面提到的类型提升过程
    • bool++和++bool的结果是true,这个C++17删掉了
  • 相关阅读:
    LeetCode Power of Three
    LeetCode Nim Game
    LeetCode,ugly number
    LeetCode Binary Tree Paths
    LeetCode Word Pattern
    LeetCode Bulls and Cows
    LeeCode Odd Even Linked List
    LeetCode twoSum
    549. Binary Tree Longest Consecutive Sequence II
    113. Path Sum II
  • 原文地址:https://www.cnblogs.com/pointer-smq/p/6817227.html
Copyright © 2020-2023  润新知