你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

你知道吗?C语言中3/-2=?3%-2=?

[复制链接]
gaosmile 发布时间:2020-5-19 13:43
抛砖引玉
& B7 n4 T7 ^$ Y) l+ j; b5 \
2 X0 v0 Y  x0 E; ]5 A
( X+ ?" [7 m* ?7 v* x
C语言负数除以正数,与正数除以负数或者负数除以负数的余数和商,正负有谁定呢?
-3 / 2 = ?;
- O: U& v6 Z  K* r4 ]; W, ]/ }-3 % 2 = ?;/ [/ f; p. t1 S
3 / (-2) = ?;
" f  g: ~/ c/ y3 g3 % (-2) = ?;% e+ g2 H7 V: S7 z+ ]0 Z8 X
(-3) / (-2) = ?;
1 Z3 c& e4 Y2 X; U: h! x5 B/ y
(-3) % (-2) = ?;
) i. d% b: ]$ ^: N! r# l
" j: [8 I  V" N' \3 M8 M+ {前提假设
, ]3 i' S: _! w% S
9 I6 F7 W8 o" H
  x$ g" X$ c# |- Z. T
假定我们让 a 除以 b,商为 q,余数为 r:q = a / b;r = a % b;这里,不妨假定 b 大于 0。我们希望 a、b、q、r 之间维持怎样的关系呢?
  • 最重的一点,我们希望 q * b + r == a,因为这是定义余数的关系。
  • 如果我们改变 a 的正负号,我们希望这会改变 q 的符号,但这不会改变 q  的绝对值。
  • 当 b>0 时,我们希望保证 r >= 0 且 r < b。

    ) k0 f( ?. R. p4 \, b$ ^( ~: l
例如,如果余数用于哈希表的索引,确保它是一个有效的索引值很重 。这三条性质是我们认为整数除法和余数操作所应该具备的。很不幸的是,它们不可能同时成立。

0 u9 B( W* {9 j# }! P
举例说明+ l# R4 S; P8 Y

# H1 y9 H3 P% E

; A2 m/ ]7 P9 p
考虑一个简单的例子:3/2,商为1,余数也为1。此时,第1条性质得到了满足。(-3)/2 的值应该是多少呢?如果满足第2条性质,答案应该是-1,但如果是这样,余数就必定是-1,这样第3条性质就无法满足了。如果我们首先满足第3条性质,即余数是 1,这种情况下根据第1条性质则商是-2,那么第2条性质又无法满足了。
因此,C语言或者其他语言在实现整数除法截断运算时,必须放弃上述三条原则中的至少一条。大多数程序设计语言选择了放弃第 3 条,而改为求余数与被除数的正负号相同。这样,性质1和性质2就可以得到满足。大多数C编译器在实践中也都是这样做的。
然而,C语言的定义只保证了性质1,以及当 a>=0 且 b>0 时,保证|r| < |b|以及 r>=0。后面部分的保证与性质2 或者性质3 比较起来,限制性弱得多。
: V; ?2 {, P* w& Z$ u5 [
实例论证
% E. H' {) o2 L8 N4 o/ A9 s$ H: B: p
5 O( x# X% m$ F5 ?
C 语言的定义虽然有时候会带来不需的灵活性,但大多数时候,只要编程者清楚地知道要做什么、该做什么,这个定义对让整数除法运算满足其需要来说还是够用了的。例如,
假定我们有一个数 n,它代表标识符中的字符经过某种函数运算后的结果,我们希望通过除 法运算得到哈希表的条目 h,满足 0<=h<HASHSIZE。又如果己知 n 恒为非负,那么我们只需要像下面一样简单地写:
h=n%HASHSIZE:
9 o' }( i0 w( ]% ~: G( t
然而,如果 n 有可能为负数,而此时 h 也有可能为负,那么这样做就不一定总是合适的了。不过,我们已知 h>-HASHSIZE,因此我们可以这样写:
h = n % HASHSIZE;" ?" Y, u1 S+ e' {$ ^5 Q
if(n < 0)$ R, J/ ]* z' Q" G# f
  h += HASHSIZE;
5 M) e5 P: c& C- e& N9 e/ P% H% ~$ f+ a5 K: F7 R
测试代码:9 |# Z! x3 ?' H1 c6 I% }

, O) {  q9 [5 s, L

% k$ W: N# h$ p( H* Q" U#include <stdio.h>
, n: v+ e4 c# g

0 F8 g( e7 H1 Y, Y9 |main()
( D# J8 m4 B% W$ K{
( j! h8 l  `4 j0 H  _- o& N  M     int a=-3, b=2,c=3,d=-2;# F! N( `$ B4 F9 W
     int q,r,m,n,x,y;2 ?# h2 Y+ N. f
     q = a / b;
# w9 W$ Y4 f' y; U# u: O     r = a % b;: v8 G3 q) {6 X% W9 L
     m = c / d;
7 d' t- d: J, ^, V: j     n = c % d;. B; b9 S* e: s! ~$ w( D
     x = a / d;4 o% l& b" O6 t" u) G2 `# u8 [
     y = a % d;
7 i+ |8 k+ H9 N3 q' c8 a% z* `" b     printf("q=%d, r=%d\n" , q , r);
' a; B) C: H3 c4 l8 o( h- J; u+ b     printf("m=%d, n=%d\n" , m , n);
! R* [0 i+ y1 l7 G2 G& i     printf("x=%d, y=%d\n" , x , y);
* F7 h8 D5 G( e& y5 O% r
}
- K3 F: Z  M1 p( y$ I6 {7 S2 _9 ~3 B$ f; P7 ^
最终结论
$ T6 P/ I9 \/ r, \
1 V$ i. p7 V6 }( r% G5 }; E( C- }

4 M8 v+ X3 p+ n4 c9 N
翻阅资料得出,这个问题在C语言早期是没有固定规定的,所以一些书中会有谁这种行为值不固定,是编译器而内决定,但是现在C99中有强制规定了,要求对容于整型数a,b,必然满足 a%b==a-(a/b)b,如果第一操作数为负,则得到的模为负;如果第一操作数为正,则得到的模为正
所以遇到这样的问题一般计算的方法是:余数与被除数(即分子的符号)相同;先将各个带符号的数全部取正值再做除法,再根据负号的个数确定商的符号

+ V/ w- F. q) U( L, S
注意的点2 L( ], `' ^# O/ {+ s1 J, k2 U5 J) U
) }% \/ b" X6 h4 y1 V
8 A5 P# ~1 D+ h
当然在实际的项目中,更好的做法是,程序在设计时就应该避免 n 的值为负这样的情形,并且声明 n 为无符号数。

0 D8 n3 }" J1 Z+ n( W& A  c; k
赞 收藏 评论0 发布时间:2020-5-19 13:43

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版