Why does the C++ modulo operator return 0 for -1 % str.size()?

给你一囗甜甜゛ 提交于 2021-01-27 13:41:36

问题


I'm confused why the following code produces this output:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    int i = -1;
    string s = "abc";
    int j = s.size();
    int x = 1 % 3;
    int y = i % j; 
    int z = i % s.size(); 
    cout << s.size() << endl; // 3
    cout << x << endl;        // 1
    cout << y << endl;        // -1
    cout << z << endl;        // 0
}

Why is z = 0? Does it have to do with casting?


回答1:


So, stripping down your code to a minimal example, you're asking why this prints 0:

#include <iostream>
#include <string>

int main()
{
    int a = -1;
    std::string::size_type b = 3; 
    int c = a % b;
    std::cout << c << '\n';
}

The primary operation in question here is this:

a % b

Per the standard,

5.6 Multiplicative operators [expr.mul]

  1. The operands of * and / shall have arithmetic or unscoped enumeration type; the operands of % shall have integral or unscoped enumeration type. The usual arithmetic conversions are performed on the operands and determine the type of the result.

So.. what about those "usual arithmetic conversions"? This is to mate the types of the two operands to a common type prior to performing the actual operation. The following are considered in order :

  • If both operands are integers, integer promotion is first performed on both operands. If after integer promotion the operands still have different types, conversion continues as follows:
    • If one operand has an unsigned type T whose conversion rank is at least as high as that of the other operand’s type, then the other operand is converted to type T.
    • Otherwise, one operand has a signed type T whose conversion rank is higher than that of the other operand’s type. The other operand is converted to type T only if type T is capable of representing all values of its previous type.
    • Otherwise, both operands are converted to the unsigned type that corresponds to the signed type T.

That's a lot of legalize for what effectively says this:

  • You have two operands, a signed int and a std::string::size_type
  • The rank of std::string::size_type is greater than that of signed int
  • Therefore, the signed int operand is converted to type std::string:size_type prior to the operation being requested.

So all that is left is the conversion, to wit, there is one more piece of legalize:

4.7 Integral conversions [conv.integral]

  1. If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note]

That means, on a 32-bit std::string::size_type platform, you're going to get 232-1 as the converted value from int (-1).

Which means...

4294967295 % 3

Which is... zero. If std::string::size_type is 64-bits, then everything above stays the same, save for the final calculation, which would be:

18446744073709551615 % 3



回答2:


What really happens here:

int z = i % s.size();

is i is converted to size_t because the other side s.size() is size_t. And unlike int, size_t is unsigned; That is to say the value is interpreted as a positive number.

Check the output of this line:

std::cout << (size_t)-1 << std::endl;

to see what -1 has become.




回答3:


@GhaziMajdoub's answer is correct, but - why don't you let the compiler tell you what's happening?

Let's use the Flags to enable thorough and verbose g++ warnings ...

$ g++ -pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wctor-dtor-privacy \ 
-Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op -Wmissing-declarations \
-Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual \
-Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-null-sentinel \
-Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wno-unused -o a a.cpp
a.cpp: In function ‘int main()’:
a.cpp:12:13: error: conversion to ‘std::__cxx11::basic_string<char>::size_type’ {aka
‘long unsigned int’} from ‘int’ may change the sign of the result
[-Werror=sign-conversion]
   12 |     int z = i % s.size();
      |             ^
cc1plus: all warnings being treated as errors

a.cpp: In function ‘int main()’:
a.cpp:12:13: warning: conversion to ‘std::__cxx11::basic_string<char>::size_type’ 
{aka ‘long unsigned int’} from ‘int’ may change the sign of the result [-Wsign-
conversion]
   12 |     int z = i % s.size();
      |             ^

and there you have it: i is converted to long unsigned int, so it's no longer -1.



来源:https://stackoverflow.com/questions/61299317/why-does-the-c-modulo-operator-return-0-for-1-str-size

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!