printf("%d", f)でfがfloatの時の挙動について
型はバイト幅だけじゃなくてどうやって二進法でエンコードするのかも規定するよね、という話をしていて確認のためのコードを書いた。手抜きをして方法Bでやってもいいよね、と思ったら予想外の結果が出たので悩んでいる。→解決
#include<stdio.h> int main(){ int i; float f = 1.0; /* 素直な方法 A (追記:やっちゃダメ!)*/ i = *(int*)(&f); printf("A %d\n", i); /* 手抜きな方法 B */ printf("B %d\n", f); }
Rubyでこんな感じに文字列化して、と。
irb> def pp(x) x.to_s(2).rjust(32, "0") end irb> pp 1065353216 => "00111111100000000000000000000000"
0.500000の時 A 1056964608 : 00111111000000000000000000000000 B 8388608 : 00000000100000000000000000000000 1.000000の時 A 1065353216 : 00111111100000000000000000000000 B 8388608 : 00000000100000000000000000000000 2.000000の時 A 1073741824 : 01000000000000000000000000000000 B 8388608 : 00000000100000000000000000000000
なんでBの方は値が変わらないのだろう?まあ方法Aを使えば問題ないんだけど、理由がわからないのはすっきりしないな。
怒られる前に追記
$ uname -a Darwin nishio.local 10.7.0 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386 $ gcc -v Using built-in specs. Target: i686-apple-darwin10 Configured with: /var/tmp/gcc/gcc-5659~1/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1 Thread model: posix gcc version 4.2.1 (Apple Inc. build 5659)
解決編
nyaxt: 西尾さんのぶろぐよんだ。プラットフォームがx64じゃないかというえすぱー。
nyaxt: ABIじゃないかなーという。x86だと全部スタックにつむけど、x64だとレジスタ渡しするので浮動小数点数はmmxレジスタにはいるんじゃね(追記:以下s/MMX/SSE/ig)nyaxt: やっぱりそうだw 手元のこーどみたらやっぱりBだとxmm0にmovssしてる
nishio_: MMXレジスタに入れて、そこからとってもらえるつもりで呼んだのに、printfの側が「%dだからスタックから取ろう」って取って変な値を返しているってこと?
nyaxt: スタックからとってるんじゃなくてレジスタからとってるはず
nishio_: 浮動小数点を渡そうと思ってMMXレジスタに入れたけど、printfは別のレジスタから取っちゃって値が変になる、ってことか
nyaxt: そうそうnyaxt: これを応用するとこういう手品ができる
nishio_: 面白い
#include
int main(){
printf("A: %d %f %d %f \n", 1, 2.0, 3, 4.0);
printf("A: %d %f %d %f \n", 1, 3, 2.0, 4.0);
}
nyaxt: 元ネタはshinhさんなので http://shinh.skr.jp/slide/tcc64/000.html
nishio_: 言及しとく
などと書いていたら解決編2
@herumi: いや,それ素直な方法も手抜きな方法も未定義なコードですから何が起こっても文句言えないです.gccならC99の%aを使うのも手. http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/printf.3.html
@gusmachine: unionを使うのが正解、でしょうか? http://bit.ly/fUn8wb
#include<stdio.h> union intfloat{ int i; float f; }; int main(){ intfloat x; x.f = 1.0; printf("%f\n", x.f); printf("int %d\n", x.i); //-> 1065353216 }
これが正解ですね。
@kosaki55tea 一般にはxmmはMMXレジスタとは言わない気がするのですが僕の周囲だけ?mm0とかがMMXレジスタでxmm0とかはSSEレジスタのような
たしかにここでもSSEレジスタと書かれてますね: about x86 CPU