打印

终于明白 a = (168*196) / 10 ; 为什么诡异

[复制链接]
楼主: 刘前辈
手机看帖
扫描二维码
随时随地手机跟帖
21
刘前辈|  楼主 | 2011-1-14 22:37 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
本帖最后由 刘前辈 于 2011-1-14 22:50 编辑

见《实用C语言详解》
        保无符号规则 (K&R  C标准)
描述
    对于整提升转换,在许多UNIX系统支持下实现的C编译系统中实现的是与ANSI C中保值规则有区别的保无符号规则(有人亦称保符号规则)。
    在运用这种规则时,如果某表达式一运算符两边的运算对象分别为unsiged short int(或unsiged char、枚举等)类型与:i}ned类型,那么就将前一运算分量的类型提升成unsigned int举型,同时再把后一运算分量的类型转移成unsigned int类型,然后再进行实际的运算。

  当某一表达式中一运算符的两个运算对象分别为unsigned int 类型与long int 类型时,可类似进行讨论。
  一般而言,当采用这种规则时,当一表达式中既有有符号对象又有无符号对象时,该表达式的计算结果就是无符号的。

    在许多情况下,保无符号规则与保值规则对表达式的执行结果而言并无什么区别。但是,在有些情况下,施用保无符号规则可能产生很奇怪的结果。例如,设usi为一unsigned short int类型的变量,si为一signed int 变量,再假定在计算表达式
use-st
时,二者的取值分别为2与3,那么这个表达式的计算结果不是-1(这是施用保值规则时求得的结果),而是一个很大的 unsignt int类型的值。

备注:
    若两个运算分量均为有符号的类型或均为无符号的类型,则施用这两种规则所产生的结果完全相同。
隶属词条
    类型转换,整提升
相关词条
    保值规则

                                保值规则 (ANSI  C 标准)

描述
    对于整提升,标准C (ANSI C)采用了保值规则,这种转换规则保持被提升类型对象的值不变。在使用这种规则时,如果某表达式一运算符两边的运算对象分别为unsigned short int(或,cosigned char、枚举)类型与singed int类型,那么,当unsigned short int(或unsigned char,枚举)类型的所有可能取值在signed int的取值范围内时,就将unsigned int(或unsigned char、枚举)类型转换成signed int类型;而当signed int类型的取值范围不包括被转换类型的取值范围时,就将之转换成unsigned int类型。当某表达式中一运算符的两运算对象分别为unsigned int类型与long int类型时,可类似进行讨论。
实例
    设有如下程序段:
        long int li;
        unsigned int ui;
        li=ui+li s
在其中的加法运算中,在实际加运算执行前.如果long int类型可以存放unsigned int类型的所有可能取值,那么就首先把ui提升转换成long int类型。然后进行长整数加法运算并把结果赋给变量1i如果long int类型
不足以表示ui所存放的值(注意到在某些C编译系统中int类型与long int类型占同样大小),那么就首先把ui与li 均转换成unsigned long int类型,然后再进行实际加法运算。
隶属词条
    类型转换,整提升
相关词条
    保无符号规则
****************************************************
所以,对于0xFF, ANSI C 标准编译成 -1;K&R C标准编译成255d 。
168*169=0x80A0;  ANSI C 标准编译器(保值规则)编译成 -32608d ; VC编译器(保无符号规则)编译为32928d 。

    具体采用什么规则是程序员的责任,……
    ……
    应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,这样就不必由编译器来选择结果的类型。
         ——摘自《C专家编程》


使用特权

评论回复
22
highgear| | 2011-1-15 00:07 | 只看该作者
"168*169=0x80A0;  ANSI C 标准编译器(保值规则)编译成 -32608d ; VC编译器(保无符号规则)编译为32928d 。"

