C99 Strict - Dziwne zachowanie przy obliczeniach zmiennoprzecinkowych

0

Mógłby mi ktoś wytłumaczyć ten fenomen? http://ideone.com/cI3DBT

#include "stdio.h"

int main() {
	double x = 0.478;
	double a = 1000.0;
	
	int ix;
	ix = (int) (a * x);
	double dx;
	dx = a * x;
	int idx;
	idx = (int) dx;
	
	printf("double x = %f, double a = %f\n\n", x, a);
	printf("int ix = (int) (a * x) = %d\n", ix);
	printf("double dx = a * x = %f\n", dx);
	printf("int idx = (int) dx = (int) (a * x) = %d\n", idx);
	return 0;
}

Wynik tego jest taki:

double x = 0.478000, double a = 1000.000000

int ix = (int) (a * x) = 477
double dx = a * x = 478.000000
int idx = (int) dx = (int) (a * x) = 478

Nie rozumiem tego. Wiem, że operacje na liczbach zmiennoprzecinkowych nie są przemienne ani łączne. Problem w tym, że ja zachowuję tu przecież kolejność wykonywanych działań, a mimo to wychodzą różne wyniki! Jak C99 obsługuje liczby zmiennoprzecinkowe?

Z góry dzięki.

1
if((int)477.9999999999999999999==497) printf("A czego się spodziewałeś?\n");
0

wystarczy sprawdzic jak dziala kojwersja z double na int (zdaje sie ze nie zaokragla).

0

@kmph: ten kod jest bledny z definicji. ix bedzie rozne od dx bo kolejnosc operacji jest rozna a nie bez znaczenia jest miejsce wykonania
operacji int(double). Mozna sobie to tlumaczyc innym kompilatorem, standardem czy ustawieniami kompilatora ale tak naprawde przyczyna jest jedna (ww).

1

Po prostu zajrzyj do wersji asm tego kodu. Najprosciej zrob dwie funkcje ktore roznie implementuja to wyrazenie. Moze sie nie udac - to bedzie troche inna wersja kodu.

2

Po pierwsze zdaj sobie sprawę, że 0.478 nie ma dokładnej reprezentacji binarnej tak samo jak 1/3 nie ma dokładnej reprezentacji dziesiętnej.

Kolejnym problemem są zaokrąglenia i rożna w precyzji podczas wykonywania obliczeń i po zapisaniu w pamięci.
Gdy obliczania są robione w koprocesorze kompilator wykorzystuje najbardziej dokładny i szybki "typ" danych, który w przypadku x86 ma chyba wielkość 80 bitów.
Twój wynik w przypadku pierwszym miał mniej więcej taką postać:
111011100.111111111111111001100101001
co dało w części całkowitej daje 477

Kopiując wynik do komórki pamięci tracisz na precyzji (64 bity), wiec musi nastąpić zaokrąglenie.
Po skróceniu mantysy (zapisaniu w zmiennej double) musiało dość do zaokrąglenia w górę co po odcięciu części całkowitej dało już oczekiwany wynik.

Można to poprawić w taki sposób//ideone.com/N38jxx

1 użytkowników online, w tym zalogowanych: 0, gości: 1