سؤال

أرغب في القيام بعملية مزدوجة باستخدام عوامات (يبدو أن الحساب المباشر لا يدعم Double Devision).

هل هذا ممكن؟

هذا ما جربته حتى الآن (C# Code ، يجب أن يكون HLSL لاحقًا):

int count = 7;
double value = 0.0073812398871474;
float f1 = (float)value;
float f2 = (float)((value - f1));
float r1 = f1 / count;
float r2 = f2 / count;
double result = (double)r1 + (double)r2;

0،00105446285765182 (النتيجة)

0،00105446284102106 (النتيجة الصحيحة)

يتعلق الأمر بالتقريب في F1. إذا كانت القيمة بدلاً من ذلك:

 double value = 0.0073812344471474;

ثم النتيجة صحيحة.

هل كانت مفيدة؟

المحلول

احسب المعاملة المتبادلة من العد مع تقسيم التعويم ثم تحسين الدقة إلى مزدوجة كاملة باستخدام صيغة نيوتن رافسون المتبادلة.

int count = 7;
double value = 0.0073812398871474;
double r = (double) (1.0f / count); // approximate reciprocal
r = r * (2.0 - count*r); // much better approximation
r = r * (2.0 - count*r); // should be full double precision by now.
double result = value * r;

نصائح أخرى

يبدو أن خطأك الحسابي ليس واضحًا لك على الفور. دعني أتهجى ذلك.

لنفترض أن مزدوج يحتوي على جزأين ، الجزء الكبير والجزء الصغير ، ولكل منهما 32 بت من الدقة تقريبًا. (هذا ليس بالضبط كيف تعمل الزوجي ولكنها ستفعل لأغراضنا.)

تعويم فقط جزء واحد.

تخيل أننا كنا نفعل ذلك 32 بت في وقت واحد ولكن الحفاظ على كل شيء في الزوجي:

double divisor = whatever;
double dividend = dividendbig + dividendlittle;
double bigquotient = dividendbig / divisor;

ما هو Bigquotient؟ إنه مزدوج. لذلك لديها جزأين. Bigquotient يساوي BigquotientBig + BigquotientLittle. الاستمرار في:

double littlequotient = dividendlittle / divisor;

مرة أخرى ، LittleQuotient هو LittleQuotientBig + LittleQuotientLittle. الآن نضيف الحبيبات:

double quotient = bigquotient + littlequotient;

كيف نحسب ذلك؟ حاصل على جزأين. سيتم ضبط QuitientBig على BigquotientBig. سيتم ضبط QuitientLittle على BigquotientLittle + LittleQuotientBig. يتم تجاهل LittleQuotientLittle.

افترض الآن أنك تفعل ذلك في عوامات. عندك:

float f1 = dividendbig;
float f2 = dividendlittle;
float r1 = f1 / divisor;

حسنًا ، ما هو R1؟ إنه تعويم. لذلك ليس له جزء واحد فقط. R1 هو bigquotientbig.

float r2 = f2 / divisor;

ما هو R2؟ إنه تعويم. لذلك ليس له جزء واحد فقط. R2 هو LittleQuotientBig.

double result = (double)r1 + (double)r2;

يمكنك إضافتها معًا وستحصل على BigQuotientBig + LittleQuotientBig. ماذا حدث لـ BigquotientLittle؟ لقد فقدت 32 بت من الدقة هناك ، ولذا لا ينبغي أن يكون مفاجئًا أنك تحصل على insacturacies 32 بت على طول الطريق. لم تتوصل إلى الخوارزمية الصحيحة على الإطلاق لتقريب الحساب 64 بت في 32 بت.

من أجل حساب (big + little)/divisor, ، لا يمكنك القيام ببساطة (big / divisor) + (little / divisor). لا تنطبق قاعدة الجبر هذه عندما تكون التقريب أثناء كل قطاع!

هل هذا واضح الآن؟

هل هذا ممكن؟

نعم ، طالما أنت:

  • قبول الخسارة الحتمية للدقة
  • ضع في اعتبارك أنه لا يتناسب كل الزوجي مع العوامات في المقام الأول

تحديث

بعد قراءة تعليقاتك (الدقة المزدوجة هي شرط) ، إجابتي المحدثة هي:

رقم.

إذن ماذا عن شيء مثل

result = value * (double)(1f / (float)count); ?

هناك فقط تقسيم اثنين من العوامات. لدي المزيد من القوالب هناك أكثر مما هو مطلوب ، لكن المفهوم هو المهم.

يحرر:
حسنًا ، هل أنت قلق بشأن الفرق بين الفعلي والمستدير ، أليس كذلك؟ لذا فقط قم بذلك مرارًا وتكرارًا حتى تحصل عليه بشكل صحيح!

double result = 0;
double difference = value;
double total = 0;
float f1 = 0;
while (difference != 0)
{
    f1 = (float)difference;
    total += f1;
    difference = value - total;
    result += (double)(f1 / count);
}

... لكنك تعلم أن الإجابة السهلة لا تزال "لا". هذا لا يزال لا يلتقط كل الأخطاء الدائرية. من اختباراتي ، يقلل من عدم الدقة إلى 1E-17 على الأكثر ، حوالي 30 ٪ من الوقت.

في تعليق ، تقول:

بالطبع لا ينبغي أن يكون هناك أي خسارة للدقة. هذا هو السبب في أنني أستخدم عوامات. إذا كنت أقبل فقدان الدقة ، فيمكنني فقط إلقاء تعويم والقيام بالتقسيم.

IEEE-754 single precision القيمة لديها 24 رقمًا ثنائيًا مهمًا. أ double precision القيمة لديها 53 رقمًا مهمًا. لا يمكنك حتى أن تمثل قيمة دقة مزدوجة كقيمتان دقيقة واحدة دون فقدان الدقة ، أقل بكثير من الحساب مع مثل هذا التمثيل.

ومع ذلك ، فهذا هو المستطاع للقيام بتقسيم دقة مزدوجة مدورة بشكل صحيح باستخدام التحويلات فقط بين الطرح/الإضافة المزدوجة والمفردة ، وعمليات دقيقة واحدة ، لكنها معقدة للغاية إذا كنت تريد حقًا القيام بذلك بشكل صحيح. هل تحتاج إلى التقريب الصحيحة IEEE-754 الفعلية ، أو مجرد إجابة صحيحة حتى آخر شيء أو اثنين؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top