合計 昨日 今日

アバター
Dixq (管理人)
管理人
 
記事: 1554
登録日時: 2010年10月12日(火) 20:16
お住まい: 北海道札幌市
日記: 日記を見る (570)
日記
- 12月 2017
ボーナスで自社製品を買う (2)
   2017年12月10日(日) 20:17
玉藤製と自宅製 (9)
   2017年12月03日(日) 22:01

+ 11月 2017
+ 10月 2017
+ 9月 2017
+ 8月 2017
+ 7月 2017
+ 6月 2017
+ 5月 2017
+ 4月 2017
+ 3月 2017
+ 2月 2017
+ 1月 2017
+ 12月 2016
+ 11月 2016
+ 10月 2016
+ 9月 2016
+ 8月 2016
+ 7月 2016
+ 6月 2016
+ 5月 2016
+ 4月 2016
+ 3月 2016
+ 2月 2016
+ 1月 2016
+ 12月 2015
+ 11月 2015
+ 10月 2015
+ 9月 2015
+ 8月 2015
+ 7月 2015
+ 6月 2015
+ 5月 2015
+ 4月 2015
+ 3月 2015
+ 2月 2015
+ 1月 2015
+ 12月 2014
+ 11月 2014
+ 10月 2014
+ 9月 2014
+ 8月 2014
+ 7月 2014
+ 6月 2014
+ 5月 2014
+ 4月 2014
+ 3月 2014
+ 2月 2014
+ 1月 2014
+ 12月 2013
+ 11月 2013
+ 10月 2013
+ 9月 2013
+ 8月 2013
+ 7月 2013
+ 6月 2013
+ 5月 2013
+ 4月 2013
+ 3月 2013
+ 2月 2013
+ 1月 2013
+ 12月 2012
+ 11月 2012
+ 10月 2012
+ 9月 2012
+ 8月 2012
+ 7月 2012
+ 6月 2012
+ 5月 2012
+ 4月 2012
+ 3月 2012
+ 2月 2012
+ 1月 2012
+ 12月 2011
+ 11月 2011
+ 10月 2011
+ 9月 2011
+ 8月 2011
+ 7月 2011
+ 6月 2011
+ 5月 2011
+ 4月 2011
+ 3月 2011
+ 2月 2011
+ 1月 2011
+ 12月 2010
+ 11月 2010
+ 10月 2010
フォロー
カテゴリー
日常
1 記事

誰か教えて・・ [解決!]

パーマリンクby Dixq (管理人) on 2011年3月17日(木) 22:17

今日計算している時に奇妙な現象に出会いました。

-1 × 10 ÷ 2

っていくらでしょう。
-5ですよね。
でも

コード[C++]: 全て選択
1
2
3
        unsigned int u = 10;
        int s1 = -1, s2 = 2;
        printf("%d\n", s1 * u / s2 );

出力結果:「2147483643」

になるんです。なんじゃこりゃ?
でも21億という数値はプログラマーならなじみのある数値。
符号ビットがおかしいのか、unsigned intと掛け算したから、unsignedに引っ張られたのかな。
なら、掛け算の時点で既におかしい値になってるはずだね。

コード[C++]: 全て選択
1
       printf("%d\n", s1 * u      ); // = -10

出力結果:「-10」

エ・・。正しい・・。
何?掛け算が終わった時点では、signedなの?
signedをsignedで割って何故unsignedになる・・。

まぁ最後s2の計算をするとunsignedになっちゃうだろう。
掛け算に変えて試してみよう。きっと21変な数になるはずだ。

コード[C++]: 全て選択
1
        printf("%d\n", s1 * u * s2 );

出力結果:「-20」

正しい・・。
意味わからん・・。
s2をマイナスにしたらどうなるんだ?

コード[C++]: 全て選択
1
        printf("%d\n", s1 * u / s2 );

出力結果:「0」

な・・!
何故0・・・。
もう分けがわかりませんorz

VisualC++、Ubuntu gcc、ARMで起こりましたが、アセンブラの振る舞いは異なりそうでした。
未定義動作ですかね。
明日アセンブラコード読んでみますか・・。
誰か原因知ってたら教えて下さい・・。

