Я не желаю гарантировать точный результат с помощью приведения с плавающей запятой, если у мантиссы достаточно бит, чтобы точно представлять все интегральные значения. Например, tonider y = 9223372036854775807 и x = (y div 10) - 1 = 922337203685477579. где «div» - целочисленное деление. x/y - 0.09999999999999999981568563067746 ..., но с использованием парного разряда вы получаете => 0,1. Это результат удвоения, имеющего только 52 цифры точности в значении (в то время как y требует 61 бит и x около 58)
Возможно, вы сможете использовать точность 80 бит или 128 бит FP, и в этом случае вы получите правильный ответ, потому что мантисса будет> = 64 бит (ULL - 64-битный, верно?), и поэтому вы без потерь представляете числа.
Я бы начал с приближения (используя целочисленную или Арифметическую Арифметику), а затем пробное умножение, чтобы увидеть, должен ли ответ быть меньше или меньше. Ключевое понимание состоит в том, что вы все равно можете сравнить два, возможно, переполненных ints, если знаете, что разница между этими двумя величинами меньше половины max unsigned int. Такой метод сравнения необходим, когда, например, Переполнение номеров последовательностей TCP.
Если вы хотите использовать только целочисленная арифметика, работает нижняя функция «fdd (x, y)». Я включил основные(), чтобы показать некоторые результаты:
#include <iostream>
using namespace std;
typedef unsigned char ull; // change char to any integral type e.g. long long
const ull maxull=(ull)-1;
const ull halfull = maxull/2;
typedef unsigned long long asint;
// x = X mod (maxull+1), y= Y mod (maxull+1). we only know x and y
// if we assume |X-Y|<halfull, then we return X<Y:
inline bool less_mod_near(ull x, ull y) {
return (x<=halfull == y<=halfull) ? x<y : y>x;
}
// assuming x<y, return first decimal digit of 10x/y (return is in [0..9])
inline int fdd(ull x, ull y) {
// assert(x<y);
if (x<=maxull/10) return (10*x)/y;
// for speed, and to ensure that y>10 to avoid division by 0 later
ull r=y/10;
if (r*10==y) return x/r;
ull ub=x/(r+1); // ub >= 10x div y (without overflow)
ull x10=x*10; // allow overflow
cout<<"ub="<<(asint)ub<<" x10="<<(asint)x10<<" r="<<(asint)r<<" ";
return less_mod_near(x10,ub) ? ub-1 : ub;
// we already handled the 10 evenly divides y case
}
int pdd(ull x, ull y,ull mustbe)
{
ull d=fdd(x,y);
cout << (asint)x << '/' << (asint)y << " = ." << (asint)d << "...";
if (d!=mustbe) cout << " (should be "<<(asint)mustbe<<")";
cout<<endl;
// assert(a==d);
}
int main() {
pdd(0,1,0);
pdd(1,2,5);
pdd(11,101,1);
pdd(10,101,0);
pdd(49,69,7);
pdd(50,69,7);
pdd(48,69,6);
pdd(160,200,8);
pdd(161,200,8);
pdd(159,200,7);
pdd(254,255,9);
}
Выход:
0/1 = .0...
1/2 = .5...
11/101 = .1...
10/101 = .0...
ub=7 x10=234 r=6 49/69 = .7...
ub=7 x10=244 r=6 50/69 = .7...
ub=6 x10=224 r=6 48/69 = .6...
160/200 = .8...
161/200 = .8...
159/200 = .7...
ub=9 x10=236 r=25 254/255 = .9...
Это неправильно. Рассмотрим y = 9223372036854775807 и x = (y div 10) - 1 = 922337203685477579. где «div» - целочисленное деление. x/y - 0.09999999999999999981568563067746, но с использованием парного разряда вы получаете> = 0,1, а не <0,1 –
Сколько мы хотим точности? Есть 0.09 (50 больше 9s) 5 действительно 0,1, или мы считаем это 0.0? В какой-то момент мы должны прекратить подсчет и просто округлить его до значения закрытия. Если мы заботимся о точной точности, то ответ будет заключаться в использовании математической библиотеки, которая выполняет переменную точность, и мы можем остановить ее и прекратить беспокоиться о количестве бит – shimpossible
@shimpossible: это зависит. Если он только получит первую цифру, возможно, это не имеет большого значения. Но если он собирается вытащить первые 5 или около того, то .899999 против округления первой цифры до 0,9 может испортить остальную часть преобразования. – NoMoreZealots