هل تستخدم التعويم أو العشري لمبلغ الدولار في تطبيق المحاسبة؟

StackOverflow https://stackoverflow.com/questions/61872

سؤال

نحن نعيد كتابة نظام المحاسبة القديم الخاص بنا في VB.NET وSQL Server.لقد قمنا بإحضار فريق جديد من مبرمجي .NET/ SQL للقيام بإعادة الكتابة.لقد اكتمل بالفعل معظم النظام بالمبالغ بالدولار باستخدام العوامات.لغة النظام القديمة، التي برمجت بها، لم تكن تحتوي على لغة Float لذا ربما كنت سأستخدم علامة عشرية.

ما هي توصيتك؟

هل يجب استخدام نوع البيانات العائمة أو العشرية للمبالغ بالدولار؟

ما هي بعض إيجابيات وسلبيات أي منهما؟

أحد الأمور المذكورة في اجتماعنا اليومي هو أنه يجب عليك توخي الحذر عند حساب المبلغ الذي يُرجع نتيجة تزيد عن منزلتين عشريتين.يبدو أنه سيتعين عليك تقريب المبلغ إلى منزلتين عشريتين.

هناك عيب آخر وهو أن جميع شاشات العرض والمبالغ المطبوعة يجب أن تحتوي على بيان تنسيق يعرض منزلتين عشريتين.لقد لاحظت عدة مرات أنه لم يتم القيام بذلك وأن المبالغ لا تبدو صحيحة.(أي.10.2 أو 10.2546)

المحترف هو أن Float يستهلك 8 بايت فقط على القرص بينما يستهلك الرقم العشري 9 بايت (الرقم العشري 12,2)

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

المحلول

هل يجب استخدام نوع البيانات العائمة أو العشرية للمبالغ بالدولار؟

الجواب سهل.لا تطفو أبدًا. أبداً !

العوامات كانت وفقا ل إيي 754 دائما ثنائي، فقط المعيار الجديد إيي 754 ر التنسيقات العشرية المحددة.لا يمكن للعديد من الأجزاء الثنائية الكسرية أن تساوي التمثيل العشري الدقيق.
يمكن كتابة أي رقم ثنائي كـ m/2^n (m, n الأعداد الصحيحة الموجبة)، أي رقم عشري مثل m/(2^n*5^n).
كما الثنائيات تفتقر إلى رئيس الوزراء factor 5, ، يمكن تمثيل جميع الأرقام الثنائية بشكل صحيح بواسطة الكسور العشرية، ولكن ليس العكس.

0.3 = 3/(2^1 * 5^1) = 0.3

0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]

          1/4         1/8         1/16          1/32

وبذلك ينتهي بك الأمر برقم أعلى أو أقل من الرقم العشري المحدد.دائماً.

لماذا هذا مهم ؟التقريب.
التقريب العادي يعني 0..4 لأسفل، 5..9 لأعلى.لذلك يفعل مسألة إذا كانت النتيجة إما 0.049999999999....أو 0.0500000000...ربما تعلم أنها تعني 5 سنتات، لكن الكمبيوتر لا يعرف ذلك ويقوم بالتقريب 0.4999...أسفل (خطأ) و 0.5000...لأعلى (يمين).
وبما أن نتيجة حسابات الفاصلة العائمة تحتوي دائمًا على مصطلحات خطأ صغيرة، فإن القرار هو مجرد حظ.يصبح الأمر ميؤوسًا منه إذا كنت تريد التعامل مع الأرقام العشرية بشكل تقريبي حتى مع الأرقام الثنائية.

غير مقتنع؟هل تصر على أن كل شيء في نظام حسابك على ما يرام تمامًا؟
الأصول والخصوم متساوية؟حسنًا، خذ كل الأرقام المنسقة المحددة لكل إدخال، وقم بتحليلها وجمعها بنظام عشري مستقل!قارن ذلك بالمجموع المنسق.
عفوا، هناك شيء خاطئ، أليس كذلك؟

لهذا الحساب ، كانت الدقة الشديدة والإخلاص مطلوبة (استخدمنا تعويم Oracle) حتى نتمكن من تسجيل "المليارث من قرش".

لا يساعد ضد هذا الخطأ.ونظرًا لأن جميع الأشخاص يفترضون تلقائيًا أن الكمبيوتر يقوم بالجمع بشكل صحيح، فمن الناحية العملية لا أحد يتحقق بشكل مستقل.

نصائح أخرى

