سؤال

أقوم بتحويل القيم المزدوجة إلى سلسلة مثل هذا:

std::string conv(double x) {
    char buf[30];
    sprintf(buf, "%.20g", x);
    return buf;
}

لقد قمت بترميز حجم المخزن المؤقت إلى 30، لكنني لست متأكدًا مما إذا كان هذا كبيرًا بما يكفي لجميع الحالات.

  • كيف يمكنني معرفة الحد الأقصى لحجم المخزن المؤقت الذي أحتاجه؟
  • هل تصبح الدقة أعلى (وبالتالي يجب زيادة المخزن المؤقت) عند التبديل من 32 بت إلى 64؟

ملاحظة:لا أستطيع استخدام ostringstream أو boost::lexical_cast لسبب الأداء (انظر هذا)

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

المحلول

لقد قمت بترميز حجم المخزن المؤقت إلى 30، لكنني لست متأكدًا مما إذا كان هذا كبيرًا بما يكفي لجميع الحالات.

إنها.يحدد %.20g 20 رقمًا في الجزء العشري.أضف 1 للفاصلة العشرية.1 لعلامة (محتمل)، 5 لـ "e+308" أو "e-308"، الحالة الأسوأ.و1 لإنهاء القيمة الخالية.

20 + 1 + 1 + 5 + 1 = 28.

هل تصبح الدقة أعلى (وبالتالي يجب زيادة المخزن المؤقت) عند التبديل من 32 بت إلى 64؟

لا.

المزدوج هو نفس الحجم في كلا المعماريين.إذا أعلنت أن متغيراتك طويلة مزدوجة، فمن المحتمل أن يكون لديك رقم واحد إضافي في الأس "e+4092"، والذي لا يزال يناسب المخزن المؤقت المكون من 30 حرفًا.ولكن فقط على X86، وعلى المعالجات الأقدم فقط.

المزدوج الطويل هو شكل 80 بت قديم من قيمة الفاصلة العائمة والذي كان التنسيق الأصلي لـ 486 FPU.لم يتم توسيع نطاق بنية FPU بشكل جيد ومنذ ذلك الحين تم تجاهلها لصالح تعليمات نمط SSE حيث تكون أكبر قيمة ممكنة للفاصلة العائمة 64 بت مزدوجة.

وهي طريقة طويلة للقول بأن المخزن المؤقت المكون من 30 حرفًا سيكون كافيًا دائمًا طالما واصلت تحديد الجزء العشري في النسخة المطبوعة بـ 20 رقمًا.

نصائح أخرى

printf("%.20g", 1.79769e+308); يكون 1.7976900000000000632e+308, ، 27 بايت بما في ذلك زائدة \0.سأختار 64 أو 128 فقط للتأكد.

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

أيضًا، هل أنت متأكد من أن عنق الزجاجة لبرنامجك هو lexical_cast..؟إن القيام بما تفعله يبدو سخيفًا جدًا بالنسبة لي

يبدو أنني أتذكر أنه إذا اتصلت sprintf مع NULL الوجهة، فإنه لا يفعل أي شيء.ومع ذلك، فإنه يقوم بإرجاع عدد الأحرف التي "كتبها".إذا كنت على حق (ولا يبدو أنني أستطيع العثور على المصدر لذلك) فيمكنك القيام بما يلي:

// find the length of the string
int len = sprintf(NULL, fmt, var1, var2,...);
// allocate the necessary memory.
char *output = malloc(sizeof(char) * (len + 1)); // yes I know that sizeof(char) is defined as 1 but this seems nicer.
// now sprintf it after checking for errors
sprintf(output, fmt, var1, var2,...);

خيار آخر هو الاستخدام snprintf والذي يسمح لك بتحديد طول الإخراج:

#define MAX 20 /* or whatever length you want */
char output[MAX];
snprintf(output, MAX, fmt, var1, var2,...);