老弟, 你又理解错了。
保无符号规则是对于两个操作(无符号与有符号)中明确以哪个为基准的问题, 以避免理解分歧, 如 168L * 169ul。如果不明确指定类型, 那么 168, 169 都会被默认为是signed int, 保无符号规则在此并不适用。 同样的168*169, 对于16bit integer (负整数:0x8000 to 0xFFFF) 编译器会产生溢出而变为负值, 32bit integer(负整数:0x80000000 to 0xFFFFFFFF) 则必须要超过0x7FFFFFFF. 去在VC中试试 65535*65535 /10000, 看看结果。

这样简单的道理, 需要我讲多少遍你才能理解?

不指定常数类型是最容易犯的,最隐蔽错误之一。一个整常数比如 1 可以是 char, short, long, 可以是有符号, 也可以是无符号, 甚至可以是指针, 浮点数据等等。如果不知道编译器的默认编译行为, 那么应明确的告诉编译器(使用cast, 强制)你所希望的行为。

使用特权

评论回复
23
johnwjl| | 2011-1-15 11:23 | 只看该作者
在编程中强制类型转换要很给力的执行,否则就会发生“a = (168*196) / 10 ; 为什么诡异”类似问题。

使用特权

评论回复
24
刘前辈|  楼主 | 2011-1-15 14:13 | 只看该作者

原来highgear 还懂保值规则和整提升?

本帖最后由 刘前辈 于 2011-1-15 19:43 编辑

22楼老H真逗,总跟在别人后面装懂,别人讲整提升,他也讲,别人讲保符号规则,他也“你们都不懂……”
     建议老highger下回不要跟在别人后面指责别人,别老“千年老二。” 跟在别人后面装懂,——中国人最大的毛病。谁率先发明了点什么:“这有什么,根本不懂,我早就……”  这种人的修养,让人厌恶。

       我就尊重“程序医人”,人家提的问题是好,无论有没有缺点。谁敢指责程序医人什么都不懂以显示自己大拿?那是自取其辱。昨天我在《C专家编程》相关问题的讲解上看到了“诡异”2个字。——Peter大师都以诡异形容此类问题,highgear 在这充什么象?大师讲不清楚的诡异问题,highgear 理解的清清楚楚?“这算什么诡异,你们都理解错了,我……”
      
以后最好率先提出问题,讲解问题。否则在人后面讲了半天,也不知道讲个啥。您老大人知不知道C编译器标准中“整提升”规则?如果不知道,就别在这瞎讲。您讲什么最好拿出例证:哪本书上、手册上、哪个国际标准上写的;—— 在这里,没人敢说“我就是标准”,“我理解的就是正确的”。这种人一定心理有病。自恋过度。

        记得您老大人前两天说这种问题“与C语言无关”,如今怎么又这么热心地讲C规则?不是无关么?——可别说 EC78/ 256d 和 168*196 2问题无关,都是编译器保值规则问题。

168*196=0x80A0; 这里的问题首先是编译器自动整提升之后的默认处理问题。(所有编译器自动整提升规则都是一致的,但是整提升后处理有差别。)您老大人能否讲清楚:为什么C51结果诡异,而franklin C 编译结果就是正确的=0x0CDC=3292d;  您去试试回来再说。

再请教:(-5000) / 256,0xEC78 /100 到底等于几? 您老人家告诉我=0xED; 哈哈,看来所有书上都写错了?还是咱们老highgear厉害。——可是,证据呢?嘴皮子说空话谁都会。

       您最好像我下面一样,列出个式子来,或者“以哪本书为证”。别在这毫无根据地瞎讲,什么算术移位,上下舍入……莫名其妙,——看来计算机还能进行10进制的计算,那咱这大学里听了半天教授讲二进制到10进制程序转换真是白练了。
       如果讲不清楚,或者连个二进制除法竖式都写不出来的话,那以前岂不都是信口胡说……



这还要算么?什么算术移位、逻辑移位,上下舍入,-19,-20,还要+0.5 补偿?天南地北 扯个什么劲。