أولا يجب عليك قراءة هذا ما يجب أن يعرفه كل عالم كمبيوتر عن حساب النقطة العائمة.إذن عليك أن تفكر حقًا في استخدام نوع ما نقطة ثابتة / رقم الدقة التعسفية الحزمة (على سبيل المثال.Java BigNum، python Decimal Module) وإلا فسوف تكون في عالم من الأذى.ثم اكتشف ما إذا كان استخدام نوع SQL العشري الأصلي كافيًا.

توجد عوامات/مضاعفات (محررة) لكشف x87 fp السريع الذي أصبح الآن قديمًا إلى حد كبير.لا تستخدمها إذا كنت تهتم بدقة الحسابات و/أو لا تعوض بشكل كامل عن حدودها.

كتحذير إضافي، يستخدم SQL Server و.Net Framework خوارزمية افتراضية مختلفة للتقريب.تأكد من مراجعة معلمة MidPointRounding في Math.Round().يستخدم .Net Framework خوارزمية Bankers بشكل افتراضي ويستخدم SQL Server التقريب الخوارزمي المتماثل.راجع مقالة ويكيبيديا هنا

اسأل المحاسبين الخاص بك!سوف يستهجنون عليك لاستخدام التعويم.مثل ما نشر من قبل، استخدم التعويم فقط إذا كنت لا تهتم بالدقة.على الرغم من أنني سأكون دائمًا ضد ذلك عندما يتعلق الأمر بالمال.

في برامج المحاسبة غير مقبول تعويم.استخدم العلامة العشرية مع 4 نقاط عشرية.

النقاط العائمة لها أرقام غير عقلانية غير متوقعة.

على سبيل المثال، لا يمكنك تخزين 1/3 كرقم عشري، فسيكون 0.3333333333...(وما إلى ذلك وهلم جرا)

يتم تخزين العوامات فعليًا كقيمة ثنائية وقوة ذات أسين.

لذلك يتم تخزين 1.5 على هيئة 3 × 2 أس -1 (أو 3/2)

باستخدام هذه الأسس ذات الأساس 2، يتم إنشاء بعض الأعداد الفردية غير المنطقية، على سبيل المثال:

قم بتحويل 1.1 إلى عدد عائم ثم قم بتحويله مرة أخرى، وستكون النتيجة كما يلي:1.099999999989

وذلك لأن التمثيل الثنائي لـ 1.1 هو في الواقع 154811237190861 x 2^-47، وهو ما يمكن التعامل معه بأكثر من رقم مزدوج.

المزيد عن هذه القضية على مدونتي, ، ولكن في الأساس، بالنسبة للتخزين، من الأفضل استخدام الكسور العشرية.

على خادم Microsoft SQL لديك money نوع البيانات - عادةً ما يكون هذا هو الأفضل للتخزين المالي.إنها دقيقة حتى 4 مواضع عشرية.

بالنسبة للحسابات، لديك مشكلة أكبر - عدم الدقة جزء صغير جدًا، لكن ضعه في دالة قوة وسرعان ما يصبح مهمًا.

ومع ذلك، فإن الكسور العشرية ليست جيدة جدًا لأي نوع من الرياضيات - لا يوجد دعم أصلي للقوى العشرية، على سبيل المثال.

استخدم خادم SQL عدد عشري يكتب.

لا تستخدم مال أو يطفو.

يستخدم المال 4 منازل عشرية، وهو أسرع من استخدام العلامة العشرية لكن يعاني من بعض المشاكل الواضحة وبعضها غير الواضحة في التقريب (انظر مشكلة الاتصال هذه)

ما أوصي به هو استخدام أعداد صحيحة 64 بت تخزن كل شيء بالسنت.

القليل من الخلفية هنا....

لا يوجد نظام أرقام يمكنه التعامل مع جميع الأعداد الحقيقية بدقة.جميعها لها حدودها، وهذا يشمل كلا من النقطة العائمة القياسية لـ IEEE والعلامة العشرية.تعتبر النقطة العائمة لـ IEEE أكثر دقة لكل بتة مستخدمة، ولكن هذا لا يهم هنا.

تعتمد الأرقام المالية على قرون من الممارسة بالورقة والقلم، مع الاتفاقيات المرتبطة بها.إنها دقيقة إلى حد معقول، لكن الأهم من ذلك أنها قابلة للتكرار.يجب أن يتوصل محاسبان يعملان بأرقام ومعدلات مختلفة إلى نفس الرقم.أي مجال للتناقض هو مجال للاحتيال.

