Dzielenie dużych liczb całowitych

0
uint64_t a;
uint64_t b;
a = 18014398509481980;
b = 16014398509481979;

Celowo wybrałem liczby na granicy pojemności tego typu. Jak mam uzyskać dokładny zmiennoprzecinkowy wynik (a/b) ?

2

albo dokładny, albo zmiennoprzecinkowy ;) A dzielenie to ho-ho-ho

Zmienny przecinek z istoty ma ograniczoną dokładność (co wielu wie) i ma problem reprezentacji (nie każda liczba istnieje - mniej ludzi wie). Im dłuższe liczby (float -> double -> ewentualne long double) tym błędy są mniejsze, ale nigdy nie znikają

0

Domyślam się tych problemów. Ale chodzi mi po prostu jak to podzielić. Long double ma chyba piętnaście cyfr, więc nie mogę tych liczb rzutować, bo jest cyfr siedemnaście. Ja na Delphi wychowany, to tam wszystko się robiło za mnie. Trzeba było tylko pilnować zgodności typów. A w tym języku dopiero zaczynam.

0

long double powinno dać radę.

0

Ale co chcesz uzyskać? Podałeś dane wejściowe, a nie napisałeś jakiego wyniku oczekujesz.
Masz 3 możliwości

  1. dzielenie całkowite
  2. dzielenie zmiennoprzecinkowe
  3. dzielenie za pomocą jakiejś biblioteki coś analogicznego do BigDecimal z Java
0
uint64_t a;
uint64_t b;
a = 18014398509481980;
b = 16014398509481979;
long double c;
c = a / b;
cout <<c;

Dlaczego wynik to 1 ?

0
Patryk27 napisał(a):

long double powinno dać radę.

Żadnych gwarancji nie ma

didzni napisał(a):

... Ja na Delphi wychowany, to tam wszystko się robiło za mnie.

Niczego takiego nie robiło, bo jest niemożliwością metematyczną.
Faktem jest, że niektóre "przyjazne" platformy oszukiwały/oszukują przy wyświetlaniu debuggerem/drukowaniu, samowolnie prezentując 3.99999999999 czy 4.000000000002 jako 4.00

dla wierzących w możliowśc dokładnego dzielenia FP (nawet na super-hiper-long double), ile jest zmiennoprzecinkowo 1/3 (liczby są binarne, tu przykład dziesiętny, ale istota ta sama)

Nie ma "dokładnego" jest najwyżej "akceptuję niedokładność taką a taką"

0
didzni napisał(a):
	uint64_t a;
	uint64_t b;
	a = 18014398509481980;
	b = 16014398509481979;
	long double c;
	c = a / b;
	cout <<c;

Dlaczego wynik to 1 ?

Dlatego, że dzielisz całkowitoliczbowo, to jest ELEMENTARZ.

Najmniej złe jest

c =(long double) a / (long double) b;

0

Rzutowanie w stylu C nigdy nie będzie najmniej złe. static_cast albo notacja {}

0

No, wiem że mogę rzutować, ale jak to się dzieje że rzutujemy taką wielką liczbę, która ma siedemnaście cyfr na long double, która może przechować tylko piętnaście i jest okej?

0

Gdzie wyczytałeś, że long double mieści 15 cyfr?

0

A.... long double to 10 bajtów. 19 cyfr. O rany. Przepraszam. Ale ze mnie gapa.

Fakt, 19 cyfr. 10 bajtów zajmuje ten typ. Przepraszam. Gapa jestem.

0

Ok, to teraz: gdzie wyczytałeś informację, że jest to 19 cyfr?
(cokolwiek to znaczy, biorąc pod uwagę podstawę, miejsce przecinka itd.)

0

Widzę, że szybko rzuciłeś okiem na tabelę, lecz do tekstu niżej już nie zerknąłeś :-)

Precyzja około 19...20 cyfr znaczących.

0

Bo się śpieszyłem. Na szybkiego chciałem sprawdzić, czy ten ciąg z liczb pierwszych, który odkrył Euler, a o którym wyczytałem w internecie, faktycznie daje Pi do kwadratu dzielone przez 6. I faktycznie, wychodzi "równiutko" tyle. Dziwna sprawa. Tylko, że właśnie ograniczeniem jest zakres wartości. Ale właśnie bardziej zależało mi na sprawdzeniu tej własności liczb pierwszych niż na wczytywaniu się w niuanse rozmiarów i zakresów zmiennych. Dziękuję wszystkim za szybką reakcję i pomoc.

Long double u mnie ma 8 bajtów. Visual Studio. Rzutowanie dużych "uint64_t" do "long double" się nie udaje. Powyżej liczby około 8987084935256169 (16 cyfr) już nie idzie. (tak na ślepo, debilską metodą prób i błędów). W Delphi zaś mogę rzutować typ "uint64" do zmiennoprzecinkowego typu "extended" (oba mają po 8 bajtów) aż do 9217300608648576081 (19 cyfr). Może w Delphi jest jakoś inaczej ustawione ile jest przeznaczone na część całkowitą?

0

Nawiasem mówiąc, w Object Pascalu poniższe coś to normalka:

var a, b : integer;
         c : Extended;

begin
    a := 1234;
    b := 4321;
    c := a/b;
    writeln (c);
end;

Zmienna c jest zadeklarowana jako zmiennoprzecinkowa, i ja sam nie muszę określać jaki rodzaj dzielenia (całkowite czy zmiennoprzecinkowe) ma zostać wykonany. Nie wiem na jakim etapie podejmowana jest decyzja, ale przez wiele lat ułatwiało mi to życie. :-)

0

W obiektowym Pascalu są dwa dzielenia – / do floatów i div do liczb całkowitych. Przy czym operatora / można używać do dzielenia floatów i intów, ale div wyłącznie do intów (jeśli o standard chodzi).

Jeśli użyje się operatora / w połączeniu z operandami całkowitoliczbowymi, to oba zostaną najpierw przekonwertowane na odpowiednie typy zmiennoprzecinkowe, a następnie wykonane zostanie dzielenie. Konwertowane, nie rzutowane – to dwa różne pojęcia.

1

Rzutowanie dużych "uint64_t" do "long double" się nie udaje.

Co to znaczy, że się nie udaje? Komputer się wyłącza? Program się nie kompiluje?

Taki kod:

#include <stdint.h>
#include <stdio.h>
 
int main(void) {
	uint64_t u64 = (1LL << 63) - 123456789 + (1LL << 63);
	printf("%lu\n", u64);
	double f64 = u64;
	printf("%lf\n", f64);
}

daje taki wynik na https://www.ideone.com/rx2ldh :

18446744073586094827
18446744073586094080.000000

Jak widać wszystko się udaje, tylko tracimy trochę precyzji.

0
sizeof(long double)

W moim Visual Studio 2017 jest osiem bajtów, a gdy uruchomię w Ideone.com po wybraniu wersji C++14 (gcc 6.3) to long double ma 16 bajtów.

0

@didzni: IIRC standard określa tylko, że sizeof(long double) >= sizeof(double), a w bibliotece standardowej chyba nie ma nigdzie definicji typów zmiennoprzecinkowych ze znanym rozmiarem. Jeśli chcesz możesz spróbować użyć Boosta, który takie rzeczy oferuje (o ile są dostępne).

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