有的人,不知道就说不知道。像丁肇中,复旦学生向他提了3个问题,他回答3个“不知道”,——“如果没有做过实验,任何说法都是对的。”

   有些人,什么实验都没做过,结果什么都知道。而且是门门学科精通。——别人都不懂:实验结果也不对。——你老人家懂,拿出个正确的实验结果来说话。别空喊:“你们都错啦,……”,有毛病?

   所以,建议highgear 下回争取在别人的前面讲课,别等别人都讲完了,最后跳出来指责别人,装大师,别人这也不对,那也不懂。您老人家懂,怎么不早点出来到2楼讲课也让咱们领教一下老人家的水平?那时说什么都晚了,只能被现在的年轻人笑话。

     再问问:0x EC58962F83D ÷ 0x11021 的余数?

哈哈,前两年硬着头皮吹了半天,“这还不容易,长除法我早就……,小菜!”   结果没一点动静,最后跑了……。这种人现在又出来充大师?2年过去,学会长除法没有?最好有点长进,也好让前辈的脸没地儿放,永不登录21IC才好。

纸上用竖式算啊。——计算机程序公式算出来无法验算正确性。连网上的程序计算结果都互不一样,也不知道哪个对。都觉得自己对,实际上大多不对。——也不知道自己先验证一下。其实只有纸上写的竖式才是唯一验证方法。可惜,highgear 研究了2年也没研究出个门道来。竟也有说话底气不足时候。

        、

使用特权

评论回复
25
highgear| | 2011-1-15 22:39 | 只看该作者
哈哈哈哈, 刘公公/冷漠又恼羞成怒了?正是一点器量都没有,还怎么“必成大器”。 Peter大师都以诡异形容此类问题,并不意味着peter不清楚,也不意味着所有人也都不应该清楚。了解编译器的基本行为是一个程序员所应该具备的基本功之一。 24楼欠缺起码的逻辑条理性,我认为我以及其他大虾已经讲得很清楚了,刘公公/冷漠的理解能力如果不是那么差的话,那么就是像以往那样再硬装。不过从24楼的0xEC78 /100,显然是刘公公/冷漠的iq问题, 虽然大家把问题关键之处提了很多次

16bit整数无符号:0xEC78 /100
16bit整数有符号:0xEC78 /100
32bit整数无符号:0xEC78 /100
32bit整数有符号:0xEC78 /100

技术问题,我就不给你讲解了, 让你变聪明并不是我的义务, 虽然我以后还会“指导”你, 那会比解答问题更有意思, 而且, 我说过, 而且不会引起其他人的反感。

使用特权

评论回复
26
highgear| | 2011-1-15 22:56 | 只看该作者
刘公公/冷漠把楼上的四个算式想明白了,再回来看看自己24楼的发言。

使用特权

评论回复
27
effx| | 2011-1-15 23:33 | 只看该作者
TH0 = (- 5000 ) / 256 ;      // 为什么会等于0x ED !
a = (168*196) / 10 ;          // 为什么会等于0xF344!
-5000对应二进制0xEC78,0xEC78对应的整数是0x1388,0x1388/256=0x13,-0x13的二进制 补码是0xED。
168*196等于0x80A0,那么0x80A0对应的正数是0x7F60,0x7F60/A=0xCBC,-0xCBC的二进制补码是0xF344。

使用特权

评论回复
28
刘前辈|  楼主 | 2011-1-16 21:40 | 只看该作者
本帖最后由 刘前辈 于 2011-1-17 11:06 编辑
25楼
16bit整数无符号:0xEC78 /100
16bit整数有符号:0xEC78 /100


老highgear 的水平是越来越退步了,光知道这么点高中生的水平。还当是“农民讲习所”给初学者入门呢。我给你讲点你没想过的知识,讲习所教材上从来没有的东西,——没研究过,当然写不出来了:
注意:
一个16位二进制所表达的数0xEC78,它所代表的值是唯一的!——就是0xEC78,(注意我在说二进制。)什么有符号、无符号?我只有16位unsigned  int , 符号位往哪放?符号位=0要考虑么?
二进制表达式中:
    65536-5000=60536 = - 5000 = 0xEC78; // highgear 若是早这么讲课,我也无地自容了。
