Lost precision

0

Ok, Ja wiem na czym polega strata precyzji, ale czuje się już zagubiony :(

[CPP] http://ideone.com/qzPQPf - C++ - 1.000000
[C#] http://ideone.com/1kQ6Sj - C# (mono-2.8) - 0.9999907
[C#] http://pastebin.com/dpQiVDRh - C# (mono-2.10) - 0.9999907
[C#] VisualStudio - 0.9999907
[C] http://ideone.com/Pr8C6N - 1.000000
[CPP] (jakiś starszy GCC, nie wiem jak sprawdzić) - 0.999991

Staram się zebrać informacje odnośnie unikania owych strat, ale jak widać zależy to od kompilera (swoją drogą sry za mix C#)

Prawidłowy wynik to oczywiście 1

Już nie wiem co się dzieje, czy żeby zacząc tłumaczyć na czym polega lost precision trzeba najpierw napisać klauze "Zależnie od kompilatora"?

Jak to w końcu jest? Skąd mam wiedzieć, czy mój program nie wykrzaczy się / nie zbuguje? Jak unikać utraty precyzji?

1

GCC prawdopodobnie zamienia to na mnożenie. Spróbuj jakiegoś bardziej zaawansowanego algorytmu, np ortogonalizację Grama-Schmidta :P
Albo skompiluj z wyłączonymi optymalizacjami.

0

o_O
Ale co to ma wspólnego z utratą precyzji? Przecież to co pokazałes to jest zwykłe ograniczenie reprezentacji zmiennoprzecinkowej i tyle, bo 0.1 to jest ułamek okresowy. Od czego zależy? Głównie od tego na ilu bitach trzyma się w danym języku floaty, double i long double (czy zgodnie z IEEE754 czy tez nie) i czy zezwala się na wykonywanie operacji w pamięci na liczbach wyższej precyzji jeśli są dostepne (np. java potrafi korzystać z obliczeń wyższej precyzji niż specyfikuje JVM jeśli ma taką możliwość). Poza tym kompilator nie jest głupi i potrafi sobie różne rzeczy optymalizować. Zobacz najpierw jaki kod ten kompilator w ogóle wygenerował...

0

Jakbyś podał jeszcze kod, który daje takie wyniki... (może da się go poprawić)

Jak to w końcu jest?
Oprócz wewnętrznej reprezntacji liczby dochodzi kwestia formatowania liczby do wyświetlenia na ekranie. Różne języki mogą mieć różną domyślną liczbę cyfr po przecinku...

Skąd mam wiedzieć, czy mój program nie wykrzaczy się / nie zbuguje?
Dlaczego miałby się wykrzaczyć?

Jak unikać utraty precyzji?
Jeśli liczba nie będzie wyświetlana na ekranie (czyli nie musi ładnie wyglądać) to pogodzić się z tym, i olać. Jeśli wynik jest dla użytkownika, zazwyczaj wystarczą np. dwie cyfry po przecinku, a wtedy problem nie istnieje.

0

@Azarien Wykrzaczył by się przykładowo przy porównaniu do jedynki, np. deadlocki etc. etc.
Mówiąc o wygenerowanym kodzie przez kompilator macie na myśli kod ASM? Jestem tragiczny w jego interpretacji, ale mogę go zamieścić jeśli nalegacie, tymczasem:

Bawiąc się debuggerem doszedłem do tego etapu:
[code]
static void Main(string[] args)
{
float a = 0.05f;
a += 0.01f;

        double b = 0.05d;
        b += 0.01d;
    }

[/code]

a = 0.0600000024;
b = 0.060000000000000005;
Żadna matematyka nie tłumaczy mi tej 24ki na końcu

Ok, Ja rozumiem, w pewnym sensie tak mała różnica nie powinna robić wielkiego szumu, ale czy w takim razie takie narzędzia jak, np. MS Excel / wolfram też dzielą ten ból?

Natomiast:
[code]
float a = 0.06f;
a += 0.01f;
[/code]
Da już poprawne 0,07

Zaznaczenie optymalizacji kodu (domyślnie wyłączonej) niczego nie zmieniła

I jak tu teraz porównywać floaty do, np 2.0 :P

0

Handryczysz się o jakieś drobiazgi na 10 miejscu po przecinku. Obliczanie pola trójkąta z wzoru Herona:

#include <iostream>
#include <math.h>

int main()
{
    {
        float a=12345678.0;
        float b=12345679.0;
        float c=1.01233995;
        float p=(a+b+c)/2;
        float pole=sqrt(p*(p-a)*(p-b)*(p-c));
        std::cout << "(float)Pole = " << pole << "\n"; //(float)Pole = 0
    }
    {
        double a=12345678.0;
        double b=12345679.0;
        double c=1.01233995;
        double p=(a+b+c)/2;
        double pole=sqrt(p*(p-a)*(p-b)*(p-c));
        std::cout << "(double)Pole = " << pole << "\n"; //(double)Pole = 972730
    }
}

I to jest znacząca różnica. http://ideone.com/hY0B0x
Liczb zmiennoprzecinkowych nie należy porównywać tak: (x == y), ale tak: abs(x - y) < jakaś_mała_liczba.

0

@Hell4Ge:

Jak chcesz unikać błędów precyzji to jest to osobny temat.
Jedno z rozwiązań to używanie int-ów: http://ideone.com/FVcaBP

Inne to używanie odpowiedniej biblioteki: (Arbitrary-precision arithmetic, multi precision, bigint):

0

@Azarien Wykrzaczył by się przykładowo przy porównaniu do jedynki
Zależy co rozumiesz przez „wykrzaczył”.
Jeśli spodziewasz się 1.0000 i porównujesz do 1.0000 to będzie problem. Ale patrz niżej...

Liczb zmiennoprzecinkowych nie należy porównywać tak: (x == y), ale tak: abs(x - y) < jakaś_mała_liczba.
Jeśli mamy dzielenie, i chcemy zabezpieczyć się przed zerem, robimy ==0.0 (albo !=0.0), bez żadnych abs(). Bo dzielenie przez 0.00000004 się uda, i przez -0.0000001 też się uda. Interesuje nas tylko przypadek 0.000000

1
bogdans napisał(a):

Liczb zmiennoprzecinkowych nie należy porównywać tak: (x == y), ale tak: abs(x - y) < jakaś_mała_liczba.

To też nie jest całkiem dobra metoda. Dobry artykuł: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

0

Dzięki wszystkim :)

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