سؤال
ما زلت أتعثر في محددات التنسيق لعائلة وظائف printf().ما أريده هو أن أكون قادرًا على طباعة رقم مزدوج (أو عائم) بحد أقصى لعدد معين من الأرقام بعد العلامة العشرية.إذا كنت تستخدم:
printf("%1.3f", 359.01335);
printf("%1.3f", 359.00999);
انا حصلت
359.013
359.010
بدلا من المطلوب
359.013
359.01
أي شخص يمكن أن تساعدني؟
المحلول
لا يمكن أن يتم ذلك مع المختصين شكل printf
العادية. سيكون الأقرب يمكن أن تحصل هي:
printf("%.6g", 359.013); // 359.013
printf("%.6g", 359.01); // 359.01
ولكن "0.6" هو مجموعه م> عرض رقمي لذلك
printf("%.6g", 3.01357); // 3.01357
وقوعه.
ما <م> يمكن م> القيام به هو sprintf("%.20g")
الرقم إلى المخزن المؤقت سلسلة ثم التلاعب السلسلة إلى نملك سوى أحرف N الماضي الفاصلة العشرية.
وعلى افتراض رقمك في الأسطوانات المتغيرة، فإن الدالة التالية إزالة جميع ولكن عشرى N
الأولى، ثم تجريدها من الأصفار زائدة (والعشرية إذا كانت كافة الأصفار).
char str[50];
sprintf (str,"%.20g",num); // Make the number.
morphNumericString (str, 3);
: :
void morphNumericString (char *s, int n) {
char *p;
int count;
p = strchr (s,'.'); // Find decimal point, if any.
if (p != NULL) {
count = n; // Adjust for more or less decimals.
while (count >= 0) { // Maximum decimals allowed.
count--;
if (*p == '\0') // If there's less than desired.
break;
p++; // Next character.
}
*p-- = '\0'; // Truncate string.
while (*p == '0') // Remove trailing zeros.
*p-- = '\0';
if (*p == '.') { // If all decimals were zeros, remove ".".
*p = '\0';
}
}
}
إذا كنت غير راض عن هذا الجانب اقتطاع (والذي من شأنه أن يحول 0.12399
إلى 0.123
بدلا من التقريب إلى 0.124
)، يمكنك فعلا استخدام مرافق التقريب التي قدمتها بالفعل printf
. تحتاج فقط لتحليل العدد قبل يد لخلق حيوي الاعراض، ثم استخدام هذه لتحويل عدد إلى سلسلة:
#include <stdio.h>
void nDecimals (char *s, double d, int n) {
int sz; double d2;
// Allow for negative.
d2 = (d >= 0) ? d : -d;
sz = (d >= 0) ? 0 : 1;
// Add one for each whole digit (0.xx special case).
if (d2 < 1) sz++;
while (d2 >= 1) { d2 /= 10.0; sz++; }
// Adjust for decimal point and fractionals.
sz += 1 + n;
// Create format string then use it.
sprintf (s, "%*.*f", sz, n, d);
}
int main (void) {
char str[50];
double num[] = { 40, 359.01335, -359.00999,
359.01, 3.01357, 0.111111111, 1.1223344 };
for (int i = 0; i < sizeof(num)/sizeof(*num); i++) {
nDecimals (str, num[i], 3);
printf ("%30.20f -> %s\n", num[i], str);
}
return 0;
}
وبيت القصيد من nDecimals()
في هذه الحالة هو العمل بشكل صحيح من الاعراض المجال، ثم تنسيق الرقم باستخدام سلسلة تنسيق بناء على ذلك. يظهر main()
اختبار تسخير في هذا العمل:
40.00000000000000000000 -> 40.000
359.01335000000000263753 -> 359.013
-359.00999000000001615263 -> -359.010
359.00999999999999090505 -> 359.010
3.01357000000000008200 -> 3.014
0.11111111099999999852 -> 0.111
1.12233439999999995429 -> 1.122
وبمجرد الانتهاء من القيمة بشكل صحيح مدورة، يمكنك تمرير مرة أخرى أن لmorphNumericString()
لإزالة الأصفار الزائدة ببساطة عن طريق تغيير:
nDecimals (str, num[i], 3);
في:
nDecimals (str, num[i], 3);
morphNumericString (str, 3);
و(أو الدعوة morphNumericString
في نهاية nDecimals
ولكن، في هذه الحالة، ربما كنت الجمع بين اثنين فقط في وظيفة واحدة)، وينتهي بك الأمر مع:
40.00000000000000000000 -> 40
359.01335000000000263753 -> 359.013
-359.00999000000001615263 -> -359.01
359.00999999999999090505 -> 359.01
3.01357000000000008200 -> 3.014
0.11111111099999999852 -> 0.111
1.12233439999999995429 -> 1.122
نصائح أخرى
لتخلص من الأصفار زائدة، يجب عليك استخدام "٪ ز" الشكل:
float num = 1.33;
printf("%g", num); //output: 1.33
وبعد السؤال وتوضيح بعض الشيء، أن قمع الأصفار ليست هي الشيء الوحيد الذي سئل، ولكن الحد من الإخراج إلى ثلاث خانات عشرية كان مطلوبا أيضا. أعتقد أن لا يمكن القيام به مع سلاسل التنسيق sprintf وحدها. كما أشار باكس ديابلو بها، ستكون هناك حاجة التلاعب السلسلة.
تعجبني إجابة ر.مُعدل قليلاً:
float f = 1234.56789;
printf("%d.%.0f", f, 1000*(f-(int)f));
"1000" يحدد الدقة.
القدرة على التقريب 0.5.
يحرر
حسنًا، تم تعديل هذه الإجابة عدة مرات وفقدت تتبع ما كنت أفكر فيه قبل بضع سنوات (وفي الأصل لم تملأ جميع المعايير).إذن إليك إصدار جديد (يملأ جميع المعايير ويتعامل مع الأرقام السالبة بشكل صحيح):
double f = 1234.05678900;
char s[100];
int decimals = 10;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf("10 decimals: %d%s\n", (int)f, s+1);
وحالات الاختبار:
#import <stdio.h>
#import <stdlib.h>
#import <math.h>
int main(void){
double f = 1234.05678900;
char s[100];
int decimals;
decimals = 10;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf("10 decimals: %d%s\n", (int)f, s+1);
decimals = 3;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" 3 decimals: %d%s\n", (int)f, s+1);
f = -f;
decimals = 10;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" negative 10: %d%s\n", (int)f, s+1);
decimals = 3;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" negative 3: %d%s\n", (int)f, s+1);
decimals = 2;
f = 1.012;
sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals));
printf(" additional : %d%s\n", (int)f, s+1);
return 0;
}
ومخرجات الاختبارات:
10 decimals: 1234.056789
3 decimals: 1234.057
negative 10: -1234.056789
negative 3: -1234.057
additional : 1.01
الآن تم استيفاء جميع المعايير:
- الحد الأقصى لعدد الكسور العشرية خلف الصفر ثابت
- تتم إزالة الأصفار الزائدة
- يفعل ذلك بشكل صحيح رياضيا (أليس كذلك؟)
- يعمل (الآن) أيضًا عندما يكون الرقم العشري الأول صفرًا
لسوء الحظ هذه الإجابة عبارة عن خطين sprintf
لا يعود السلسلة.
وأنا ابحث في سلسلة (ابتداء من أقصى اليمين) للحرف الأول في 1
مجموعة ل9
(ASCII قيمة 49
-57
) ثم null
(مجموعة ل0
) كل حق شار منه - انظر أدناه:
void stripTrailingZeros(void) {
//This finds the index of the rightmost ASCII char[1-9] in array
//All elements to the left of this are nulled (=0)
int i = 20;
unsigned char char1 = 0; //initialised to ensure entry to condition below
while ((char1 > 57) || (char1 < 49)) {
i--;
char1 = sprintfBuffer[i];
}
//null chars left of i
for (int j = i; j < 20; j++) {
sprintfBuffer[i] = 0;
}
}
وماذا عن شيء مثل هذا (قد يكون أخطاء التقريب والقضايا قيمة سالبة، التي تحتاج إلى تصحيح، غادر باعتبارها ممارسة للقارئ):
printf("%.0d%.4g\n", (int)f/10, f-((int)f-(int)f%10));
وانها برنامجي قليلا إلا أنها على الأقل لا يجعلك تفعل أي تلاعب السلسلة.
وهناك حل بسيط لكنه يحصل على هذه المهمة، يعين طول المعروفة والدقة والابتعاد عن فرصة للصعود الشكل الأسي (الذي يشكل خطرا عند استخدام٪ ز):
// Since we are only interested in 3 decimal places, this function
// can avoid any potential miniscule floating point differences
// which can return false when using "=="
int DoubleEquals(double i, double j)
{
return (fabs(i - j) < 0.000001);
}
void PrintMaxThreeDecimal(double d)
{
if (DoubleEquals(d, floor(d)))
printf("%.0f", d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%.1f", d);
else if (DoubleEquals(d * 100, floor(d* 100)))
printf("%.2f", d);
else
printf("%.3f", d);
}
وإضافة أو إزالة "ELSES" إذا كنت تريد بحد اقصى 2 عشرية. 4 العشرية. وما إلى ذلك.
وعلى سبيل المثال إذا كنت تريد 2 الكسور العشرية:
void PrintMaxTwoDecimal(double d)
{
if (DoubleEquals(d, floor(d)))
printf("%.0f", d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%.1f", d);
else
printf("%.2f", d);
}
إذا كنت ترغب في تحديد الحد الأدنى للعرض للحفاظ على الحقول الانحياز، زيادة عند الضرورة، على سبيل المثال:
void PrintAlignedMaxThreeDecimal(double d)
{
if (DoubleEquals(d, floor(d)))
printf("%7.0f", d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%9.1f", d);
else if (DoubleEquals(d * 100, floor(d* 100)))
printf("%10.2f", d);
else
printf("%11.3f", d);
}
ويمكنك أيضا تحويل ذلك إلى وظيفة حيث يمكنك تمرير العرض المطلوب من الملعب:
void PrintAlignedWidthMaxThreeDecimal(int w, double d)
{
if (DoubleEquals(d, floor(d)))
printf("%*.0f", w-4, d);
else if (DoubleEquals(d * 10, floor(d * 10)))
printf("%*.1f", w-2, d);
else if (DoubleEquals(d * 100, floor(d* 100)))
printf("%*.2f", w-1, d);
else
printf("%*.3f", w, d);
}
ولقد وجدت مشاكل في بعض الحلول التي تم نشرها. أنا وضعت هذا معا على أساس الإجابات أعلاه. ويبدو أن العمل بالنسبة لي.
int doubleEquals(double i, double j) {
return (fabs(i - j) < 0.000001);
}
void printTruncatedDouble(double dd, int max_len) {
char str[50];
int match = 0;
for ( int ii = 0; ii < max_len; ii++ ) {
if (doubleEquals(dd * pow(10,ii), floor(dd * pow(10,ii)))) {
sprintf (str,"%f", round(dd*pow(10,ii))/pow(10,ii));
match = 1;
break;
}
}
if ( match != 1 ) {
sprintf (str,"%f", round(dd*pow(10,max_len))/pow(10,max_len));
}
char *pp;
int count;
pp = strchr (str,'.');
if (pp != NULL) {
count = max_len;
while (count >= 0) {
count--;
if (*pp == '\0')
break;
pp++;
}
*pp-- = '\0';
while (*pp == '0')
*pp-- = '\0';
if (*pp == '.') {
*pp = '\0';
}
}
printf ("%s\n", str);
}
int main(int argc, char **argv)
{
printTruncatedDouble( -1.999, 2 ); // prints -2
printTruncatedDouble( -1.006, 2 ); // prints -1.01
printTruncatedDouble( -1.005, 2 ); // prints -1
printf("\n");
printTruncatedDouble( 1.005, 2 ); // prints 1 (should be 1.01?)
printTruncatedDouble( 1.006, 2 ); // prints 1.01
printTruncatedDouble( 1.999, 2 ); // prints 2
printf("\n");
printTruncatedDouble( -1.999, 3 ); // prints -1.999
printTruncatedDouble( -1.001, 3 ); // prints -1.001
printTruncatedDouble( -1.0005, 3 ); // prints -1.001 (shound be -1?)
printTruncatedDouble( -1.0004, 3 ); // prints -1
printf("\n");
printTruncatedDouble( 1.0004, 3 ); // prints 1
printTruncatedDouble( 1.0005, 3 ); // prints 1.001
printTruncatedDouble( 1.001, 3 ); // prints 1.001
printTruncatedDouble( 1.999, 3 ); // prints 1.999
printf("\n");
exit(0);
}
وبعض الحلول صوت عالية اقتراح محدد تحويل %g
من printf
. وهذا خطأ لأن هناك حالات حيث %g
سينتج العلمي. حلول أخرى تستخدم الرياضيات في طباعة العدد المطلوب من الأرقام العشرية.
وأعتقد أن الحل الأسهل هو استخدام sprintf
مع محدد تحويل %f
وإزالة الأصفار الزائدة وربما العشرية من النتيجة يدويا. وهنا حل C99:
#include <stdio.h>
#include <stdlib.h>
char*
format_double(double d) {
int size = snprintf(NULL, 0, "%.3f", d);
char *str = malloc(size + 1);
snprintf(str, size + 1, "%.3f", d);
for (int i = size - 1, end = size; i >= 0; i--) {
if (str[i] == '0') {
if (end == i + 1) {
end = i;
}
}
else if (str[i] == '.') {
if (end == i + 1) {
end = i;
}
str[end] = '\0';
break;
}
}
return str;
}
لاحظ أن الحروف المستخدمة للأرقام والفاصل العشري تعتمد على لغة الحالية. رمز أعلاه يفترض لغة C أو الإنكليزية.
وهنا هو أول محاولة في الإجابة:
void xprintfloat(char *format, float f) { char s[50]; char *p; sprintf(s, format, f); for(p=s; *p; ++p) if('.' == *p) { while(*++p); while('0'==*--p) *p = '\0'; } printf("%s", s); }
والحشرات المعروفة: ممكن تجاوز سعة المخزن المؤقت اعتمادا على الشكل. إذا "." هي موجودة لسبب سوى٪ و قد يحدث نتيجة خاطئة.
لماذا لا نفعل ذلك؟
double f = 359.01335;
printf("%g", round(f * 1000.0) / 1000.0);
اختلاف طفيف عما سبق:
- يزيل الفترة للحالة (10000.0).
- فترات راحة بعد معالجة الفترة الأولى.
الكود هنا:
void EliminateTrailingFloatZeros(char *iValue)
{
char *p = 0;
for(p=iValue; *p; ++p) {
if('.' == *p) {
while(*++p);
while('0'==*--p) *p = '\0';
if(*p == '.') *p = '\0';
break;
}
}
}
لا يزال لديه احتمال التجاوز، لذا كن حذرًا؛P
يتم تقريب الكود الخاص بك إلى ثلاث منازل عشرية بسبب ".3" قبل f
printf("%1.3f", 359.01335);
printf("%1.3f", 359.00999);
وبالتالي، إذا قمت بتقريب السطر الثاني إلى منزلتين عشريتين، فيجب عليك تغييره إلى هذا:
printf("%1.3f", 359.01335);
printf("%1.2f", 359.00999);
سيُخرج هذا الرمز النتائج المرجوة:
359.013
359.01
*لاحظ أن هذا على افتراض أنك قمت بالفعل بالطباعة على أسطر منفصلة، وإذا لم يكن الأمر كذلك، فإن ما يلي سيمنعها من الطباعة على نفس السطر:
printf("%1.3f\n", 359.01335);
printf("%1.2f\n", 359.00999);
كان الكود المصدري للبرنامج التالي هو اختباري لهذه الإجابة
#include <cstdio>
int main()
{
printf("%1.3f\n", 359.01335);
printf("%1.2f\n", 359.00999);
while (true){}
return 0;
}