ولذلك، بالنسبة للحسابات المالية، فإن الإجابة الصحيحة هي أي شيء يعطي نفس الإجابة التي يقدمها محاسب قانوني معتمد يجيد الحساب.هذا حساب عشري، وليس نقطة عائمة لـ IEEE.

السبب الوحيد لاستخدام Float مقابل المال هو إذا كنت لا تهتم بالإجابات الدقيقة.

العوامات ليست تمثيلات دقيقة، فمن الممكن حدوث مشكلات في الدقة، على سبيل المثال عند إضافة قيم كبيرة جدًا وقيم صغيرة جدًا.ولهذا السبب يوصى باستخدام الأنواع العشرية للعملة، على الرغم من أن مشكلة الدقة قد تكون نادرة بما فيه الكفاية.

للتوضيح، النوع العشري 12,2 سيخزن تلك الأرقام الـ 14 بالضبط، في حين أن التعويم لن يفعل ذلك لأنه يستخدم تمثيلًا ثنائيًا داخليًا.على سبيل المثال، لا يمكن تمثيل 0.01 بالضبط بواسطة رقم الفاصلة العائمة - أقرب تمثيل هو في الواقع 0.0099999998

بالنسبة للنظام المصرفي الذي ساعدت في تطويره، كنت مسؤولاً عن جزء "تراكم الفوائد" من النظام.كل يوم، كان الكود الخاص بي يحسب مقدار الفائدة المتراكمة (المكتسبة) على الرصيد في ذلك اليوم.

لإجراء هذا الحساب، كان الأمر يتطلب دقة وإخلاصًا شديدين (استخدمنا Oracle's FLOAT) حتى نتمكن من تسجيل "جزء من المليار من البنس" المتراكم.

عندما يتعلق الأمر بـ "الاستفادة من" الفائدة (أي.سداد الفائدة إلى حسابك) تم تقريب المبلغ إلى قرش واحد.كان نوع البيانات لأرصدة الحسابات منزلتين عشريتين.(في الواقع كان الأمر أكثر تعقيدًا لأنه كان نظامًا متعدد العملات يمكن أن يعمل في العديد من المنازل العشرية - ولكننا كنا دائمًا نقرب إلى "البنس" من تلك العملة).نعم - هناك "كسور" من الخسارة والمكاسب، ولكن عندما يتم تحقيق أرقام أجهزة الكمبيوتر (الأموال المدفوعة أو المدفوعة) كانت دائمًا قيم أموال حقيقية.

وهذا يرضي المحاسبين والمدققين والمختبرين.

لذلك، تحقق مع عملائك.سيخبرونك بالقواعد والممارسات المصرفية/المحاسبية الخاصة بهم.

أفضل من استخدام الكسور العشرية هو استخدام الأعداد الصحيحة القديمة فقط (أو ربما نوعًا ما من الأشياء الكبيرة).بهذه الطريقة تحصل دائمًا على أعلى دقة ممكنة، ولكن يمكن تحديد الدقة.على سبيل المثال الرقم 100 ربما يقصد 1.00, ، والذي تم تنسيقه على النحو التالي:

int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);

إذا كنت ترغب في الحصول على مزيد من الدقة، يمكنك تغيير 100 إلى قيمة أكبر، مثل:10 ^ n، حيث n هو عدد الكسور العشرية.

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

أذكر هذا لأنه إذا استخدم المبرمجون الخاص بك التعويم في نظام محاسبي، فمن المحتمل أنهم غير مألوفين تمامًا بفكرة الضوابط الداخلية ولم يأخذوها في الاعتبار في جهودهم البرمجية.

يمكنك دائمًا كتابة شيء مثل نوع Money لـ .Net.

نلقي نظرة على هذه المادة: نوع المال لـ CLR - قام المؤلف بعمل ممتاز في رأيي.

كنت أستخدم نوع أموال SQL لتخزين القيم النقدية.لقد اضطررت مؤخرًا إلى العمل مع عدد من أنظمة الدفع عبر الإنترنت ولاحظت أن بعضها يستخدم الأعداد الصحيحة لتخزين القيم النقدية.لقد بدأت في مشاريعي الحالية والجديدة باستخدام الأعداد الصحيحة وأنا راضٍ تمامًا عن هذا الحل.

من بين 100 كسور n/100، حيث n هو عدد طبيعي مثل 0 <= n وn <100، أربعة فقط يمكن تمثيلها كأرقام الفاصلة العائمة.ألق نظرة على مخرجات برنامج C هذا:

#include <stdio.h>

