2013-10-11 2 views
2

Я читаю книгу Компьютерные системы: перспектива программиста, и я пытаюсь выполнить код, который он предоставляет на моем Macbook Pro с Intel Core i7 ,Настройки среды, необходимые для обучения «Компьютерные системы: перспектива программиста»

Но какой-то код просто не работает точно так, как предлагает книга.

Этот пример C должен демонстрировать, что тот же поплавок будет отличаться при сохранении в памяти от момента его хранения в регистре.

#include<stdio.h> 

double recip(int denom) 
{ 
    return 1.0/(double) denom; 
} 

void do_nothing(){} /* to clear the register */ 

void fcomp(int denom) 
{ 
    double r1, r2; 
    int t1, t2; 

    r1 = recip(denom); /* stored in memory */ 
    r2 = recip(denom); /* stored in register */ 
    t1 = r1 == r2;  /* Compares register to memory */ 
    do_nothing();  /* Forces register save to memory */ 
    t2 = r1 == r2;  /* Compares memory to memory */ 
    printf("test1 t1: r1 %f %c= r2 %f\n", r1, t1 ? '=' : '!', r2); 
    printf("test1 t1: r2 %f %c= r2 %f\n", r1, t2 ? '=' : '!', r2); 
} 

main(){ 
    int demon = 10; 
    fcomp(demon); 
} 

По сравнению с GCC с опцией "O2", результат, предложенной в книге, должно быть:

test1 t1: r1 0.100000 != r2 0.100000 
test2 t1: r1 0.100000 == r2 0.100000 

Однако, я получил два "==" S и непонятно, почему. Любое предложение о настройках среды для книги? Большое спасибо.

+0

Вы должны хранить книгу на сухом рабочем столе из-под прямых солнечных лучей ... –

+4

Обратите внимание, что любой полуподобный компилятор может доказать, что вызов 'do_nothing()' ничего не делает и просто не испускает для него никакого кода. Упомянутый компилятор также может, кстати, доказать, что «r1» и «r2» должны иметь одинаковое значение и оптимизировать ваши назначения и оптимизировать тернарный оператор в 'printf()' вызывает до прямой '' = ' '. Кроме того, я ничего не вижу в коде, который говорит что-либо о регистрах, поэтому любое предположение о значениях, хранящихся в регистрах, совершенно необоснованно. –

+0

Что-то здесь очень не так. Книга была написана Рэндалом Брайантом, который хорошо известен. Некоторые из книг онлайн. Вы уверены, что ваш фрагмент кода - это то, что находится в книге? Какая глава, раздел и страница? –

ответ

1

Пример в книге нацелен (по всей вероятности) на конкретные свойства x90 FPU в процессорах Intel. Основным свойством этого типа FPU является то, что он предоставляет только регистры с (видимой) точностью 80 бит. Таким образом, 32 или 64-битные поплавки преобразуются в 80-битные поплавки при загрузке в регистр FPU. Кроме того, обычно арифметические операции выполняются с полной точностью, поэтому, если значение сохраняется в регистре FPU для последующего использования, оно не округляется до 32 или 64 бит, как это делается для значения, которое копируется в память и затем загружается назад позже. В связи с этим имеет значение, если значение хранится в регистре или нет.

Однако Mac OS X (который, я полагаю, вы используете на Macbook) не использует FPU x87, он использует SSE-модуль: SSE предоставляет регистр 32 и 64 бит с плавающей запятой, поэтому он не делает разница, если значение хранится в регистре или сохраняется в памяти относительно его точности. Результат всегда округляется после каждой операции. Обычно это относится к 64-битным exectubles в Windows и Linux.

Напр. 32 бит, Linux или Windows ситуация другая.Использование устройства x87 или SSE зависит от среды, часто используется FPU x87, потому что 32-битные машины могут не поддерживать необходимые инструкции SSE2, хотя последние процессоры без SSE2 были построены примерно 10 лет назад.

+0

+1 Это отличное объяснение поведения, которое я эмпирически нашел для моего [ответа на неответ] (http://stackoverflow.com/questions/19308894/environment-settings-needed-for-learning-computer-systemsa-programmers -pers/19323829 # 19323829) –

0

Не так много ответа, но я исследовал это немного. Я нашел этот файл fcomp.c http://csapp.cs.cmu.edu/public/1e/ics/code/data/fcomp.c, который выглядит, вероятно, из того же примера из вашей книги, но ваша версия просто содержит первый тест. Во всяком случае, я играл с различными версиями gcc и -m32 vs -m64 и обнаружил, что test1 (так же, как и ваш тест) всегда выглядит равным, по крайней мере, для i386 и x86_64.

Однако есть один тест (test2), который, кажется, демонстрируют архитектуру в зависимости от поведения:

void test2(int denom) 
{ 
    double r1; 
    int t1; 
    r1 = recip(denom);    /* Default: register, Forced store: memory */ 
    t1 = r1 == 1.0/(double) denom; /* Compares register or memory to register */ 
    printf("test2 t1: r1 %f %c= 1.0/10.0\n", r1, t1 ? '=' : '!'); 
    printf("A long double on this machine requires %d bytes\n", sizeof(long double)); 
} 

(test2() вызывается с демоном 10)

При компиляции с gcc -m64 -o fcomp fcomp.c я получаю этот вывод:

test2 t1: r1 0.100000 == 1.0/10.0 
A long double on this machine requires 16 bytes 

в то время как при компиляции с gcc -m32 -o fcomp fcomp.c я получаю этот выход:

test2 t1: r1 0.100000 != 1.0/10.0 
A long double on this machine requires 12 bytes 

Для записи я получил эти результаты как с gcc 3.4.6, так и с 4.1.2.

Все остальные тесты совпадают, независимо от того, какой компилятор/арка я использую.

+0

Я забыл поиграть с опциями -O. Все тесты проходят для x86_64, независимо от опции -O. Однако для i386 с -O0 test2 терпит неудачу; с ошибкой -O1 test4 t1 и t2 и с -O2 все тесты проходят. –