所以: TH0=(65536 - 5000)/ 256 ;
    与      TH0=  -5000 /256 ;

完全等效!都等于0xEC !您老那个-5000/256=0xED ; 按照我上面的理论推导,无法自圆其说。 有本事就指出我上面的推导错在哪里。我是书上抄来的,不是我自己创新。但是推导是我自己证明的。呵呵,你老水平高,也证明一个给咱看看。也好让咱前辈尴尬一把;别老在这扯着 嗓子空喊自己高明。否则,尴尬的就不是前辈我啦。嘟嘟囔囔不知道要说啥,还硬撑着。

     老highgear 过去在论坛上自吹:“俺16岁上大学,差点去了少年班……”这种事也值得吹?真的假的?我怎么听着只感觉这人不正常。
     可惜了这少年天才一生也没怎么样,如今天命之年已过,开始走下坡路了,还在这跟我们这帮菜鸟里讲课。中国少年班的可是都出了国,除了几个(好像2个)患心理疾病,经常发作的,如今都是国外博士教授什么的了。——也难怪highgear 老人家一看见谁出国就眼睛发绿。一通粗口:“败类。”——孔乙己?如今还养成了处处跟小辈比较,习惯以贬低学生来抬高自己的臭毛病,屡次提醒,注意形象;现在又开始犯了。
    唉,一生不得志。看谁都不顺眼。

早像我下面讲点对路的,讲习所也不至于关门:
168*196 /10 一个道理。这么简单的问题就给问住了,还东扯西扯,现在也不+0.5 啦?也不算术移位了?也不弱信号处理啦?又跟着我讲保符号还是保值了。你到底想说什么?最好把你的误差理论**到底,讲透彻了,再联系到拉普拉斯变换傅里叶级数也行,也显示一下你少年班的才能,自己讲自己的创意思想,别老看着我,思维也别老转变跟着我跑。——我下面又要讲整提升了。又当了一回第一,—— 老highgear 又要跟在我后面思维转向啦:“你理解错啦,我才是…………

******************************************************************
                              整提升
描述
    整提升是指把表达式中类型为char,short int、枚举、位段的运算对象转换成int类型或long int类型,或把int类型的运算对象转换成unsigned  int或者 long int类型。
    当把char,short int、枚举、位段这些较小的类型的对象用在该用int类型的地方时,就自动进行这种形式的提升.如果int类型可以存放该较小类型的所有可能取值,那么该较小类型就提升成int类型;如果int类型不能存放该较小类型的所有可能取值,那么该较小类型就提升为unsigned int类型,这种转换规则保待被提升类型的值不变(包括值的符号)所以称其为保值.在许多已实现的C编译系统中采用了一种叫做保无符号的提升模式。在该模式下,较小的无符号类型总是提升成较大的无符号类型。在某些情况下,这两种提升模式对程序执行会有不同影响.隶属词条
    类型转换,整类型
下属词条
    保无符号规则,保值规则




                                         结 贴 !



使用特权

评论回复
29
john_light| | 2011-1-17 09:14 | 只看该作者
看到很多字,我就激动地回帖了。

使用特权

评论回复
30
STM32W108| | 2011-1-17 13:25 | 只看该作者
本帖最后由 STM32W108 于 2011-1-17 13:31 编辑

帖子看到这里,整型提升的概念都还没搞明白


If an int can represent all values of the original type, the value is converted to an int;otherwise, it is converted to an unsigned int.These are called the integer promotions。

使用特权

评论回复
31
刘前辈|  楼主 | 2011-1-17 17:29 | 只看该作者

若说错了,盼望举例指正。

本帖最后由 刘前辈 于 2011-1-17 17:35 编辑