int main()
{
    printf("Mapping 100 numbers between 0 and 1 ");
    printf("to their hexadecimal exponential form (HEF).\n");
    printf("Most of them do not equal their HEFs. That means ");
    printf("that their representations as floats ");
    printf("differ from their actual values.\n");
    double f = 0.01;
    int i;
    for (i = 0; i < 100; i++) {
        printf("%1.2f -> %a\n",f*i,f*i);
    }
    printf("Printing 128 'float-compatible' numbers ");
    printf("together with their HEFs for comparison.\n");
    f = 0x1p-7; // ==0.0071825
    for (i = 0; i < 0x80; i++) {
        printf("%1.7f -> %a\n",f*i,f*i);
    }
    return 0;
}

هل فكرت في استخدام نوع بيانات المال لتخزين المبالغ بالدولار؟

فيما يتعلق بـ Con أن العلامة العشرية تشغل بايتًا آخر، أود أن أقول لا تهتم بها.في مليون صف، ستستخدم فقط 1 ميغابايت إضافية، كما أن سعة التخزين رخيصة جدًا هذه الأيام.

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

ربما ترغب في استخدام شكل من أشكال تمثيل النقاط الثابتة لقيم العملة.ستحتاج أيضًا إلى التحقق من تقريب بانكر (المعروف أيضًا باسم "النصف المستدير حتى".) فهو يتجنب التحيز الموجود في طريقة "تقريب النصف لأعلى" المعتادة.

سوف يرغب المحاسبون لديك في التحكم في كيفية التقريب.استخدام التعويم يعني أنك ستقوم بالتقريب باستمرار، عادةً باستخدام عبارة نوع FORMAT()، وهي ليست الطريقة التي تريد القيام بها (استخدم الكلمة/السقف بدلاً من ذلك).

لديك أنواع بيانات العملات (المال، والمال الصغير)، والتي ينبغي استخدامها بدلاً من البيانات العائمة أو الحقيقية.سيؤدي تخزين العلامة العشرية (12,2) إلى إلغاء عمليات التقريب الخاصة بك، ولكنه سيؤدي أيضًا إلى إزالتها أثناء الخطوات المتوسطة - وهو ما لا تريده على الإطلاق في التطبيق المالي.

استخدم العلامة العشرية دائمًا.سيعطيك Float قيمًا غير دقيقة بسبب مشكلات التقريب.

يمكن لأرقام النقطة العائمة فقط تمثل الأرقام التي هي مجموع المضاعفات السالبة للأساس - بالنسبة للنقطة العائمة الثنائية، بالطبع، هذا اثنان.

لا يوجد سوى أربعة كسور عشرية يمكن تمثيلها بدقة بالفاصلة العائمة الثنائية:0، 0.25، 0.5 و 0.75.كل شيء آخر هو تقريبي، بنفس الطريقة التي 0.3333...هو تقريب لـ 1/3 في الحساب العشري.

تعد النقطة العائمة خيارًا جيدًا للحسابات حيث يكون حجم النتيجة هو المهم.إنه اختيار سيء حيث تحاول أن تكون دقيقًا لعدد معين من المنازل العشرية.

هذه مقالة ممتازة تصف متى تستخدم العائمة والعشرية.يخزن Float قيمة تقريبية ويخزن العلامة العشرية قيمة دقيقة.

باختصار، يجب أن تستخدم القيم الدقيقة مثل المال العلامة العشرية، ويجب أن تستخدم القيم التقريبية مثل القياسات العلمية التعويم.

فيما يلي مثال مثير للاهتمام يوضح أن كلاً من التعويم والعشري قادران على فقدان الدقة.عند إضافة رقم ليس عددًا صحيحًا ثم طرح نفس الرقم العائم يؤدي إلى فقدان الدقة بينما لا يؤدي ذلك إلى فقدان الدقة:

    DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float; 
    SET @Float1 = 54; 
    SET @Float2 = 3.1; 
    SET @Float3 = 0 + @Float1 + @Float2; 
    SELECT @Float3 - @Float1 - @Float2 AS "Should be 0";

Should be 0 
---------------------- 
1.13797860024079E-15

عند ضرب عدد غير صحيح والقسمة على نفس الرقم، تفقد الكسور العشرية الدقة بينما لا تفقد الأعداد العشرية دقتها.

DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4); 
SET @Fixed1 = 54; 
SET @Fixed2 = 0.03; 
SET @Fixed3 = 1 * @Fixed1 / @Fixed2; 
SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1";

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