Personal tools
You are here: Home ブログ 井上 時刻処理なのに浮動小数点絡みのバグ
« December 2010 »
Su Mo Tu We Th Fr Sa
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Recent entries
Apache2.4のリリース予定は来年(2011年)初め(あくまで予定) inoue 2010-12-23
Herokuの発音 inoue 2010-12-20
雑誌記事「ソフトウェア・テストPRESS Vol.9」の原稿公開 inoue 2010-12-18
IPA未踏のニュース inoue 2010-12-15
労基法とチキンゲーム inoue 2010-12-06
フロントエンドエンジニア inoue 2010-12-03
ASCII.technologies誌にMapReduceの記事を書きました inoue 2010-11-25
技術評論社パーフェクトシリーズ絶賛発売中 inoue 2010-11-24
雑誌連載「Emacsのトラノマキ」の原稿(part8)公開 inoue 2010-11-22
RESTの当惑 inoue 2010-11-22
「プログラマのためのUXチートシート」を作りました inoue 2010-11-19
「ビューティフルコード」を読みました inoue 2010-11-16
Categories
カテゴリなし
 
Document Actions

時刻処理なのに浮動小数点絡みのバグ

AirOneのOutlook同期の時、時刻のずれる予定がある、というバグ報告がありました。

実際に試してみると、9時9分の予定は問題無いのですが、9時10分の予定をOutlookからAirOneにインポートすると9時9分の予定になります。

コードを調べてみると、Outlookから渡る時刻情報が怪しげなDATE型です。DATE型の説明は次のようになっています。

(マイクロソフトファンには常識なのかもしれませんが)なんと、Outlookから時刻情報が小数で渡ってきます...知りませんでした。

こうなると、9時9分と9時10分の違いは bc(1) でも確認可能です(86400は24*60*60)。

$ bc -l
(9*60*60 + 9*60)    <==9時9分
32940
(9*60*60 + 9*60)/86400
.38125000000000000000
((9*60*60 + 9*60)/86400) * 86400
32940.00000000000000000000
(9*60*60 + 10*60)   <==9時10分
33000
(9*60*60 + 10*60)/86400
.38194444444444444444
((9*60*60 + 10*60)/86400) * 86400
32999.99999999999999961600

http://www.codeguru.com/cpp_mfc/ATLDateTime.shtml を参考にしてDATE型からの変換処理を書き直しました。変数名が手抜きですが、元のロジックと新しいロジックの検証コードを示します。

#include <stdio.h>
#include <math.h>

#define DAY_BY_SEC    (24*60*60)
#define HALF_SECOND  (1.0/172800.0)   // Half a second, expressed in days

int main()
{
    int err2 = 0;
    int err3 = 0;
    
    int h,m,s;
    for (h = 0; h < 24; h++) {
        for (m = 0; m < 60; m++) {
            for (s = 0; s < 60; s++) {
                double msdate = ((long)h*60*60 + m*60 + s) / (double)DAY_BY_SEC; /* emuldate DATE type */

                /* wrong conversion to h2,m2,s2 */
                long sec2 = msdate * DAY_BY_SEC;
                int h2 = sec2 / (60 * 60);
                int m2 = (sec2 / 60) % 60;
                int s2 = sec2 % 60;

                /* right conversion to h3,m3,s3 */
                /* @see http://www.codeguru.com/cpp_mfc/ATLDateTime.shtml */
                double dblDate = msdate + HALF_SECOND;
                long nSecsInDay = (long)((dblDate - floor(dblDate)) * 86400);
                long nMinutesInDay = nSecsInDay / 60L;
                int h3 = (int)nMinutesInDay / 60;
                int m3 = (int)nMinutesInDay % 60;
                int s3 = (int)nSecsInDay % 60;
                
                printf("%d:%d:%d=> %d:%d:%d or %d:%d:%d\n",
                       h,m,s,
                       h2,m2,s2,
                       h3,m3,s3);
                if (h != h2 || m != m2 || s != s2) err2++;
                if (h != h3 || m != m3 || s != s3) err3++;
            }
        }
    }
    printf("error %d,%d\n", err2, err3);
    return 0;
}

この変換処理の正しさを代数的に示そうと http://docs.sun.com/source/806-4847/ncg_goldberg.html を読もうとしたのですが、難しくて挫折しました(難しいので、アリエルの採用試験問題にします)。

もう少し易しい記事でお勧めなのが http://pc.nikkeibp.co.jp/pc21/special/gosa/eg4.shtml です。

そんなに難しくないとは言え、日経の雑誌かつExcelユーザ向け記事で、このレベルに驚きました(Excelユーザを見下し過ぎていた気がします。反省します)。

The URL to Trackback this entry is:
http://dev.ariel-networks.com/Members/inoue/floating-point-problem/tbping
Add comment

You can add a comment by filling out the form below. Plain text formatting.

(Required)
(Required)
(Required)
This helps us prevent automated spamming.
Captcha Image


Copyright(C) 2001 - 2006 Ariel Networks, Inc. All rights reserved.