windows版rsyncとubuntuサーバを使って、開発環境を同期しよう

Posted on

 私は現在、デスクトップPCとノートパソコンで開発しています。

 こういった時に問題になるのが、開発環境の同期です。同じソフトを何度もインストールしてメンテナンスするのは時間の無駄ですから、できるだけ同期させる必要があります。

 そのために、ここ数年間様々な方法を検討して実際に使ってきました。

方法1:USBメモリ

 PortableApps等を見習って、USBに開発環境一式を入れて使うというアプローチです。ある意味、一番素直ですね。8GB~16GB程度のUSBメモリもそれなりに安価に入手できるようになり、実用性が出ました。

  • メリット
    • “コピー”は存在しないので、同期を考えなくていい。
    • PortableAppsあたりで確立されているノウハウがそのまま使える。
  • デメリット
    • 安いUSBメモリは想像以上に遅い。そして、結構クラッシュする。
    • モバイル環境で、USBメモリが出っ張ってるのはちょっと怖い。USBが物とぶつかった場合、てこの原理でUSBコネクタが破壊されるかもしれない。

 16GBのUSBメモリが2本も壊れてしまったので、この方法は諦めました。今はまた別のUSBメモリ†1を買いましたが、大きなサイズのデータのやり取りに留めています。

方法2:dropbox

 USBメモリを諦めて、同期すると考えた場合、まっさきに思いついた方法です。専用のクライアントをインストールすることで、Windows/Mac OSX/Linuxの各OSで、ファイルを全自動で同期してくれます。

 初期容量が2GBしかありませんが、お金を払うor無駄に頑張ることで、容量を増やすことができます。私は無料で6.1GB手に入れました!(ドヤ

  • メリット
    • 自動でやってくれる
  • デメリット
    • 間違えて消した結果も全自動でミラーリング。ノートPCの不意のクラッシュによってデータが破壊された場合も同様。

 とはいえ自動でシンクロしてくれるのは便利で、現在でも、開発中のソースコードをシンクロさせるのに使っています。gitリポジトリも一緒に同期しているため冗長化されている上に、仮にファイルが紛失しても元のリポジトリから取得してくれば大丈夫なようになっています。

方法3:Bazaarの軽量チェックアウト

 新進気鋭のバージョン管理システムBazaarの、軽量チェックアウト機能を使ってSyncします。

 軽量チェックアウトというのは、SVNと似たような方式で、バージョンの一番新しいものだけが同期されます。ではなぜSVNを使わないとかというと、SVNも最新バージョンだけを同期しますが、最新バージョンのコピーも作成し、結果ファイル数が倍になってしまうのと、全フォルダに.svnというメタ情報を格納したフォルダ(先述の最新バージョンのコピーもここに入ってます)が作成され、これが存外邪魔だからです。

  • メリット
    • バージョン管理システムのリッチな所がすべて使える。
    • 変更点が一目で分かる
  • デメリット
    • クライアント側は最新バージョンだけだが、サーバ側は全バージョンなのでサーバのディスクを逼迫する(注:このsyncサーバはSSDで動いています)
    • 流石に10万ファイルを超えるとbazaarが重い。

 GUIのクライアントが一切使えないのには困ってしまいました…。コマンドラインだけだと正直ちょっとしんどいです。

rsync

 というわけで、rsyncに乗り換えました。これにより、速度の改善とディスクの逼迫を改善したいです。バージョン管理はなくても、たぶんなんとかなります。まだそんなに使っていないので、効果についてはこれから検証!です。

というわけで、実際にrsync環境を作ってみよう。

Ubuntu側のセットアップ

 基本的に公式マニュアルの通りです。玄箱でやっていたあたりからずっとinetdを使って来たのですが、どうやら最近はxinetdが推奨のようですね。

 設定ファイル先頭にこんなのを書き加えるともうちょっとセキュアかも。

use chroot = yes

 一つ引っかかったのは、xinetdだけでなく、システム全体の再起動をしないとうまく接続できない(telnetで叩いてもconnection refusedされる)事くらいでしょうか。どうしてかは分からないのですが、再起動すればうまく動いたのでよしとします。

Windows側のセットアップ

 cygwinをパッケージしたcwRsyncをDLします。インストーラだからインストール…。いえいえ、あり得ないですよね。Universal Extractで解答できます。使い方はこの辺を参考にね

 解答するとbinやdocといったフォルダが含まれるフォルダが出てくるので、そのフォルダを頂戴して”sync”とか適当にリネームして、そのフォルダに色々とsyncのためのスクリプトを書きましょう。

 まず必要なのは、pathを通してくれるようなバッチファイルです。

rem "common.bat"として保存してね。
@echo off
set PATH=%~dp0bin;%PATH%
rem この辺は適当に調整してね。
SET REPOS=ledyba@ledyba.org::windev/
rem cwRsyncはcygwinなので、パスの形式はcygwin形式です。
SET TARGET=/cygdrive/d/software/

 同期のための個々のバッチファイルを書く際は、このファイルをcallすることにしましょう。

 %~dp0に関してはこの辺を参考にしてください。バッチファイルが置かれたフォルダに置き換えられます。結構便利。

ファイルをサーバへバックアップする(push)

call "%~dp0common.bat"
rsync -rzht --progress --stats --chmod=a+rwx --delete %TARGET% %REPOS%
pause

 環境設定のための、先程作成したcommon.batを呼び出して環境を設定した後、rsyncを叩くだけです。

 それぞれのオプションは…

  • “-r”:再帰的にコピー。そうしないと、一番上段だけコピーされて意味が無い。
  • “-z”:圧縮をかける。exeが多いので意味があります。
  • “-h”:それっぽいログを出してくれるようになるので、ちょっとうれしくなります(ぇ
  • “-c”:チェックサムを取り、その結果を用いてコピーするかどうか判断します。
  • “–progress”:転送中の状態を表示します。
  • “–stat”:転送後に結果を表示します。
  • ” –chmod=a+rwx”:ファイル転送時にパーミッションを設定します。
    • これが一番重要です。転送時に、rsyncサーバが、デフォルトでは000でファイル・フォルダを作成してしまい、エラーが起きます。
  • “–delete”:ローカルで削除したファイルを、転送先にも反映させます。

 詳しくはこの辺参照。一番いいのは、それよりもお使いのrsyncの設定を見ることだと思いますが…。

 こんな感じです。特にこったところはありません。

ファイルから更新されたファイルを引き出す(pull)

call "%~dp0common.bat"
rsync -rzhc --progress --stats --delete %REPOS% %TARGET%
pause

 逆の事をしているだけです…。

ファイルが破壊されたかもしれない時用。

 私のノートパソコンは比較的クラッシュしやすい(どうしてだろう…)ので、そのための対策です。

call "%~dp0common.bat"
rsync -rzhc --progress --stats %REPOS% %TARGET%
pause

 –deleteをなくしただけです。こうすると、クライアント側には、クライアント側とサーバ側の和集合が残ります。

 この辺のノウハウはかなり無駄に貯めこんでるので、ちょっとずつ放出していけると良いかもしれませんね。

 みなさんも、リモート環境のノウハウが合ったら、ぜひ教えてくださいね。

 @ryoutenihamuさんに、common.batの存在しているフォルダのパスに空白が含まれている場合を忘れているとご指摘頂きました、ありがとうございます!また、WindowsNT系OSでは、.cmdがバッチファイルの標準拡張子だそうです…(どっちでも動くみたいですけど)。

  • †1: デザインが可愛くてかっこよくて、中々気に入っています。無くしがちなフタ部分もないし。

ミリ秒単位のタイマーと整数だけを使って60FPS固定にするには

Posted on

 考えてみれば当たり前な方法ですが、ぐぐっても見つからなかったので…。

 世の中に存在する大体のライブラリでは、時間をミリ秒単位で計測することができます。一秒間は1000ミリ秒です。

 また、世の中、特にこの日本に存在するゲームは、大体60FPSで動いています。一秒間に60回画面上の絵が更新されるということです。

 1/60秒は、16.666666666666667秒。1ミリ秒が精度のタイマーでは、正確に測定することはできません。でもゲームスピードを60FPSにしたいなら、なんとか測定しなければなりません。

 そこで、多くのゲームではいろいろな方法でこの問題を解決してきました。

方法1:1/60秒≒16ミリ秒と近似する。

 いろいろと諦めてるのがこの方法です。この方法だと、1000ミリ秒/16ミリ秒=62.5となるので、60FPSより少し早くなってしまいますが、目をつぶります。

 ソースコードはこんな感じ

void gameloop()
{
	uint32_t nextFrame = getNow()+16;//ミリ秒のタイマー関数
	while(ゲームが終わるまで){
		move();//ゲーム内でのキャラクタの移動処理など
		uint32_t now = getNow();
		if(now < nextFrame){
			draw();//描画処理
			now = getNow();
			if(now < nextFrame){ //描画しても時間が余ってたら・・・
				sleep(nextFrame-now);//ミリ秒精度で処理を中断する関数
			}
		}
		nextFrame+=16;
	}
}

方法2:浮動小数点を使う

 sleepも、現在の時間を取得する関数もミリ秒の精度しかありませんが、それらを管理する変数だけは浮動小数点を使う方法です。この方法を使うと、実際には16ミリ秒のフレームと17ミリ秒のフレームが、1:2の割合で現れます。

 ソースコードを見ていただけたほうが早いでしょう。

const float frameInterval = 1000.0f/60; //60FPSの基準時間

void gameloop()
{
	//floatでの管理に変えた
	float nextFrame = getNow()+frameInterval;
	while(ゲームが終わるまで){
		move();//ゲーム内でのキャラクタの移動処理など
		uint32_t now = getNow();
		if(now < nextFrame){
			draw();//描画処理
			now = getNow();
			if(now < nextFrame){ //描画しても時間が余ってたら・・・
				//型変換が必要。。。
				sleep(static_cast<uint32_t>(nextFrame-now));
			}
		}
		nextFrame+=frameInterval;
	}
}

 この方法ではかなり正確に60FPSを達成できますが、「計算の遅いfloat+型変換まで必要、よって遅そう」、「浮動小数点の精度は直感的に分かりづらい」(からなんか気持ち悪い)という精神衛生上の問題が発生します。

 実際には一秒間に60回しか行わないので、パフォーマンス上の問題は無いでしょうし、そもそもミリ秒精度しか測れない以上キッチリ60FPSを測ることも不可能なので、floatの誤差を気にする必要は無いと思いますけど。でもなんか気持ち悪いんです…(ぇ

方法3:vsyncに任せる

 DirectXやらOpenGLやらのゲームライブラリは、システムがモニタに画面を描画するタイミングまで待ってくれたりします。これを垂直同期(vsync)を取るといい、この方法を使うとシステムが画面をディスプレイに描画している最中にゲーム画面を更新する事がなくなるので、画面がガクついたりしなくなります。

 この方法は自分で時間を測る必要もないし†1、画面も綺麗になるしでいいこと尽くめなのですが、ひとつだけ問題があります。「すべてのモニタが60FPSとは限らない」、という点です。75FPSの画面もあれば、50FPSの画面もあります。垂直同期を取るとこういった場合にゲームが早くなったり遅くなったりしてしまいます。。

 また、必ずしもvsyncがサポートされているとは限りません。グラフィックカードの設定でvsyncを無効にすることもできます(OpenGLの場合。DirectXは存じません)。

今回の方法:浮動小数点でなく分数で計算する

 長々と書いてきましたが、やっと今回の本題に入れます。vsyncが一番良いのはそのとおりなのですが、諸々の事情で使えないこともあるとわかりました。ということはやっぱりタイマを用いて制御しなければならないこともあるわけです。。。

 方法2のfloatを用いる方法を継承しつつ、精神衛生上よろしくないfloatを取り除きます。一秒間に1000回回るカウンタを使って一秒間に60回を測る方法…。そう、最小公倍数の6000を基準にして、分数で測ってしまいましょう†2

void gameloop()
{
	//(1/60秒 = 100/6000秒)
	uint32_t nextFrame = getNow()*6 + 100;
	while(ゲームが終わるまで){
		move();//ゲーム内でのキャラクタの移動処理など
		uint32_t now = getNow() * 6;
		if(now < nextFrame){
			draw();//描画処理
			now = getNow() * 6;
			if(now < nextFrame){ //描画しても時間が余ってたら・・・
				sleep((nextFrame-now)/6);
			}
		}
		nextFrame+=100;
	}
}

 問題はタイマーの限界が6倍早く来てしまう点でしょうか…。SDLのSDL_GetTicksなどの32ビット整数を返すタイマでは49日間で0に戻ってしまいますが、6倍するのでだいたい8日でオーバーフローしてしまいます。

応用

 抽象化すると「”一定期間内にn回まわるカウンタ”で”一定期間内にm回まわるカウンタ”をつくる方法」ですので、フレームレート以外でも活用していただけます。

 そもそも今回の内容は、現在開発中のファミコンエミュレータで、サウンドとCPUを同期する方法を考えていて思いつきました。とはいえその内容をそのまま書いても汎用性が低いので、フレームレートに置き換えて記述しています。

  • †1: フレームスキップするなら別です
  • †2: 書いていて中学受験を思い出しました…。

やっぱりdoubleでは「76287755398823936」は表現できない

Posted on

 「なぜJavaScriptで「76287755398823936」が正しく表示できないか、あるいはなぜRubyでも表せないか。」の続きです。後半戦、テンションあげてまいりましょー(涙目

出力側ソースコードのチェック!

 さて…では重い腰を上げてソースコードを読みましょうか…。 FirefoxでもChromeでも起きるなら、何かWindowsのライブラリのバグ…なんでしょうか。ま、いいや。とりあえずソースコードが探しやすそうなChromeから見てみましょう。

 それっぽいメソッドを探していくと…見つかりました。これですね。v8::internal::Grisu3()です。…あれ…?標準ライブラリじゃ…ない…!?うげぇめんどくさい…

 v8をWindows上でコンパイルするのはひたすら☆面倒†1なので、Ubuntu上でコンパイルしてCodeLiteというIDEをGDBのGUIラッパーとして使いました。これ、初めてだったんですが結構便利。Windowsでも使えるならちょっと試してみようかなってレベルです。EclipseCDTとは何だったのか。あとDDDとxxgdbはクソ。

原因は、やっぱり精度の問題(

 これも、実はやっぱりというか結局というか、doubleの精度の問題です(

 この問題の値をintegerからdoubleに一意に表現することができるのですが、doubleからintegerには必ずしも一意に変換はできない、ということです。

何もかもを忘れてさっきのdoubleの表現から元の値を読みだそうとしてみる

 これをやるとどうしてなのかわかります。

0/10000110111/0000111100000111010011110011000101000010010000000000

 今までの事は一切わすれて。この値、いったい何なんでしょうね?早速IEEE754に基づいて分析してみよう!(投げやりな態度で)

 ふむふむ…符号は0だから、プラスの数みたいですね。

 指数は1079だから…1023でバイアスされてるから本当は1079-1023=56なんですね。メモメモ。

 仮数は0b0000111100000111010011110011000101000010010000000000だから、二進の小数で

1.0000111100000111010011110011000101000010010000000000(53桁)

 なんですねですね。へー。

 それで、このdoubleが示す値は

 (-1)符号 * 仮数 * 2指数

 で表現されるから…

+1.0000111100000111010011110011000101000010010000000000 * 256

=10000111100000111010011110011000101000010010000000000xxx

 …ん?xxx??

「1」と「1.00」は全然違う!

 高校や大学の時に、物理の時間とかで、定規で測った値を「1cm」とかって書いたら怒られませんでした?「この定規、ミリの目盛りまであるじゃん。1cmぴったりだったんなら、1.0cmって書かないとだめだよ!」とかって言われた記憶、ありません…?†2

 今回もそれと根っこは同じことです。「x=1」と書いたら数学的には「x=1.000000000000000000….」だけど、物理学的にはそうではなく「0.5 <= x < 1.5」のあいだであるのと同じ。

 今回の例で言えば、

1.0000111100000111010011110011000101000010010000000000

の”本当の値”は

1.00001111000001110100111100110001010000100011111111111以上、

1.00001111000001110100111100110001010000100100000000001未満

 の間にある!って事です(逆に言えば、これらの値をdoubleにすると皆同じdoubleの表現に落ちる、ということです)。これをそれぞれさっきのに代入すると…

+1.00001111000001110100111100110001010000100011111111111 * 256

=76287755398823928

+1.00001111000001110100111100110001010000100100000000001 * 256

=76287755398823944

 ほう…そう来ましたか…

とうわけで、このdoubleが示すxは、

6287755398823928 <= x < 76287755398823944

なんですね。そう、やっぱり「76287755398823936」には決まりませんでした

 この時、62877553988239**という桁までは確定、その次の桁は2か3か4、最後の下一桁はさっぱり☆わからない、というわけですが、stringに変換する際はどれかには決めないといけません。この時に最後を40(上側)とするのがChrome/(とたぶんFirefoxとRuby)、30(真ん中)に多分するのがIE8/9、ということになります。でもIEの挙動はなんか標準じゃ無いっぽい…?

 IEEE754での標準の丸めモードは「最近接丸め(偶数):最も近くの表現できる値へ丸める。表現可能な2つの値の中間の値であったら、一番低い仮数ビットが0になるほうを採用する。」との事なので、切り捨てられがちになるから上の方の値を採用してるって事なんでしょうかね、たぶん。

 ちなみに、この解説した部分がissueで示した、v8::internal::Grisu3のboundary_minusとboundary_plusを計算する部分です。V8では、boundary_plusの方から文字列を作っているので、ソースを読みたい場合はboundary_plusに着目していってみてください。

 あとこの実装の理論的詳細はこちら「Florian Loitschの論文”Printing floating-point numbers quickly and accurately with integers”」にあるそうです。

もっと単純に表現すると

 「76287755398823936=0b100001111000001110100111100110001010000100100000000000000」は最後の0まで意味があるので、43ビット精度の数ではなくてやっぱり57ビット精度の数です。

 つまり、64ビットの浮動小数点の精度53ビットでは足りないので、うまく表現することはできません。

でも「76287755398823936」って出てほしい!!

 1cmって書いたら1.0cmだし1.00cmだって言いたくなるのも人情(たぶん)。そう表現したい場合は、

javascript:alert((76287755398823936).toFixed(0))

 ってやってみてください。詳しい仕様はこちら!そして今までの、ToStringをnumberに適用した場合の仕様はこちら。

まとめ

  • JavaScriptで整数を扱うときは53ビット整数までにしよう(あれ…?普通だ…?)
  • 浮動小数点は結構厄介。でもたまに見る分には面白いね!

早速v8のissueに回答もらった

 はやい!ありがとう!

Precise representation of this number requires 57 bits not 43.

When formatting a string representation of double we are free to choose any string that when parsed back would give the same double number. Simply speaking only the following must hold:

parseFloat(d.toString()) == d

In this case both 76287755398823940 and 76287755398823936 map to the same double number and we can use either of them when printing the result back.

You can use d.toFixed(0) to uses different formatting algorithm for numbers less than 10^21. This algorithm will render double with mantisa m and positive exponent p exactly as integer m*2^p. In you case it means that

(76287755398823936).toFixed(0) === “76287755398823936”

For more information please read:

ECMA-262 5th 9.8.1 ToString Applied to the Number Type. http://es5.github.com/#x9.8.1

ECMA-262 5th 15.7.4.5 Number.prototype.toFixed (fractionDigits) http://es5.github.com/#x15.7.4.5

Details about the algorithm used by V8 are available in Florian Loitsch’s paper “Printing Floating-Point Numbers Quickly and Accurately with Integers”.

 こんなに詳しく教えてもらえるなんてほんとありがたいです…。しかしそれに対する私の返答はひどすぎ…。こういうのあるともっと英語を知らないとな!って思います…。

  • †1: SConsっていうビルドツールとかが必要。
  • †2: 多分理系じゃないと無いとおもうケド。

なぜJavaScriptで「76287755398823936」が正しく表示できないか、あるいはなぜRubyでも表せないか。

Posted on

 「Twitter住所特定実験」を開発中に気づいた事です。取得したツイートをサーバからJSONでクライアント側のJSに送る処理があって、当初この時にTwitterのツイートのIDをJSONに数値として含めて送っていました。

 が、JavaScriptではこの値を受信してeval()した†1際、うまく変換することができず、たとえばChrome/Firefoxでは「76287755398823940」となり、微妙に異なった数値になってしまいました。

 以下をクリックすることで、実際に実行できます。

javascript:alert(76287755398823936)

 IE8/9だと「76287755398823930」となり、まだ微妙に違った値になります。

doubleの精度の問題??

 まず疑われたのはdoubleの精度の問題。一部では有名な話で、JavaScriptでは数値はすべてdoubleで持っています。doubleは64ビットの浮動小数点なので、64bit整数より若干精度が落ち(具体的には53ビットしか精度がありません)、かなり大きな(64bit整数の限界に近いような値は)うまく表現することができません。

 で、実際に実験してみると…。

$ cat test.asm
global  _main
extern  _printf
section .text
_main:
;問題の値を読み込む
push 0x010f074f
push 0x31424000
fild qword [esp]
;64ビット浮動小数点としてスタックに保存←おそらくここで下位ビットが失われる
fstp qword [esp]
;表示
push    messagef
call    _printf
add     esp, 12
ret
messagef:
db      "%f", 0x0a, 0
$ nasm -fwin32 test.asm
$ gcc -o test.exe test.obj
$ ./test.exe
76287755398823936.000000

あれれ!?せっかくアセンブラまで書いたのに

実は余裕でdoubleで表せる

 Doubleの精度は53ビットまでですが、この「76287755398823936」という値は43ビットの精度があれば表すことができます。この値を2進数で表すと、

100001111000001110100111100110001010000100100000000000000(57ビット)

 となります。これは57ビットありますが、doubleで十分表すことができます。えっdoubleって53ビットまでってさっき言ったじゃん?

 それについて詳しく納得していくために、doubleの仕組みを見ていきましょう。

 doubleはIEEE754に基づく浮動小数点で…。能書きはいいさね、実物を見ながら解説します。

 問題の値のdoubleでの表現は次の通りです。

0/10000110111/0000111100000111010011110011000101000010010000000000

 スラッシュで区切った三つの構造からできています:左から符号、指数、仮数。

 浮動小数点では、理系の本でよく見るような値の表現をします。つまり、

+4.2195 * 10³m

 みたいな感じですね。42195mじゃなくて、+4.2195 * 10³mです。

符号(0)

 先ほどの例で言うと、「+」のところを現しています。

 1ビットの値で、0ならプラス、1ならマイナスの値で有ることを示します。

指数(10000110111)

 先程の例で言えば、「³」の部分にあたります。

 11ビットの値で、仮数で表される値が2の何乗されるかを表します。ただし、この値は本当の値より1023大きく表現されています。-2(マイナス二乗)のように小さな値を示す場合に困らないようにするための表現です†2。これをバイアスする、と言います。

仮数(0000111100000111010011110011000101000010010000000000)

 残りの52ビットの値です。先程の(略)、「4.2195」に対応します。

 doubleでは10進数の小数ではなく、二進数の小数を表します。

 よって、値は必ず1.01011110… * 2xxxxのように表す、つまり小数点より大きな部分は必ず1とするように指数を調整することができます(正規化)。

 そこで、doubleでは最初の1を省略して残りの小数点以下の部分だけをここに記入しています。つまり、この仮数は、

1.0000111100000111010011110011000101000010010000000000

 という二進数の小数を表している、ということになります。

 最初の1ビットを省略してるので、実際に表現できる桁数が1ビット増えるんです。仮数部分が52ビットしかないのに、精度が52ビットでなく53ビットなのは、このためです。

基数

 先ほどだと10の何乗かをかけていましたが、doubleでは2の何乗をかけています。

まとめ

 以上をまとめると、doubleのデータは符号、仮数、指数の部分に分けられて、

 (-1)符号 * 仮数 * 2指数

 の値を示す、ということがわかりました。

問題の値をdoubleに手動で変換してみよう。

 問題の値は、

100001111000001110100111100110001010000100100000000000000

でした。これを実際に表して先ほどしめしたのと一致することを確かめましょう。まずは2進小数での表現になおします。10進数と同じ、小数点を移動させるだけですよ。

1.000011110000011101001111001100010100001001 * 256

 ほら、こうすると最後の0が消えて43桁の二進数になったでしょう!だから53ビット精度で表せるはずです。

 上の説明とにらめっこしながらパラメータを決めると…

符号:0(プラスだから)

指数:56+1023 = 1079(1023でバイアスします)

仮数:000011110000011101001111001100010100001001(二進数)

(小数点以下だけをdoubleには記入します;けち表現)

これを並べると…

0/10000110111/0000111100000111010011110011000101000010010000000000

 元に戻りました!とりあえず、doubleでもちゃんと表現できるんだってこと、理解してもらえましたか?

問題は入力側?出力側?

 となると、これはブラウザのどこかでバグが起きてるということになります。

 さて、問題の切り分けを行いましょう。この問題は、2つのうちのどちらかで起こってると思われます。

  • 入力側(ソースコードに書かれた文字列をdoubleに変換する際に起こっている)
  • 出力側(doubleを文字列に変換する際に起こっている)

 で、切り分けるために実際に試してみたのがこれ。

javascript:alert(76287755398823936/1024)

 割り算してみました。本当に内部の表現でも76287755398823940になってる(=入力側の問題)なら1024では割り切れないから何かしらの少数が出ると思われますし、もし実は内部表現は正しくなってる(=出力側の問題)なら、きっと桁数が減ってうまく表示できると思われます。

 で、結果は「74499761131664」になったと思います。これは正しい結果です

Rubyでも起きる!

 いつ書こうか悩みましたが、この問題はRubyでも発生します。

$ ruby1.9.1 -e "puts 76287755398823936.to_f"
76287755398823940.0

次回に続きます

 今から続きを書きますが、長くなりすぎたのでちょっと分割。とりあえずV8のissueに投げておきましたので、ゴミのような英語でも良いならそちらへ…。

続きかけた

 つづきです:

やっぱりdoubleでは「76287755398823936」は表現できない

  • †1: といいつつprototype.js頼りですが
  • †2: 補数表現じゃだめだったんだろうか?

SVNのデフォルト無視設定

Posted on

 引っかかってしまって悩んだので備忘録的に書いておきます。

 SVNでは、一切設定をしていなくても特定の拡張子をデフォルトで無視するようになっています。

 設定ファイル(UNIXでは~/.subversion/.config、windows版ではC:\Users\<ユーザ名>\AppData\Roaming\Subversion\config)内では、一切設定していない場合このようになっています。

# global-ignores = *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo
#   *.rej *~ #*# .#* .*.swp .DS_Store

 これを読む限りだとコメントアウトされてるから無視されなさそうな感じがしますが、実は無視されます。(設定例を示すのではなく、デフォルト値を示すコメントみたいで…。)

$ touch test.a
$ svn st
$ svn st --no-ignore
I       test.a

 明示的にこれらを共有したい場合は、svn addに–no-ignoreを設定するか、上記の設定ファイルをコメントアウトして無視設定を外すなど工夫が必要です。

# こうすれば無視しなくなる
global-ignores =
$ svn add --no-ignore test.a
A         test.a

 現在ノートPCとデスクトップPCでSVNを使って開発環境を共有してるのですが、この仕様でひどく苦労しました…。Bazaarに移行しようかな、やっぱり…。

Twitterで住所がバレる!?「Twitter住所特定実験」

Posted on

なにやら物騒なタイトルで。たまにはタイトル一本釣り。

リア充の皆様。AndroidにiPhoneといったスマートフォンを活用してリアルを充実させていらっしゃることと存じます。今回、私どもは、皆様のスマートフォンからTwitterへ送信することができる位置情報から、住所を特定できるのでは無いか、というありがちだけど実際検証した人は居ない†1仮説に基づき、実際にそれを行えるのかどうかやってみました。

今回開発したアプリでは、過去のツイートのうち、位置情報を持っている物を抜き出してGoogleMaps上に配置します。このアプリでは自分の位置情報のみを取得するようにしていますが、誰でも取得できる情報です。

実例と結果

こちらからアクセスしてください。

使い方も解説します。

20110626_01.jpg

この画面で「Twitter経由でログインする!」をクリックすると、このようにOAuthの認証画面が現れます。ログインを押して続行できます。

20110626_02.jpg

ログインに成功すると、このような画面になりますので、「ロード」を押してツイートの読み込みを開始してください。

20110626_03.jpg

うまく読み込むと、こんな感じで地図上にマッピングされます。このψ(プサイ)という人は関東から…出ていないようです…w

20110626_04.jpg

ズームしていくと…つくばエクスプレスユーザーだと一目で分かってしまいますーー;;

20110626_05.jpg

自宅周辺では、たくさんのピンが立っているのが特徴なのはもちろん、こういった自宅でしてるっぽいツイートがあるのも特徴です。

住所を特定するコツ

単に位置情報だけでは通勤・通学で使う駅等で呟いてることが案外多かたりで、それだけで住所まで絞り込むのは難しいです。

ですので、発言内容も手がかりにしてみてください。住所だけじゃなくてよく使うレストランとかも特定できます←父親のアカウントで検証済み、こがねちゃん弁当乙

色々と生活パターンが分かって怖い…^^;こち亀でGPSの大きな装置を背負って生活してもらう!なんて話が100巻~120巻くらいにありましたけど、それが現実になってるんだな~、って感じです。もちろん、背負わなきゃいけないほど大きな装置ではありあませんが。

住所が第三者からでも特定できちゃいそう!どうすれば?

Twitterの設定画面から、あなたの位置情報をすべて消すことができます。

20110626_twitter-acount-settings.jpg

書かれている通り、大体30分くらい時間が必要なようです。何か怖いと感じたら消してしまって良いと思います。

そもそも

位置情報って活用されてるんでしょうか?私はなんとな~くiPhoneの時は毎回入れていましたけど、冷静に考えるとあんまりいらない機能である気がして仕方がありません…w

政情不安とか地震とかの超リアルタイム緊急時だったら案外活用されてるのかな。

その他

sessionでOAuth用のtokenの受け渡しとかが出来るのが便利~。やっぱりフレームワーク使うとサクサクできていいですね~。

  • †1: Google先生調べ

録画したアニメのファイル名を自動で書き換えて整理しよう:「ShoboCal_Renamer」

Posted on

 現在、このサーバは「foltia」という録画ソフトとPT2を組み合わせた、アニメ録画サーバを兼ねています。このfoltiaでは、ファイル名を以下のような形で格納しています:

1725-35-20100607-1730.m2t
(しょぼいカレンダの番組ID)-(話数)-(日付)-(時間).拡張子

 このファイル名の書式は機械が扱う分には簡単なのですが、人間が読むのはちょっとしんどいです(だって、番組ID1725って何なのか知らないし…)。というわけで、これをしょぼいカレンダーのDBに問い合せ、こんな感じの名前にしてくれるソフトを作ってみました:

あにゃまる探偵 キルミンずぅ 第35話 ダブルでお任せ!ハグハグリーダー!? (20100607-1730).m2t

 ファイル名の書式は入力/出力ともに自由にかつ割と簡単に設定可能ですので、foltia以外でも使えるかもしれませんね。

ダウンロード

githubからDLしてください。

オプションの指定

面倒なのでhelpをコピー!(ぉ

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
ShoboCal_Renamer 1.0 (2011/04/28)
written by PSI ( / )
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
usage: ruby shobo_cal.rb [options] files or directories
-i, --in_format FORMAT           入力ファイルのフォーマットを指定してください。
-o, --out_format FORMAT          出力ファイルのフォーマットを指定してください。
-d, --dry-run                    ファイル名を実際には書き換えません。
-r, --recursive                  再帰的に実行します。
-e, --except FILENAME            指定したファイルは除外します。
-y                               上書きが必要な際に、その旨を聞きません。
-h, --help                       このメッセージを表示します
--version                    バージョンを表示します。

 こんな感じで使えます。

カレントフォルダのファイルを全て処理
> ruby shobo_cal.rb .
mp4に変換してあるのだけ処理したい。
> ruby shobo_cal.rb *.mp4
入力ファイル名、出力ファイル名のフォーマットを指定して、カレントフォルダのファイルを処理する。
> ruby shobo_cal.rb \
-i %tid%-%stid%-%date%-%time%.%ext% \
-o %title% 第%stid%話 %subtitle% (%date%-%time%).%ext% \
.

ファイル名のフォーマットについて

 入力ファイル内の”%キーワード%”で囲んだ部分の数字を読み取り、出力ファイル名の対応する”%キーワード%”に書き出します。基本的に数字縛りです。

 ただし、以下の例外があります。

  • 入力ファイル名で%tid%で指定した番号のアニメのタイトルを、出力側で%title%として使えます。
  • 入力ファイル名で%stid%で指定した番号のアニメのサブタイトルを、出力側で%subtitle%として使えます。
  • %ext%は特殊で、数字以外でも受け付けます。拡張子用です。

東大隣人部設立のお知らせ

Posted on

東大隣人部

にかく臨機応変に隣人

善き関係を築くべく

からと心を健全に鍛え

たびだのその日まで、

共に想いらせ励まし合い

皆の信望をめる人間になろう!†1

\部員募集中だよ!/

東大隣人部とは?

ミーム、あるいは概念です。非コミュのルサンチマンが鬱積した何かです。この先部員が居なくなって一時的に消滅しても、人間がコミュニケーションするならきっと何度でも蘇ります。

東大隣人部に参加したい!

隣人部に参加したいと思ったその日から、あなたは立派な隣人部員です。入部届や退部届といった物はコミュニケーションが怖いのでありません。

全くない質問とその回答

具体的な活動曜日と内容を教えてください。

あなたがリア充へのルサンチマンに燃えたその時が活動日で、非リアのルサンチマンのために行ったこと全てが活動内容です。

隣人部員同士で何かを行っても構いませんが、部員の性質上難しいのではないでしょうか。ただし、ネット上での腹芸やエア友達技術についての解説や、事務的な情報交換等はありうるかもしれません。

twitter上では「#rinjinbu」と「#todai_rinjinbu」タグの使用が推奨されています。mixiはリア充が怖いので参加出来てません。

新歓にはどのような事を行いますか?

駒場キャンパスで数万枚のエアビラ貼り、そして正門前での盛大なエアパフォーマンスの強行、一号館†2のエア封鎖を行い、現在、大学当局とエア対決中です。

で、新歓のコンパはいつやるの?

リア充爆発しろ

部員の数を教えてください。

前述のような形の上、エア部員も多数おり、部員数の把握は困難を極めています。コミュニケーション怖いし、他の部員の数はどうでも良いんじゃないでしょうか。

「私は一人でもリア充になってみせる」だそうですが、矛盾しませんか?

エア友達とならば可能です。

新入生に伝えたい事はありますか?

教科書は必ず買ってください。教授とはいい関係を築きましょう。遅刻せず、ノートはしっかりとってください。どれも、あなたを守る大切な武器になるでしょう。エア友達と一緒なら大丈夫ですよ、きっと。

最後に

まさかの非ジョークネタ

それとは別に、東大ニコ研ではエアオリ合宿†3の放送を予定しています。本物のオリ合宿とはちがってぼっちになる危険性は0ですので、ぜひご参加ください。

  • †1: 左上から右下に掛けて、縦読みならぬ斜め読み
  • †2: 安田講堂に似てるパチモン
  • †3: 上級生が下級生のお金を使って一緒に旅行に行く行事ですコミュニケーションこわい