質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

5回答

962閲覧

c言語、「変数の値が異なるというのは、内部ビットがことなることであり、またそれだけである」は正しいですか?

abustoy

総合スコア12

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2021/07/27 13:36

前提・実現したいこと

例えば
int a=3;
double b=3.0;

int型変数aとdouble型変数bを宣言して
a==b
と等価演算子で結び、この式を評価すると、
まず「暗黙の型変換」によりint型変数aはdouble型変数aに型変換されますよね?

そして、この式の評価値は1、つまり等しいということなのですが、これは内部ビットが等しいという意味でしょうか(またそれだけでしょうか)。
このような質問をした背景には以下のソースコードが関係しております。

float値0にfor文でfloat値0.01足し続けた float値0.999999と普通に宣言したfloat値0.999999は、画面の表示上はともに「0.999999」なのですが、比較をすると異なるという、個人的に面白い(?)結果になりました。
このことから、「変数が等しいこと」と「内部ビットが等しいこと」は同値であると考えましたが、これは正しいでしょうか?

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

C言語

1#include <stdio.h> 2#include <string.h> 3 4void hudoufloat(float); 5// floatを与えると浮動小数点数表示の内部ビットをプリントする関数 6 7int main(void) 8{ 9 float x; 10 for ( x = 0; x <=(float)0.99; x+=(float)0.01) 11 { 12 printf("%f\n",x); 13 hudoufloat(x); 14 } 15 printf("ここから下がお聞きしたいところです\n"); 16 printf("%f ver a\n",x); 17 // 0.999999 ver a 18 // これはfloat 0にfloat 0.01を足し続けた0.999999 19 hudoufloat(x); 20 // 00111111 01111111 11111111 11110101 21 22 printf("%f ver b\n",(float)0.999999); 23 // 0.999999 ver b 24 // これは普通の0.999999 25 hudoufloat((float)0.999999); 26 // 00111111 01111111 11111111 11101111 27 28 if (x!=(float)0.999999) 29 { 30 printf("%fと%fは等しくない\n内部ビットが異なるから、画面の表示上おなじ「float型0.999999」なのに等しくないという理解でただしいでしょうか\n",x,(float)0.999999); 31 // 内部ビットが異なるから、画面の表示上おなじ「float型0.999999」なのに等しくないという理解でただしいでしょうか 32 } 33 34 35 return 0; 36} 37 38void hudoufloat(float f){ 39 40 int i,j,k; 41 char c[sizeof(f)]; 42 memcpy(c, &f, sizeof(f)); /* fの内容を配列cにコピー */ 43 printf("sEEEEEEE EEEEdddd dddddddd... \n"); 44 45 for( i = sizeof(f) - 1 ; i >= 0; i--) 46 { 47 for ( j = sizeof(c[i]) * 8 - 1 ; j >= 0 ; j-- ) 48 { 49 k = c[i] & (1 << j); /* jを1つずつずらして論理積をとる */ 50 printf("%d", k ? 1 : 0 ); /* k=0でなければ1, k=0ならば0を表示 */ 51 } 52 printf(" "); 53 } 54 printf("\n"); 55 56 return; 57} 58 59

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

cygwinでgccコンパイラを使用しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

細かいことを言うと、まったく同じビットパターンでも等しくない場合が存在します。

c

1#include <stdio.h> 2#include <math.h> 3 4int main(void) 5{ 6 float x = NAN; 7 if (x != x) 8 printf("%fと%fは等しくない", x, x); 9 10 return 0; 11}

投稿2021/07/27 14:10

actorbug

総合スコア2431

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

abustoy

2021/07/27 14:32

ご回答ありがとうございます、NANというものを初めて知りました。<math.h>で定義されているマクロなのですね。 ==や!=の評価がビットパターンによって決まらない(決まる要素のうちの一つであるかもしれないが)となると、何で決まると思いますか?差し支えなければ意見をお聞きしたいです。
actorbug

2021/07/27 15:03 編集

「規格で決まる」だけだと思います(そういうことを聞きたいのではないとは思いますが)。言語を使う側は、その言語の規格に従うしかありません。
abustoy

2021/07/27 15:40

いえ、やはりそういうものですよね。とりあえずあまり気にせず進めようと思います。ご回答いただきありがとうございました。
ppaul

2021/07/28 03:40 編集

