负数的整除和余数

刚试了一下负数的整数除法和余数,发现有两种结果。

  • 一种是令余数和被除数的符号一致,例如 C / C++ (网上说还有 java):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <cstdio>
    #include <cstdlib>
    int main()
    {
    #define CALC(x, y) \
    fprintf(stdout, "(%+d) / (%+d) = %+d; ", x, y, (x) / (y)); \
    fprintf(stdout, "(%+d) %% (%+d) = %+d\n", x, y, (x) % (y));
    CALC(+5, +2);
    CALC(+5, -2);
    CALC(-5, +2);
    CALC(-5, -2);
    #undef CALC
    return 0;
    }
    -->
    (+5) / (+2) = +2; (+5) % (+2) = +1
    (+5) / (-2) = -2; (+5) % (-2) = +1
    (-5) / (+2) = -2; (-5) % (+2) = -1
    (-5) / (-2) = +2; (-5) % (-2) = -1
  • 另一种是令余数和除数的符号一致,例如 python、matlab:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def calc(x, y):
    print("({:+d}) // ({:+d}) = {:+d}; ({:+d}) % ({:+d}) = {:+d}" \
    .format(x, y, (x) // (y), x, y, (x) % (y)))
    calc(+5, +2)
    calc(+5, -2)
    calc(-5, +2)
    calc(-5, -2)
    -->
    (+5) // (+2) = +2; (+5) % (+2) = +1
    (+5) // (-2) = -3; (+5) % (-2) = -1
    (-5) // (+2) = -3; (-5) % (+2) = +1
    (-5) // (-2) = +2; (-5) % (-2) = -1

好吧,知道即可,不纠结。

C++ 原生字符串

引发原生字符串标识提议的是这样一个“惊天地泣鬼神”的例子:

1
2
"('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|" 
// 这五个反斜杠是否正确?即使是专家,也很容易被这么多反斜杠搞得晕头转向

C++ 整型变量的绝对值

今天在 CppTemplateTutorial 群里面问了一个问题,为什么 C++ 里面的整型绝对值函数 abs 返回有符号类型而不是无符号类型,如 int abs(int) 而不是 unsigned abs(int),因为这样会出现 abs(INT_MIN) 不对的情况。后来给出的两个说法比较容易接受:

1
2
3
int b, a;
a = abs(a); // 1 使得这里可以直接赋值而不用转换
abs(a) - abs(b); // 2 使得这里可以得到想要的正负数

不过后来讨论偏了,引来了一个讨论:函数 abs 中是否需要判断?结果还真有这种操作。看 abs 的反汇编,还有 MFC 里面的 abs 实现,都用了下面这种方式(以 int 为例):

1
2
3
4
5
int abs(int x)
{
unsigned mask = (unsigned)(x >> (sizeof(int)* CHAR_BIT - 1)); // CHAR_BIT = 8
return (int)(((unsigned)x ^ mask) - mask);
}

第一行的 mask 全部由 x 的符号位组成,对应于正数和负数分别为全 0 (0x00000000) 和全 1 (0xffffffff);第二步中的 x ^ mask,对于正数,仍是 x,对于负数,结果是 ~x;第三步的 -mask,对于正数,因为 mask = 0 所以不变,对于负数,无符号数减去最大的无符号数等于 +1,即 x - 0xfffffffff = x + 1u,所以上述函数对于正数等价于 return x,对于负数等价于 ~(unsigned)(x) + 1u,在 x != INT_MIN 时正好等价于 (unsigned)(~x) + 1u,按预期返回。

下面是一个实例代码,用来看各种中间结果。简便起见,这里使用 char 而不是 int

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <cstdio>
typedef unsigned char uchar;
int main()
{
char x = -25; // 25
uchar ux = (uchar)x;
uchar mask = (uchar)(x >> (sizeof(char) * 8 - 1));
uchar dx = ux ^ mask;
uchar ddx = dx - mask;
char ax = (char)ddx;
#define pr(x, s) printf(#x ": %02hhX, " #s " ", x, x)
pr(x, %+04hhd);
pr(ux, %03hhu); pr(mask, %03hhu); pr(dx, %03hhu); pr(ddx, %03hhu);
pr(ax, %+04hhd);
#undef pr
return 0;
}

下面为当 x 分别取 25 和 -25 时的输出:

1
2
x: 19, +025    ux: 19, 025    mask: 00, 000    dx: 19, 025    ddx: 19, 025    ax: 19, +025
x: E7, -025 ux: E7, 231 mask: FF, 255 dx: 18, 024 ddx: 19, 025 ax: 19, +025

最后,可以调侃一下。在 C++ 中,对于有符号整型变量 xx == (-x) 有两个解:0TYPE_MIN