↓まとめ

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
int main(){
        unsigned int u = 10;
        int s1 = -1, s2 = 2;
        printf("%d\n", s1 * u / s2 ); // = 2147483643
        printf("%d\n", s1 * u      ); // = -10
        printf("%d\n",      u / s2 ); // = 5
        s2 = -2;      
        printf("%d\n", s1 * u / s2 ); // = 0
        printf("%d\n", s1 * u      ); // = -10
        printf("%d\n",      u / s2 ); // = 0
        return 0;
}


※unsignedとsignedをキャスト無しで混合させる事自体間違ってますが、まぁそれは承知の上ということで・・。
最後に編集したユーザー Dixq (管理人) [ 2011年3月17日(木) 23:02 ], 累計 4 回

コメント数: 4 閲覧数: 25048
コメント

RE: 誰か教えて・・

パーマリンクby a5ua on 2011年3月17日(木) 22:47

intとunsigned intの演算ではunsigned intとして計算されるからだと思います。
全て16進数表現のまま計算し、最後にintに変換すると以下のようになります。

・s1 * u / s2
0xffffffff * 0x0000000a = 0xfffffff6 (オーバーフロー)
0xfffffff6 / 0x00000002 = 0x7ffffffb
(int)07fffffb = 2147483643

・s1 * u
0xffffffff * 0x0000000a = 0xfffffff6
(int)0xfffffff6 = -10

・u / s2
0x0000000a / 0x00000002 = 0x00000005
(int)0x00000005 = 5

s2 = -2

・s1 * u / s2
0xffffffff * 0x0000000a = 0xfffffff6
0xfffffff6 / 0xfffffffe = 0x00000000 (切捨て)
(int)0x00000000 = 0

・s1 * u
(略)

・u / s2
0x0000000a / 0xfffffffe = 0x00000000 (切捨て)
最後に編集したユーザー a5ua [ 2011年3月17日(木) 23:18 ], 累計 1 回
アバター
a5ua
 
記事: 194
登録日時: 2010年10月17日(日) 17:52
日記: 日記を見る (14)

Re: 誰か教えて・・

パーマリンクby Dixq (管理人) on 2011年3月17日(木) 22:51

※修正

> GRAMさん
0になるのは「10/巨大な符号無し整数」だと解釈してるからでは?というご意見頂きました。なるほど。

> a5uaさん

なるほど確かにそうなりますね・・。
割り算をすると大きな数にならなかったのは符号ビットが外れたからですね。
0になる理由も解りました。
こんなにすぐ答えが頂けるとは思いませんでした^^;
ありがとうございます。
最後に編集したユーザー Dixq (管理人) [ 2011年3月17日(木) 23:01 ], 累計 1 回
アバター
Dixq (管理人)
管理人
 
記事: 1554
登録日時: 2010年10月12日(火) 20:16
お住まい: 北海道札幌市
日記: 日記を見る (570)

Re: 誰か教えて・・

パーマリンクby Dixq (管理人) on 2011年3月17日(木) 23:05

解決マーク付けておきます^^;
[解決!]
最後に編集したユーザー Dixq (管理人) [ 2011年3月17日(木) 23:33 ], 累計 1 回
アバター
Dixq (管理人)
管理人
 
記事: 1554
登録日時: 2010年10月12日(火) 20:16
お住まい: 北海道札幌市
日記: 日記を見る (570)

Re: 誰か教えて・・

パーマリンクby ISLe on 2011年3月17日(木) 23:38

s1 * u はunsigned int型で4294967286という値になります。
%dで-10と表示されるのはムリヤリ内部形式をsigned intとみなしているからですね。
-2も符合なしに変換されて、4294967286/4294967294→0となります。
すべてunsigned int型になるので%uで表示されるのが本来の評価された値ということになります。

%dで表示するものは明確にintでキャストしないとLinuxの64ビット版のlongがintよりビット幅が大きい環境とかで気付きにくいバグを産みます。
(追記)誤りです。int幅の値だけを使った計算でオーバーフローしてもlong幅に昇格することはありませんね。

(追記)
もたもたしてるうちに既に回答されてました。
しかも間違ったこと書いちゃった。
最後に編集したユーザー ISLe [ 2011年3月17日(木) 23:56 ], 累計 1 回
ISLe
 
記事: 2593
登録日時: 2010年10月16日(土) 22:47
日記: 日記を見る (19)

オンラインデータ

登録ユーザー: みけCAT