NaNの比較の結果はIEEE 754で決まっているので、ハードウェアの規格で決まっています。 もっとも、ハードウェアがIEEE 754で動いているからといって、プログラミング言語がそれと異なる処理をやろうと思えばできるので、言語の規格という考え方もありますね。
guest

0

ベストアンサー

整数やポインタであれば合っていると思いますが、
IEEE 754 を採用している環境のC言語では、ビット表現が異なるのに == が成立することがあります。例: 0.0 == -0.00.0f == -0.0f

投稿2021/07/27 14:19

編集2021/07/27 14:30
int32_t

総合スコア21695

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

abustoy

2021/07/27 14:34

ご回答ありがとうございます。 ==や!=の評価がビットパターンによって決まらない(決まる要素のうちの一つであるかもしれないが)となると、何で決まると思いますか?差し支えなければ意見をお聞きしたいです。
int32_t

2021/07/27 23:20

C言語やIEEE754の仕様で決まります。
abustoy

2021/07/28 04:00

ご回答ありがとうございます。助かりました。
guest

0

その認識で合っています。
お書きのように自動変換される場合は変換後の値ですが。

言語によっては、==演算子を定義できるので、その場合はいろいろです。
そういう言語では「10進7桁に丸めながら変換して、変換後の文字列が等しい」という定義にする事もできます。

投稿2021/07/27 13:46

otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

abustoy

2021/07/27 14:11

ご回答ありがとうございます。「10進7桁に丸めながら変換」という言葉の意味がわからなくて予想なのですが、おっしゃるような「10進7桁に丸めながら変換して、変換後の文字列が等しい」という==演算子の定義であれば、「内部ビットが異なっていても==演算が真を返す」ということが起こりえますよね
otn

2021/07/27 14:55

> 「10進7桁に丸めながら変換」という言葉の意味がわからなくて 「doubleやfloatの値を10進7桁の文字列に変換(8桁目を丸める)」という意味です。
abustoy

2021/07/27 15:37

ご回答ありがとうございます。それはつまり「float型の値 1.23456789」が==演算子を適用する際には「文字列 "1.234568"」(丸めは四捨五入とします)に変換されるという認識で正しいでしょうか(回答者様の意図を理解できているでしょうか)?
otn

2021/07/27 16:31

「そういう変換してから比較するように演算子を定義することが出来る言語もある」ということです。
abustoy

2021/07/27 17:03

理解いたしました。ご回答いただきありがとうございます。
guest

0

浮動小数を==で比較してはいけないと入門書にも書いてあります。
あとはご自分でググってください。

投稿2021/07/28 04:14

YA-METAL

総合スコア42

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

abustoy

2021/07/28 05:59

ご回答ありがとうございます。おっしゃるとおり、そのような記述を見つけました
guest

0

と等価演算子で結び、この式を評価すると、
まず「暗黙の型変換」によりint型変数aはdouble型変数aに型変換されますよね?
そして、この式の評価値は1、つまり等しいということなのですが、これは内部ビットが等しいという意味でしょうか

たぶん違うと思います。
double型変数で名前がaというのは生成されません。a と数値的に等しいdouble型の値が作られ、その値は一般には、直ぐに廃棄されます。

 次にdouble の変数の精度は有限であるからです。2進法で、0.01 は、循環小数であって、有限桁では正確に表現できません。2進数0.1は、2進数0.5 で、10進数0.01は、2進数 0.00000101000111010111.. でそこから循環します。その意味で、"内部ビットが等しい"の意味が薄らぎます。

double f = 0.01 ; と書けば、コンパイラは、できるだけ0.01 に数値に変換しますが、いくらか誤差があります。0.01 っぽいが、正確には、0.01 ではないf を100回足しても、1.00 っぽい数字にはなりますが、数学的な整数1とは等しくなりません。

一方、整数1は、10進法でも2進法で表現しても循環小数ではないので、有限のbit数で表現できて誤差は0です。上の1.00 っぽい数と、正確に1であるものを比較すると当然一致しません。

double とdouble の数の比較に限定して、== とは何かを考える場合、+/-無限、+/-0、0/0 にあたる数などのfloat/doubleのプログラム上の表現的表現のケースを除いて2つのdoubleの数が等しいとは、 bit by bit で内部ビットが等しいという意味になります。

投稿2021/08/03 06:49

gm300

総合スコア580

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問