
C言語 型変換のタイミングについて教えてください。
「型変換は代入のとき行われる」と理解していたのですが、代入の前の計算時も行われるのでしょうか?
char c1=120,c2=10;
int i;
のとき、
i = c1 + c2;
は i= -126 を期待したのですが i = 130 となっていました。
質問1)これは、C言語の仕様でしょうか? それともコンパイラに任されている仕様でしょうか?
関連して、
質問2)期待した計算結果をiに代入するには、
char c3;
c3 = c1 + c2; i=c3;
とする以外に方法ありますか?
質問3)計算途中でオーバーフローする可能性のある計算を確実の行うにはどのように記述したらよいでしょう?
int x0,x1,x2,x3;
x0 = (x1 + x2 + x3) / 3;
x0 = x1 * x2 / x3; (ただしx2 < x3)
よろしくお願いします。
No.3ベストアンサー
- 回答日時:
1 については C の仕様です. 計算を行うときに, int 以下の型はすべて int (または unsigned int) に変換したうえで計算されます. したがって
i = c1 + c2;
はあたかも
i = (int)c1 + (int)c2;
と書いたのと同じ計算をします.
2 は
i = (char)(c1+c2);
のように演算結果を強引にキャストすればいい, んじゃないかなと思う.
3 については.... ポータブルにやるなら努力と根性で多倍長演算を実装するしかないんじゃないかなぁ.
あ, あと余談だけど C の仕様では
char は*少なくとも*8ビット
です.
回答ありがとうございます。
>1 については C の仕様です. 計算を行うときに, int 以下の型はすべて int (または unsigned int) に変換したうえで計算されます.
なるほど!!
納得です。
>char は*少なくとも*8ビット
ありがとうございます。今までずーっと勘違いして、覚えていました。
質問2,3の回答も納得です。
No.10
- 回答日時:
「長い整数型」から「短い整数型」にキャストしたときに, 元の値が新しい型で表現できないときには
・新しい型が符号なしの型であれば範囲内に入るように適当に変形
・新しい型が符号付きの型であれば処理系定義 (処理系定義の方法で処理される, あるいは処理系が定義したシグナルが発生する)
です. なので char が 8ビット符号付きの場合
char c1=120,c2=10;
int i;
i = (char)(c1 + c2);
とやっても i が -126 になるという保証はありません (c1+c2 の結果である 130 が char に入らないため: ただし処理系が決まればどうなるかも決まる). 未定義動作であるオーバーフローよりはましですが, ポータブルではないというのも確か.
あと符号付き整数における負数の表現は
・2の補数
・1の補数
・符号+絶対値
のいずれかです.
・符号+絶対値
なるほど、忘れていました。
ご指摘ありがとうございます。
今質問を読み直して、読み手に誤解を与えてしまう記述と反省しているところです。
>質問2)期待した計算結果をiに代入するには、
これは、
char c1=120,c2=10;
int i;
のとき、
i = c1 + c2;
で i= -126 を期待してプログラミングをしたい、というわけではありません。
デバッグしていて、私の予想と違う挙動をした部分を抜き出して質問しただけです。
私も、処理系に依存しないプログラミングを心がけているので、 i= -126 を期待したプログラムにならないように気を付けているところです。
でも、皆さまからいろいろと深堀した回答がいただけて、感謝しています。
皆さまをベストアンサーにしたいところですが、悲しいかな1名しか選べないこと、お許しください。
No.9
- 回答日時:
intより精度の低い整数はintに型変換される、というのはこのあたりから。
https://ja.wikipedia.org/wiki/%E6%B1%8E%E6%95%B4 …
https://msdn.microsoft.com/ja-jp/library/aetzh11 …
(2)
> char c3;
> c3 = c1 + c2; i=c3;
は、
c3 = (int)c1 + (int)c2;
となり、 = の代入時に暗黙の型変換(int→char)が適用されています。
ですから、これを明示的に型変換すれば、 中間変数c3 を使う必要はありません
i = (char)(c1 + c2) ;
(3)
> 計算途中でオーバーフローする可能性のある計算を確実の行うにはどのように記述したらよいでしょう?
には「どんな時でも使える万能な方法はありません」
速度、メモリ、許容される誤差、難易度等々を考慮して、その場その場で対応するしかありません。
主なやり方は
○精度の高い型を使う
・intが32bit,longが64bitなら、longに型変換して計算する
・doubleがIEEE754の倍精度だったら、仮数部が52(+1)ビットあるので、int32ビット入れても誤差は無い。
○多倍長整数演算用ライブラリを利用する、または自作する
https://ja.wikipedia.org/wiki/%E4%BB%BB%E6%84%8F …
○数学の性質を利用する
例えば
x1=m1*3+r1; /* r1 は x1 を3で割った余り(0,1,2) */
x2=m2*3+r2;
x3=m3*3+r3;
とすると、
x0 = (x1 + x2 + x3) / 3;
を展開すると
x0=(m1+m2+m3)+(r1+r2+r3)/2 ;
になります。
m1=x1/3, r1 = x1 % 3
を使って、置き変えれば、x1,x2,x3の式になります。
また、m1+m2+m3 は intの精度を越えません。
○何もしない
状況によっては、そのままオーバーフローさせてしまうことも考える。
kmee様、前回https://oshiete.goo.ne.jp/qa/9704420.htmlに引き続き、回答ありがとうございます。
教えていただいたページの
「予期せぬ汎整数拡張」
の記事、参考になります。
>i = (char)(c1 + c2) ;
これでOKですか? i に代入される時点で(char) が無視される気がしていたので、意外です。intで計算され、計算結果をcharに変換してから、intに再変換されて i に代入されるのですね。
計算途中ではintですが、すべてcharで計算した場合と同じ結果になる、
訳ですね。じっくり考えてみます。
>○数学の性質を利用する
これと類似した手法
予測される平均値をあらかじめ引いておいて、残差の累計から求めた平均誤差値を最後に足す
は依然使ったことがあります。
>○何もしない
> 状況によっては、そのままオーバーフローさせてしまうことも考える。
これまた、斬新なアイデアありがとうございます。
状況によっては意外とシンプルな解決策の気がしてきました。
No.8
- 回答日時:
余談その2.
本気で「アーキテクチャに依存しないコーディング」を突き詰めると非常にめんどくさかったりします.
例えば, char が 8ビットだとして
unsigned char uc = 130;
signed char sc = *(signed char *)&uc;
とすると sc の値がどうなるかって考えると難しい.
回答ありがとうございます。
8ビットならば sc = -126 , 9ビット以上であれば sc = 130 ということですね。
いや、8ビットであっても最上位ビットが符号ビットとは限らない、
負の数の表現方法として、1の補数、2の補数表現かによってscの値はかわりますね。
「アーキテクチャに依存しないコーディング」、本当に面倒ですね。
了解しました。
No.7
- 回答日時:
char 型の符号の解釈は、Tacosan さんの見解が正しいかもしれませんね。
私は、ポーティングエンジニアの仕事をしていた時、環境に依存しない書き方を徹底的に叩き込まれたので、曖昧な表現は避けるべきという考えを今でも持っています。タダそれだけです。
失礼しました。
No.6
- 回答日時:
確かに
i=(signed char)c3;
でも Microsoft のコンパイラなら大丈夫ですね。
ビットの並びを変更せずに解釈だけ変えるときはアドレスに対してポインタをキャストするのがアーキテクチャに依存しないコーディングの基本です。それを知らずに書いていると、いざ他の環境に移植しようと思った時に誤動作の原因が掴めなくなります。(例えばバイトオーダーが違う環境など) 私はかつてポーティングの仕事をしていたので結構、こういうところ神経質になる癖があります。
他の環境を触る可能性がないのでしたら気にしないで結構です。
回答ありがとうございます。
>ビットの並びを変更せずに解釈だけ変えるときはアドレスに対してポインタをキャストするのがアーキテクチャに依存しないコーディングの基本です。
なるほど、勉強になります。
奥が深いですね。
No.5
- 回答日時:
ちょっと今手元の規格を調べてみたけど, char が符号を持つかどうかは処理系定義, と読める文章しか見つからなかった. だから #4 の「unsigned char を既定としている処理系に於いて 120+10=130 ですから合ってます」は正しいんだけど, だからといってそれが任意の処理系で成り立つと判断しちゃいけないよなと思ったりする.
「ANSI の規格では 'char' は文字型を前提としているので、既定値は 'unsigned char' と解釈され、符号付きで処理したい場合には明示的に 'signed char' と表記しなければいけません。」ってのはどこにあるんでしょうか>#4.
あ, 念のため補足しておくと, #1 に書いてあるように「計算時に型が違えば、精度が高いほうの型に拡張されます」は正しい. ただ, その「精度が高いほうの型に拡張されます」の前に「int より小さい型の値は自動的に int (か unsigned int) に変換する」というステップが入ります.
回答ありがとうございます。
No7さんの回答も合わせて、
char は 最低8ビット
unsigned char、signed char かは処理系次第
ということですね。
かなりすっきりしてきました。
>「int より小さい型の値は自動的に int (か unsigned int) に変換する」
これ、今回の質問での最大の収穫です。
ありがとうございました。
No.4
- 回答日時:
処理系に依存する曖昧な表現は極力避けて下さい。
ANSI の規格では 'char' は文字型を前提としているので、既定値は 'unsigned char' と解釈され、符号付きで処理したい場合には明示的に 'signed char' と表記しなければいけません。
unsigned char を既定としている処理系に於いて 120+10=130 ですから合ってます。明示的にオーバーフローを期待した処理ならば、代入時に
i=*(signed char*)&c3;
と記述するべきでしょう。
回答ありがとうございます。
>ANSI の規格では 'char' は文字型を前提としているので、既定値は 'unsigned char' と解釈され、符号付きで処理したい場合には明示的に 'signed char' と表記しなければいけません。
そういえばそうでした!!
ということは、Visual Studio は
ANSIの規格に従っていない
のですね。
https://msdn.microsoft.com/ja-jp/library/s3f49kt …
には、
char 1バイト 既定では -128 ~ 127
/J を使用してコンパイルするときは 0 〜 255
とあります。
>明示的にオーバーフローを期待した処理ならば、代入時に
今回の質問はデバグ中に遭遇した疑問なので、「明示的にオーバーフローを期待した処理」をしたいわけではないですが、そのような場合には、参考になります。
ところで
> i=*(signed char*)&c3;
素直に、
i= (signed char) c3;
と書かない訳は何かあるのでしょうか?
No.2
- 回答日時:
>計算時c1 + c2にはchar 型同士の足し算なので、型変換されずにchar型で計算され、 -126 の計算結果になると思います。
うん、ごめんなさい
そうですね、なんか私が変な勘違いしてたみたいです
そう考えると、計算の精度拡張より代入の変換のほうが優先度が高いということでしょうね
ここは色々実験して確かめたほうがいいかもしれません
なので質問3については私の知識ではちょっと分かりません
回答ありがとうございます。
>そうですね、なんか私が変な勘違いしてたみたいです
ご理解いただけたようで、うれしいです。
回答、お待ちしています。
補足
C言語の仕様として定められているのは、
char 8bit
shortとintは少なくとも16ビット、
longは少なくとも32ビット、
shortはint以下で、intはlong以下、
ということだけであることは承知しています。
質問3に関しては、intが16ビット、longが32ビットの処理系であれば、
x0 = ((long)x1 + (long)x2 + (long)x3) / (long)3;
でOKなことはわかっていますが、もっとスマートな記載方法が知りたいと思っています。
int、longが共に32ビットの処理系では、平易な「解なし」かとも思っています。
No.1
- 回答日時:
> i= -126 を期待したのですが i = 130 となっていました。
なぜその結果を期待したのかが全然分かりませんね
型変換のタイミングは代入時と計算時だったはずです
計算時に型が違えば、精度が高いほうの型に拡張されます
そして代入時に型が違えば、左辺の型に変換されたはずです
多分C言語の使用かな?ちゃんと調べてないので確かではないです
質問2)
あなたの期待結果がよく分からないので
ちょっと何も言えないです
私の知識が乏しいせいです。すみません
質問3)
型を変えてください
オーバーフローする可能性のある計算を
そのままの型で行うこと自体がおかしなことです
そのような状況に陥らないように普通はコーディングします。
回答ありがとうございます。
>なぜその結果を期待したのかが全然分かりませんね
そうですか、詳細を述べます。
>型変換のタイミングは代入時と計算時だったはずです
>計算時に型が違えば、精度が高いほうの型に拡張されます
計算時c1 + c2にはchar 型同士の足し算なので、型変換されずにchar型で計算され、 -126 の計算結果になると思います。
>そして代入時に型が違えば、左辺の型に変換されたはずです
char型の値 -126 が int型 に変換されて int型の値 -126 が i に代入されると思います。
質問3)
>そのような状況に陥らないように普通はコーディングします。
質問文に書いてあるように、そのような状況に陥らないようなコーディングが必要なことは理解しています。
ですので、具体的な、そのようなコーディングを教えてくださいという質問です。
よろしくお願いします。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- 数学 線形代数の対称行列についての問題がわからないです。 2 2023/01/08 14:59
- 数学 「FFTの基本は、DFTはサンプル数Nが偶数なら 2つのDFTに分解できるということ。 分解するとD 3 2022/03/31 21:01
- C言語・C++・C# c言語でユーザ関数を利用して複素数のべき乗と絶対値の数列を計算するプログラムが作りたいです。 3 2023/01/29 22:13
- C言語・C++・C# C言語 3 2022/10/04 15:07
- Excel(エクセル) スプレッドシートについて A1÷B1の値をC1に、A2÷B2をC2、A3÷B3をC3…といった感じで 1 2022/05/17 20:24
- 数学 x1+3x2+2x3=4 2x1+x2-3x3=2 -5x1+5x2+18x3=a 次の連立1次方程 2 2023/07/02 03:15
- C言語・C++・C# c言語 int temp = 0; if(isdigit(arr[i])){ temp=arr[i] 2 2022/03/27 01:44
- C言語・C++・C# numpyスライス機能を使った数値計算 2 2023/05/08 16:01
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- 数学 ハイネボレルの被覆定理、内田伏一著 「集合と位相」定理22.1 1 2022/07/07 10:49
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
2進数の足し算(C言語)
-
有効数字について 以前質問をし...
-
ExcelのINT関数の計算結果がお...
-
浮動小数点演算を固定小数点演...
-
c languageで 簡単な質問があ...
-
C言語プログラミングにて、arct...
-
VB.net Double と...
-
Fortran において変数の定義
-
double型からfloat型への型変換...
-
三菱シーケンサ(Aシリーズ)で...
-
加算と減算で乗算と除算を表現...
-
16進数 加算 減算 C言語
-
ExcelでPC(パソコン)によって...
-
ニュートン法でのプログラミング
-
浮動小数演算は実行環境の変化...
-
PICで小数点の演算
-
大きすぎる数値になるとE+にな...
-
「Aに対するBの割合」と「Aに対...
-
マイナスからプラスへ転じた時...
-
「指定されたキャストは有効で...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ExcelでPC(パソコン)によって...
-
O(n log n)について2
-
有効数字について 以前質問をし...
-
c languageで 簡単な質問があ...
-
ExcelのINT関数の計算結果がお...
-
EXCELの関数"STDEV(標準偏差)"...
-
三菱シーケンサ(Aシリーズ)で...
-
VB.net Double と...
-
計算の丸め誤差の解消について
-
除算を使わずに10で割りたい。
-
2進数の足し算(C言語)
-
16進数 加算 減算 C言語
-
”/”を使わずに割り算したいんで...
-
CRCの計算方法について
-
VB6.0での小数点の扱いについて
-
VBAでミリ秒まで出力する方法
-
時刻の比較
-
2進数データのビット演算
-
教えて小数点の比較!(C言語)
-
C言語 型変換のタイミング
おすすめ情報