2

私は(私にとって)非常に奇妙な振る舞いをしました:

    int generate_scenario_one_pass(FILE *out, double freq_mhz) {
        unsigned int d_freq, d_freq_test;
        d_freq              = (int)(freq_mhz * 20);
        d_freq_test         = (int)(float)(freq_mhz * 20);
        printf("when freq_mhz = %.1f, d_freq = 0x%04X, d_freq_test = 0x%04X\n", freq_mhz, d_freq, d_freq_test);
    }

コード全体はここにはありませんが、関係ありません。この関数は、2110.0 から 0.1 ずつ増加する値で数回呼び出されます。

when freq_mhz = 2110.0, d_freq = 0xA4D8, d_freq_test = 0xA4D8
when freq_mhz = 2110.1, d_freq = 0xA4DA, d_freq_test = 0xA4DA
when freq_mhz = 2110.2, d_freq = 0xA4DC, d_freq_test = 0xA4DC
when freq_mhz = 2110.3, d_freq = 0xA4DD, d_freq_test = 0xA4DE

最後の反復でd_freqは、間違っています! しかしd_freq_test、正しい値を持っています。だから私の問題は、からdoublefloat、次にからfloatにキャストすることで解決しましたint。理由を知りたかった。

これは、x86 CPU で MSVC++ 6.0 を使用してコンパイルされました。

4

5 に答える 5

4

浮動小数点数として正確に表現できない多くの数値があり、その中に 0.1 があります (表現できる最も近い数値に丸められます - 0.0999999999999998 の行に沿ったものです)。を使用するdoubleと、2110.3 はたまたま 2110.3 よりもわずかに小さい数値で表されるため、20 を掛けて にキャストするとint(切り捨てられます)、「間違った」結果が得られますが、浮動小数点としての 2110.3 は で表されます。この数値は 2110.3 よりわずかに大きいため、期待どおりの結果が得られます。

于 2012-06-05T11:32:08.173 に答える
2

double から int に変換すると、切り捨てられます。

freq_mhz*20atの値は- で2110.3表され、0x40E49BFFFFFFFFFF42207.9999999999927240423858166 です。intそれを に切り詰めると、.999999 が切り捨てられ、 42207 (または 0xA4DD - なぜこれらを 16 進数で表現することを選択するのですか?) が得られます。

その間に float に変換すると、丸め操作が実行されます。実際にやりたいことはround、値を明示的に呼び出してから に変換することintです。

于 2012-06-05T11:47:47.073 に答える
2

実際、私のダブルキャストは解決策ではありませんでした。

#include <stdio.h>

int main(int argc, char **argv) {
    int d_freq, d_freq_test;
    double freq_mhz = 2110.0;
    double step = 0.1;

    while (freq_mhz < 2111.0) {
        d_freq = (int)(freq_mhz * 20.0);
        d_freq_test = (int)(float)(freq_mhz * 20.0);
        printf("freq: %.1f, d_freq: 0x%04X, d_freq_test: 0x%04X\n", freq_mhz, d_freq, d_freq_test);
        freq_mhz += step;
    }

    return 0;
}

これは(間違って)生成されます:

freq: 2110.0, d_freq: 0xA4D8, d_freq_test: 0xA4D8
freq: 2110.1, d_freq: 0xA4DA, d_freq_test: 0xA4DA
freq: 2110.2, d_freq: 0xA4DC, d_freq_test: 0xA4DC
freq: 2110.3, d_freq: 0xA4DD, d_freq_test: 0xA4DD <-- :(
freq: 2110.4, d_freq: 0xA4DF, d_freq_test: 0xA4DF
freq: 2110.5, d_freq: 0xA4E1, d_freq_test: 0xA4E1
freq: 2110.6, d_freq: 0xA4E3, d_freq_test: 0xA4E3
freq: 2110.7, d_freq: 0xA4E5, d_freq_test: 0xA4E5
freq: 2110.8, d_freq: 0xA4E7, d_freq_test: 0xA4E7
freq: 2110.9, d_freq: 0xA4E9, d_freq_test: 0xA4E9
freq: 2111.0, d_freq: 0xA4EB, d_freq_test: 0xA4EB

このコードの間:

#include <stdio.h>

int main(int argc, char **argv) {
    int d_freq, d_freq_test;
    double freq_mhz = 2110.0;
    double step = 0.1;

    while (freq_mhz < 2111.0) {
        d_freq = (int)(freq_mhz * 20.0);
        d_freq_test = (int)(float)(freq_mhz * 20.0 + 0.5);
        printf("freq: %.1f, d_freq: 0x%04X, d_freq_test: 0x%04X\n", freq_mhz, d_freq, d_freq_test);
        freq_mhz += step;
    }

    return 0;
}

生成:

freq: 2110.0, d_freq: 0xA4D8, d_freq_test: 0xA4D8
freq: 2110.1, d_freq: 0xA4DA, d_freq_test: 0xA4DA
freq: 2110.2, d_freq: 0xA4DC, d_freq_test: 0xA4DC
freq: 2110.3, d_freq: 0xA4DD, d_freq_test: 0xA4DE <-- :)
freq: 2110.4, d_freq: 0xA4DF, d_freq_test: 0xA4E0
freq: 2110.5, d_freq: 0xA4E1, d_freq_test: 0xA4E2
freq: 2110.6, d_freq: 0xA4E3, d_freq_test: 0xA4E4
freq: 2110.7, d_freq: 0xA4E5, d_freq_test: 0xA4E6
freq: 2110.8, d_freq: 0xA4E7, d_freq_test: 0xA4E8
freq: 2110.9, d_freq: 0xA4E9, d_freq_test: 0xA4EA
freq: 2111.0, d_freq: 0xA4EB, d_freq_test: 0xA4EC

これは正しいです。

したがって、実際には丸めの問題、精度の問題であり、x20 乗算の結果に 0.5 を追加することで解決されました。

于 2012-06-05T11:39:10.383 に答える
1

0.1 は 2 進浮動小数点では正確に表現できないためです。あなたが見ているのは概算であり、キャストが原因の切り捨てと丸めがprintf原因で悪化しています。

これを解決する 1 つの方法は、int にキャストするときに切り捨てるのではなく、明示的に丸めることです ( を使用できますround())。

于 2012-06-05T11:30:29.310 に答える
0

10 分の 1 は 2 進数では表現できません。10 進法の 1/3 のようなものです。小数点以下の桁数が多いほど、近くなりますが、そこには到達できません。あらゆる種類の対処戦略がありますが、基本的に正確な表現が必要な場合、浮動小数点形式はそれを行いません。固定小数点 (10 進数) 形式が必要です。

于 2012-06-05T11:40:43.577 に答える