如何在size_t上用余数运算符得到负余数
How to get negative remainder with remainder operator on size_t?
考虑以下代码示例:
#include <iostream>
#include <string>
int main()
{
std::string str("someString"); // length 10
int num = -11;
std::cout << num % str.length() << std::endl;
}
在http://cpp.sh上运行此代码,我得到5
作为结果,而我期望它是-1
。
我知道这是因为str.length()
的类型是size_t
,这是一个依赖于实现的无符号类型,并且因为使用二进制操作符发生的隐式类型转换导致num
从signed int
转换为无符号size_t
(更多信息在这里);这将导致负值变为正值,并使操作结果混乱。
可以考虑通过显式强制转换到int
来解决这个问题:
num % (int)str.length()
这可能有效,但不能保证,例如在长度大于int
最大值的字符串的情况下。可以使用更大的类型来降低风险,比如long long
,但是如果size_t
是unsigned long long
呢?同样的问题。
您将如何以可移植和健壮的方式解决这个问题?
从c++ 11开始,您可以将length
的结果强制转换为std::string::difference_type
。
解决"但是如果尺寸太大怎么办?":
这不会发生在64位平台上,即使你在一个较小的平台上:你最后一次有一个字符串占用了总内存的一半以上是什么时候?除非你正在做非常具体的事情(你会知道),使用difference_type
是很好的;别打鬼了。
或者,只使用int64_t
,这当然足够大了。(虽然可能在一些32位处理器上循环一个比int32_t
慢,我不知道。对于单个模数运算来说,这并不重要)
在c++ 11之前,带有负值的%
符号是实现定义的,对于定义良好的行为,使用std::div
加上上面描述的强制转换之一。
我们知道
-a % b == -(a % b)
你可以这样写:
template<typename T, typename T2>
constexpr T safeModulo(T a, T2 b)
{
return (a >= 0 ? 1 : -1) * static_cast<T>(std::llabs(a) % b);
}
这在99.98%的情况下都不会溢出,因为考虑这个
safeModulo(num, str.length());
如果std::size_t
被实现为unsigned long long
,则T2 -> unsigned long long
和T -> int
。
正如评论中指出的那样,使用std::llabs
而不是std::abs
是很重要的,因为如果a
是int
的最小可能值,那么移除标志将会溢出。在此之前将a
提升为long long
不会导致此问题,因为long long
的值范围更大。
现在static_cast<int>(std::llabs(a) % b)
将总是产生一个小于a
的值,所以将其转换为int
将永远不会溢出/下溢。即使a
被提升为unsigned long long
,也没关系,因为a
已经从std::llabs(a)
"unsigned"了,所以值是不变的(即没有溢出/下溢)。
由于上述属性,如果a
是负的,将结果与-1
相乘,得到正确的结果。
导致未定义行为的唯一情况是当a
是std::numeric_limits<long long>::min()
时,因为删除符号溢出a
,导致未定义行为。可能还有另一种方法来实现这个函数,我会考虑的
- 为什么比较运算符如此快速
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 增量运算符与后缀混淆
- 一个关于在C++中重载布尔运算符的问题
- 运算符C++ "delete []"仅删除 2 个前值
- 模板类无法识别友元运算符
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 关闭||运算符优化
- 通过继承类使用来自不同命名空间的运算符
- C++Cast运算符过载
- 如何使用AngelScript注册SFML Vector2运算符
- 重载元组索引运算符-C++
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 余数运算符的等效操作,用于处理低于允许的最小值
- 在标准(C++11)中,有人说余数运算符只适用于积分类型
- 仅使用算术运算符获得余数的商
- 如何在size_t上用余数运算符得到负余数