snprintf يأخذ حجم المخزن المؤقت كوسيطة، ولا يسمح لسلسلة الإخراج بتجاوز هذا الحجم.

وإليك برنامج لطباعة عدد الأرقام المطلوبة للقيم القصوى والدنيا double يمكن أن تتخذ لأي نظام:

#include <float.h>
#include <stdio.h>

int main(void)
{
    double m = DBL_MAX;
    double n = DBL_MIN;
    int i;
    i = printf("%.20g\n", m);
    printf("%d\n", i);
    i = printf("%.20g\n", n);
    printf("%d\n", i);
    return 0;
}

بالنسبة لي يطبع:

1.7976931348623157081e+308
27
2.2250738585072013831e-308
27

بما أن 27 يتضمن سطرًا جديدًا ولكنه لا يتضمن الإنهاء 0 بالنسبة للسلاسل، أود أن أقول أنه في هذا النظام، 27 يجب أن يكون كافيا.ل long double, يبدو أن الإجابة هي 27 و 28 LDBL_MAX و LDBL_MIN على التوالي على نظامي.

صفحة الرجل (على موقعي for sprintf يقول هذا عنه %g:

يتم تحويل الوسيطة المزدوجة في النمط F أو E (أو F أو E لتحويل G).الدقة تحدد عدد الأرقام المهمة.إذا كانت الدقة مفقودة، فسيتم إعطاء 6 أرقام؛إذا كانت الدقة صفرًا ، يتم التعامل معها على أنها 1.يتم استخدام النمط E إذا كان الأسس من تحويله أقل من -4 أو أكبر من أو يساوي الدقة.تتم إزالة الأصفار الزائدة من الجزء الكسري من النتيجة ؛تظهر نقطة عشرية فقط إذا تليها رقم واحد على الأقل.

توجد صياغة مماثلة في معيار C.

لذلك أعتقد أنك ستكون آمنًا إذا استخدمت مخرجات البرنامج أعلاه كحجم المصفوفة الخاصة بك.

إذا كنت تستخدم نظامًا أساسيًا يدعم POSIX أو C99، فيجب أن تكون قادرًا على استخدامه snprintf لحساب حجم المخزن المؤقت الذي ستحتاجه. snprintf يأخذ معلمة تشير إلى حجم المخزن المؤقت الذي تقوم بتمريره؛إذا كان حجم السلسلة يتجاوز حجم المخزن المؤقت، فإنه يقتطع الإخراج ليتناسب مع المخزن المؤقت، ويعيد مقدار المساحة التي كان سيحتاجها لملاءمة الإخراج بأكمله.يمكنك استخدام مخرجات هذا لتخصيص مخزن مؤقت بالحجم الصحيح تمامًا.إذا كنت تريد فقط حساب حجم المخزن المؤقت الذي تحتاجه، فيمكنك تمرير NULL كمخزن مؤقت وحجم 0 لحساب مقدار المساحة التي تحتاجها.

int size = snprintf(NULL, 0, "%.20g", x);
char *buf = malloc(size + 1); // Need the + 1 for a terminating null character
snprintf(buf, size + 1, "%.20g", x);

تذكر أن free(buf) بعد استخدامه لتجنب تسرب الذاكرة.

المشكلة في ذلك هي أنها لن تعمل في Visual Studio، الذي لا يزال لا يدعم C99.بينما لديهم شيء مثل snprintf, ، إذا كان المخزن المؤقت الذي تم تمريره صغيرًا جدًا، فلن يُرجع الحجم المطلوب، بل يُرجعه -1 بدلاً من ذلك، وهو أمر عديم الفائدة تمامًا (ولا يقبل NULL كمخزن مؤقت، حتى مع أ 0 طول).

إذا كنت لا تمانع في الاقتطاع، يمكنك ببساطة استخدام snprintf بمخزن مؤقت ذو حجم ثابت، وتأكد من أنك لن تتجاوزه:

char buf[30];
snprintf(buf, sizeof(buf), "%.20g", x);

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

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