如果a=|(uchar)X * (uchar)Y|< 32768d(0x7FFF),那么a 就是int;——2个uchar相乘不可能超过16位。若是2个int相乘,自动扩大为long /unsigned long。
如果a=|X*Y|> 32768d(0x7FFF),那么a 就自动放大一倍,成为unsigned int ,符号位被占用为有效数值。问题就是自动整提升之后的符号位保留。
   1、若结果a为正,当然没什么问题了。 168*196=0x80A0=32928d。采用保值规则的编译器会得到这个结果,忽略符号位。
   2、而采用ANSI C标准的编译器采用保符号规则,把结果a 处理为负数,
      0x80A0= -0x7F60=-32608;
2个结果其实都是相等的一样的。就像0xFF=-1 ;二进制中都是一个点0xFF。
  ∵    32928+32608=65536=0x10000.
  ∴    32928= -32608;
   但是     32928/10≠ -32608/10.  二进制里这是2个点了。


/

使用特权

评论回复
32
STM32W108| | 2011-1-17 18:24 | 只看该作者
本帖最后由 STM32W108 于 2011-1-17 18:30 编辑
若是2个int相乘,自动扩大为long /unsigned long

刘前辈 发表于 2011-1-17 17:29


胡扯。
int 与int相乘,结果只能是int,不可能是其他类型。

整型提升是指不满int的整型,在运算之前自动转换成int或者unsigned int类型。

比如说:
char a,b;
a+b;
a是char型,在运算之前进行整形提升至int型
b是char型,在运算之前进行整形提升至int型
最后结果是int型.

signed char a;
unsigned char b;

a是signed char型,在运算之前进行整形提升至int型
b是unsigned char型,在运算之前进行整形提升至int型
最后结果是int型.

int a,b;
a+b;
a是int型,
b是int型,
最后结果是int型.

int a;
unsigned int b;
a+b;
a是int型,
b是unsigned int型,
根据C语言标准,通用算术转换规则,a将转换成unsigned int型,再参与运算
最后结果是unsigned int型.
(这里不叫整型提升)

=,-,*,/都一样。

使用特权

评论回复
33
刘前辈|  楼主 | 2011-1-17 20:01 | 只看该作者
明白一些了。
int 与int相乘,结果只能是int,不可能是其他类型。


是不是应该这样说,“2个char类型操作数经过整提升之后的int 与int 相乘,结果……”。
否则不好理解了,例如:
0x7FFF*0x7FFF=107367289d=0x3FFF0001;  //32位

还有:请教下面:
0x00A8*0x00C4=0x80A0 ;   

您说的168*196 = -0x80A0;     
不好理解了。

使用特权

评论回复
34
刘前辈|  楼主 | 2011-1-17 20:14 | 只看该作者

不至于为什么要整型提升都不知道吧。

本帖最后由 刘前辈 于 2011-1-17 20:28 编辑

32楼
     若是2个int相乘,自动扩大为long /unsigned long。

绝对正确!谁在胡扯?

根据32楼“整型提升是指不满int的整型,在运算之前自动转换成int或者unsigned int类型。”

根据整提升原理类推:“整型提升是指不满long 的int 整型操作数,在运算之前自动转换成long 或者unsigned long类型。”(因为 int*int =long ! 别急于否定书本。)

呵呵,以书为证,谁在胡扯!——要不要我把书拿出来?
很简单的道理: int * int  =long ;   
谁说的  int*int = int ;    16位*16位=32位 ;很简单的道理。 应该把为什么要整提升原因讲清楚。

本来大侠不应该出错的。结果让我改变了对32楼的看法。

——原来大侠也不一定总是正确。——连 8位*8位=16位;看来也是胡扯?

   跟我一样都不太清楚。比我好一点。





使用特权

评论回复
35
流行音乐| | 2011-1-17 21:10 | 只看该作者
int*int=int,这是 C 的标准,没错。

使用特权

评论回复
36
STM32W108| | 2011-1-17 21:51 | 只看该作者
本帖最后由 STM32W108 于 2011-1-17 22:06 编辑

根据整提升原理类推:“整型提升是指不满long 的int 整型操作数,在运算之前自动转换成long  
----------------------------------------------------------
又是胡扯。

C99标准整型提升定义:

If an int can represent all values of the original type, the value is converted to an int;otherwise, it is converted to an unsigned int.These are called the integer promotions。

使用特权

评论回复
37
aihe| | 2011-1-17 23:49 | 只看该作者
再说,我把书拿出来砸你

使用特权

评论回复
38
刘前辈|  楼主 | 2011-1-18 10:14 | 只看该作者

C51 不是 C99。

本帖最后由 刘前辈 于 2011-1-18 10:48 编辑

36楼还真是没明白C运算为什么要先对运算符两边的操作数进行整提升。最好还是先弄明白原理和C标准修改历史及原因,再来看谁在“胡扯”。

先劝36一句,今年没过35岁吧?本科学历吧?所以,建议:如果想做研究,(不想给人打工),那么:

1、闷头泡2年图书馆。把自己想做的研究领域的所有新旧历史资料看遍。保证你越看越觉得没资格说任何人“胡扯”,说漏嘴了反而显得自己知识面太窄。——就像我,本来认为你是高手,现在看来,很多东西不知道……当然我也一样,所以我从不说任何人“胡扯”。

2、不光知道C99,还知道C99出台原因,它修补了K&R C 或者旧版ANSI C的什么漏洞。
   照我看,这些漏洞是随着计算机高速发展才显现出来的,而C51标准没怎么修改,没什么漏洞(除非51发展到16位,C51就要修改了。)——那么36楼拿着32/64位PC机器最新标准在8位嵌入式机器上说什么事?如今嘴里嚼着洋快餐,就说中国的窝头是猪食?——我就是这感觉。

3、C99是以前旧标准上的修改标准,34楼最好把旧版原版标准看看:别说什么人证明了爱因斯坦什么理论的不足,就说爱因斯坦胡扯,就自己成了爱因斯坦。——旧的发明不可能适合永久未来,但是第一发明就是了不起,哪怕是有缺陷的。跟着后面修改的,永远都是模仿者。没资格说前人——发明者胡扯。

贴一个旧标准:(C51参考标准)


                                       整提升

描述

整提升是指把表达式中类型为char,short int、枚举、位段的运算对象转换成int类型
或long int类型,或把int类型的运算对象转换成unsigned  int 或者 long int 类型。




C99的修改或者说改进,不过是把后面 int 运算对象的内容拿出来放到“类型隐式自动转换”里去了,功能一样,叫法不同,更严格说得过去而已。


(否则未来出现更高的数据类型,又要改了。做标准的人整天就是没事干,显得他们做了一些事。这是我胡扯,不过最好讲清楚自己的高论。)



使用特权

评论回复
39
流行音乐| | 2011-1-18 11:32 | 只看该作者
我验证过了,C51 也是符合 int*int=int 这个规律的,而不是刘前辈说的 ini*int=long。

使用特权

评论回复
40
STM32W108| | 2011-1-18 11:51 | 只看该作者
本帖最后由 STM32W108 于 2011-1-18 12:11 编辑
36楼还真是没明白C运算为什么要先对运算符两边的操作数进行整提升。最好还是先弄明白原理和C标准修改历史及原因,再来看谁在“胡扯”。

先劝36一句,今年没过35岁吧?本科学历吧?所以,建议:如果想做研究,(不想 ...
刘前辈 发表于 2011-1-18 10:14


完全胡扯。

楼主犯错误:
1.没有真真去查看标准。不管是C89,C99,还是C51。
2.楼主根本没理解整型提升的规则,适用范围。
3.楼主由char * char = int ,想当然得出int *int =long ,更加错误。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则